Files
2026-05-27 23:29:53 +01:00

60 lines
3.4 KiB
Markdown

# Tasks: add-import-sse-progress
## 0. Pre-flight (non-negotiable)
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
## 1. OTel — extend instruments bundle
- [ ] Add `sseConnected: Counter` (`import.sse.connected`) to `ImportInstruments`.
- [ ] Add `sseEventsSent: Counter` (`import.sse.events_sent`) labelled by `kind`.
- [ ] Add `sseDisconnected: Counter` (`import.sse.disconnected`) labelled by `reason`.
- [ ] Update `src/otel/instruments.md` to describe the three new counters.
- [ ] Extend `src/otel/instruments.test.ts` to assert the new counters round-trip through a fresh `MeterProvider`.
## 2. Route — `GET /:id/progress`
- [ ] Create `src/routes/progress-sse/progress-sse.ts` exporting `registerProgressSseRoute(app, deps)`.
- [ ] Wire the handler into `createImportRoutes` in `src/routes/build-routes.ts`.
- [ ] Subscribe to `service.bus` (`import.progress`, `import.completed`, `import.failed`) — NO polling.
- [ ] Forward the request `AbortSignal` to detach the listener on client disconnect.
- [ ] Frame format: `id: <jobId>-<seq>`, `event: progress|complete|failed`, JSON-stringified `ImportProgress` payload, double-newline terminator.
- [ ] Initial frame: snapshot from `service.getJob(id, ownerId)` so the client sees current state before the first bus tick.
- [ ] Terminal events (`complete` / `failed`) close the stream cleanly.
## 3. Errors
- [ ] Reuse `IMPORT.NOT_FOUND` for foreign-owner / missing jobs (404 problem+json).
- [ ] Reuse `IMPORT.UNAUTHENTICATED` (via `requireAuth()` middleware already in `build-routes.ts`) for the 401 path.
- [ ] No new error codes added.
## 4. Tests
- [ ] `src/routes/progress-sse/progress-sse.test.ts` covering eight scenarios listed in `proposal.md#source-mapping`.
- [ ] Tests use Hono's `app.request()` returning a `Response` with a streamed body; assertions read via `body.getReader()` and decode UTF-8.
- [ ] No mocks. Real `ImportService`, real Hono, real in-memory `MeterProvider`.
## 5. Docs
- [ ] `src/routes/progress-sse/progress-sse.md` (≥200 words, Purpose / Features / Usage Basic / Usage Advanced / API Reference / Implementation Notes).
- [ ] Update package `README.md` Features list and routes table to include the new SSE endpoint.
## 6. Subpath export
- [ ] Add `./routes/progress-sse/progress-sse` to `package.json#exports`.
## 7. Gates (in order)
- [ ] `bunx biome check src/`
- [ ] `bunx turbo lint typecheck test --filter=@suaveplan/api-plugin-import`
- [ ] `bunx turbo test:coverage --filter=@suaveplan/api-plugin-import` (100/100/100/100)
- [ ] `bun scripts/openspec-conformance.ts @suaveplan/api-plugin-import` (0 errors)
- [ ] `bunx turbo build --filter=@suaveplan/api-plugin-import`
## 8. Archive
- [ ] Fold spec delta into `openspec/specs/api-plugin-import/spec.md`.
- [ ] `mv openspec/changes/add-import-sse-progress openspec/archive/2026-05-20-add-import-sse-progress`.