120 lines
11 KiB
Markdown
120 lines
11 KiB
Markdown
# 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)
|