Files
suaveplan/openspec/changes/migration-adapters-completion/tasks.md
2026-05-27 23:29:53 +01:00

120 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks — Migration adapters completion
> Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify.
## 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.
## Audit Note (2026-05-02)
Status: **DEFERRED**. None of the proposed additive surfaces exist: no `migrationEvents` shared bus, no `MigrationLockLike` / `MigrationStoreLike` interfaces (existing `MigrationLockAdapter` / `MigrationStoreAdapter` shapes are untouched), no `BatchTargetProgress` / `BatchTargetStatus` / `TargetConfigFactory` types, no `GetAllRecordsFn`, no `ValidationError` alias, no `registerMigrationErrorCodes()` named export. Lifecycle event-key aliases (`migration:started`, `migration:applied`, `migration:completed`, `migration:rolled-back`) are absent — code only emits `migration:before` / `migration:after` / `migration:failed` / `migration:plan`. 0% of work is done.
## 1. Spec authoring
- [ ] 1.1 Read `../suaveplan/enterprise/migration.md` end-to-end
- [ ] 1.2 Read `openspec/specs/migration/spec.md` (does not exist yet — confirm and start fresh)
- [ ] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14
- [ ] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
- [ ] 1.5 Fill in `## Source mapping` table — every requirement cites a line range OR appears in the "no source line" table with justification
- [ ] 1.6 Draft `specs/migration/spec.md` from `openspec/templates/spec-delta.md`
- [ ] 1.7 Every requirement in the spec delta carries a `**Verified by:**` line naming concrete `it(...)` titles
## 2. Implementation — `lock/migration-lock`
- [ ] 2.1 Create `packages/database/migration/src/lock/migration-lock/migration-lock.ts` exporting the `MigrationLockLike` interface (`acquire(lockId, ttlMs)`, `release(lockId)`, `extend(lockId, ttlMs)`)
- [ ] 2.2 Re-export the existing `MigrationLockAdapter` from this module under the same name for back-compat
- [ ] 2.3 Add a `migration-lock.md` sibling doc covering both interface contracts (≥200 words, basic + advanced examples)
- [ ] 2.4 Update `MemoryMigrationLock` to implement `MigrationLockLike` alongside the existing `MigrationLockAdapter` shape (additive methods only)
- [ ] 2.5 Update `lock/index.ts` to export `MigrationLockLike` from the package root via `index.ts`
## 3. Implementation — `store/migration-store`
- [ ] 3.1 Create `packages/database/migration/src/store/migration-store/migration-store.ts` exporting the `MigrationStoreLike` interface (`list`, `append`, `markFailed`, `markApplied`, `getLatestVersion`)
- [ ] 3.2 Re-export the existing `MigrationStoreAdapter` from this module under the same name for back-compat
- [ ] 3.3 Add a `migration-store.md` sibling doc (≥200 words, basic + advanced examples)
- [ ] 3.4 Update `MemoryMigrationStore` to implement `MigrationStoreLike` alongside the existing `MigrationStoreAdapter` shape (additive methods only)
- [ ] 3.5 Update `store/index.ts` to export `MigrationStoreLike` from the package root via `index.ts`
## 4. Implementation — `migration-events`
- [ ] 4.1 Create `packages/database/migration/src/migration-events/migration-events.ts` exporting `migrationEvents: EventBus<MigrationEventMap>` plus `createMigrationEvents()` factory
- [ ] 4.2 Extend `MigrationEventMap` in `types/migration.types.ts` with the lifecycle keys: `"migration:started"`, `"migration:applied"`, `"migration:failed"`, `"migration:completed"`, `"migration:rolled-back"` (additive — preserve `migration:before` / `migration:after` / `migration:plan`)
- [ ] 4.3 Update `MigrationExecutor` to emit both legacy AND canonical lifecycle keys for one release; add `// genesis: legacy emit removed in follow-up change` comment (no `@deprecated` per CLAUDE.md rule)
- [ ] 4.4 Add a `migration-events.md` sibling doc (≥200 words, basic + advanced examples) covering the event-bus subscription pattern
- [ ] 4.5 Wire `migrationEvents` into `index.ts` exports
## 5. Implementation — batch types
- [ ] 5.1 Add `BatchTargetStatus = "pending" | "running" | "completed" | "failed"` type alias to `types/migration.types.ts`
- [ ] 5.2 Add `BatchTargetProgress` interface (`targetId`, `status`, `results`, `error?`, `startedAt?`, `completedAt?`)
- [ ] 5.3 Add `TargetConfigFactory = (targetId: string) => MigrationRunnerConfig | Promise<MigrationRunnerConfig>` type alias
- [ ] 5.4 Update `BatchMigratorConfig` to accept either the existing `targets` array OR a `configFactory` + `targetIds` pair (additive)
- [ ] 5.5 Update `BatchMigrator` to emit `BatchTargetProgress` updates via an optional `onProgress?: (progress: BatchTargetProgress) => void` callback
- [ ] 5.6 Re-export the new types from `index.ts`
- [ ] 5.7 Update `batch-migrator.md` sibling doc to cover the new factory pattern
## 6. Implementation — status `GetAllRecordsFn`
- [ ] 6.1 Add `GetAllRecordsFn = () => Promise<MigrationRecord[]>` type alias to `status/migration-status.ts`
- [ ] 6.2 Update `MigrationStatusDashboard` constructor to accept an optional `getAllRecords?: GetAllRecordsFn` parameter; falls back to `store.list()` when absent
- [ ] 6.3 Re-export `GetAllRecordsFn` from `index.ts`
- [ ] 6.4 Update `migration-status.md` sibling doc to describe the injection point
## 7. Implementation — `ValidationError` re-export
- [ ] 7.1 Add `export type { MigrationValidationError as ValidationError }` to `types/migration.types.ts`
- [ ] 7.2 Update `index.ts` to export both `MigrationValidationError` and `ValidationError`
## 8. Implementation — `MIGRATION_CODES` registration
- [ ] 8.1 Confirm the side-effect import in `index.ts` already triggers `defineCodes` registration on load
- [ ] 8.2 Export an idempotent `registerMigrationErrorCodes()` function from `errors/migration-error.ts` for downstream consumers that bundle without side-effect imports
- [ ] 8.3 Re-running `registerMigrationErrorCodes()` MUST NOT throw — guard with the registry's existing dedup
- [ ] 8.4 Re-export `registerMigrationErrorCodes` from `index.ts`
## 9. Tests
- [ ] 9.1 `lock/migration-lock/migration-lock.test.ts` — acquire returns true on uncontested lockId, false when contested; release frees lockId; extend refreshes TTL; multiple lockIds isolated; expired locks auto-release
- [ ] 9.2 `lock/migration-lock/migration-lock.spec-conformance.test.ts` — pins `MigrationLockAdapter` back-compat alias; verifies `MemoryMigrationLock` satisfies BOTH interfaces
- [ ] 9.3 `store/migration-store/migration-store.test.ts` — empty list initial; append → list returns single; markApplied flips status; markFailed records error; getLatestVersion returns latest by id sort
- [ ] 9.4 `store/migration-store/migration-store.spec-conformance.test.ts` — pins `MigrationStoreAdapter` back-compat alias; verifies `MemoryMigrationStore` satisfies BOTH interfaces
- [ ] 9.5 `migration-events/migration-events.test.ts` — every lifecycle key emits with the correct payload shape; multiple subscribers receive in registration order
- [ ] 9.6 `migration-events/migration-events.spec-conformance.test.ts` — listener throwing does not abort emit loop; off() removes specific listener; clear() removes all
- [ ] 9.7 `batch/batch-migrator/batch-migrator.test.ts` — `BatchTargetProgress` transitions pending → running → completed; failure transitions to failed and isolates from siblings; `onProgress` called per transition
- [ ] 9.8 `status/migration-status/migration-status.test.ts` — `GetAllRecordsFn` injected source returned from `getHistory()`; absent factory falls back to `store.list()`
- [ ] 9.9 `errors/migration-error.spec-conformance.test.ts` — `ErrorCodes.has("MIGRATION.LOCK_HELD")` is true after package import; second `registerMigrationErrorCodes()` call does not throw
- [ ] 9.10 Every `it(...)` in 9.1–9.9 carries `{ timeout: 5000 }` (or 30000 for genuinely async)
## 10. Documentation
- [ ] 10.1 Sibling `.md` for every new implementation `.ts` (`migration-lock.md`, `migration-store.md`, `migration-events.md`); each ≥200 words with basic + advanced examples
- [ ] 10.2 Update `packages/database/migration/README.md` — add Modules links for the three new files; add Quick-start snippet showing `migrationEvents` subscription
- [ ] 10.3 Update `CLAUDE.md` package listing line for `@suaveplan/migration` if the public-surface description has changed
## 11. Pre-archive verification (the 5-gate triple)
- [ ] 11.1 `bun run check` — Biome 0 errors / 0 warnings / 0 info across the whole monorepo
- [ ] 11.2 `bunx turbo lint typecheck test --filter=@suaveplan/migration` — green
- [ ] 11.3 `bunx turbo test:coverage --filter=@suaveplan/migration` — 100/100/100/100
- [ ] 11.4 `bun scripts/openspec-conformance.ts @suaveplan/migration` — exit 0, no `error` findings
- [ ] 11.5 `bunx turbo build --filter=@suaveplan/migration` — green
## 12. Pre-archive 7-row checklist (AGENTS.md Section 13)
- [ ] 12.1 Generics threaded; `expectTypeOf` assertions on every literal-type claim (lifecycle key strings, `BatchTargetStatus` union)
- [ ] 12.2 Zod schemas wired at every declared boundary (`MigrationLockLike` / `MigrationStoreLike` consumers do not need Zod — interfaces are TS-only; verify schemas are still wired in `MigrationRunnerOptionsSchema` etc.)
- [ ] 12.3 Every error code in `errors/migration-error.ts` thrown by at least one production path
- [ ] 12.4 Strict-mode behaviors tested both on and off (lock TTL expiry honoured under `enableTtlEnforcement: true` and bypassed when `false`)
- [ ] 12.5 OTel instruments emitted from production paths and asserted with a real in-memory `MeterProvider`
- [ ] 12.6 Lifecycle dedup tested where claimed (idempotent `registerMigrationErrorCodes`)
- [ ] 12.7 No retroactive tick — every box above reflects work done in this change
## 13. Archive
- [ ] 13.1 Fold spec delta into `openspec/specs/migration/spec.md` (will be the first time this canonical spec exists)
- [ ] 13.2 Move `openspec/changes/migration-adapters-completion/` → `openspec/archive/<yyyy-mm-dd>-migration-adapters-completion/`
- [ ] 13.3 Refresh `MANIFEST.md` if package set or deps changed (no expected change here)