# Change: add-import-sse-progress ## Why `@suaveplan/api-plugin-import` shipped (archive `2026-05-19-add-api-plugin-import`) with the **`## Excluded from this migration`** clause explicitly deferring SSE progress push: > Foreign-key resolution, parallel batch workers, resumable checkpoints, S3/URL source adapters, **SSE progress push** This change lifts that single deferral. Consumers can now subscribe to a job's progress via `GET /imports/:id/progress` and receive `text/event-stream` frames driven directly off the existing `ImportService.bus` events — no polling. The other deferred items (foreign-key resolution, parallel workers, checkpoints, S3 sources) remain deferred. The endpoint reuses: - the canonical `ImportProgress` payload type, - the existing `import.progress` / `import.completed` / `import.failed` events, - the existing `IMPORT.*` error registry, - the owner-scoping invariants of `ImportService.getJob(jobId, ownerId)`. The endpoint introduces three new OTel counters scoped to the new feature module: `import.sse.connected`, `import.sse.events_sent{kind}`, `import.sse.disconnected{reason}`. ## Affected requirements ADDED requirement: **SSE progress push** (one new requirement; no MODIFIED / REMOVED entries). ## Source mapping | Spec scenario | Implementation | Test | | --- | --- | --- | | GET `/imports/:id/progress` opens an event-stream | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"opens a text/event-stream and emits an initial progress frame"` | | Each `import.progress` bus emit produces an `event: progress` frame | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a progress frame for every bus progress event"` | | Terminal `import.completed` emits `event: complete` then closes | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a complete frame and closes on import.completed"` | | Terminal `import.failed` emits `event: failed` then closes | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"emits a failed frame and closes on import.failed"` | | Foreign-owner request returns 404 problem+json | `src/routes/progress-sse/progress-sse.ts` | `src/routes/progress-sse/progress-sse.test.ts::"returns 404 problem+json for a foreign-owner job"` | | Anonymous request returns 401 problem+json | route is mounted under `requireAuth()` in `build-routes.ts` | `src/routes/progress-sse/progress-sse.test.ts::"returns 401 problem+json when the caller is anonymous"` | | Client disconnect cleans up the bus listener | `AbortSignal` wired into the `ReadableStream.cancel` path | `src/routes/progress-sse/progress-sse.test.ts::"detaches the listener and increments disconnect counter on client abort"` | | OTel counters increment on connect / event / disconnect | `src/routes/progress-sse/progress-sse.ts` (new instruments wired through `getImportInstruments()`) | `src/routes/progress-sse/progress-sse.test.ts::"increments import.sse counters on connect, send, and disconnect"` | ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/api-plugin-import` to enumerate. - `@suaveplan/api` — [ ] tests run / [ ] docs reviewed / [x] no action needed (reason: additive route under an existing prefix; no signature changes to the consumed surface) Foundation package? No. This is a feature-additive change on a server-tier plugin. ## Out of scope (still deferred from the original `Excluded` list) - Foreign-key resolution - Parallel batch workers - Resumable checkpoints - S3 / URL source adapters - Streaming input parsing - Benchmarks