111 lines
6.9 KiB
Markdown
111 lines
6.9 KiB
Markdown
# Tasks — complete-storage-mux
|
||
|
||
> Every checkbox MUST reflect work actually done in this change.
|
||
|
||
## 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. Spec authoring
|
||
|
||
- [ ] 1.1 Read `openspec/specs/storage/spec.md` and confirm `AbstractStorage` surface
|
||
- [ ] 1.2 Read `openspec/AGENTS.md` §0c, §13, §14
|
||
- [ ] 1.3 Read archived `openspec/archive/2026-05-16-add-storage-cdn/` for the deferral and family conventions
|
||
- [ ] 1.4 Read `packages/flow/retry/src/` to confirm `runWithRetry` + `RetryStrategy` surface
|
||
- [ ] 1.5 Read `packages/events/events/src/` to confirm `EventBusLike` shape
|
||
- [ ] 1.6 Draft `proposal.md`, `design.md`, `specs/storage-mux/spec.md`, `tasks.md`
|
||
- [ ] 1.7 Numbered Requirement/Scenario checklist per §0c step 6
|
||
|
||
## 2. Scaffolding
|
||
|
||
- [ ] 2.1 Create `packages/storage/storage-mux/` mirroring `packages/storage/storage-cdn/` conventions
|
||
- [ ] 2.2 `package.json` v0.0.0, `"sideEffects": false`, subpath exports per feature directory
|
||
- [ ] 2.3 Deps: `@suaveplan/error`, `@suaveplan/events`, `@suaveplan/retry`, `@suaveplan/storage`, `@suaveplan/types`, `@opentelemetry/api`, `zod`, `tslib`
|
||
- [ ] 2.4 DevDeps: `@suaveplan/testing`, `@vitest/coverage-istanbul`, `fast-check`, `mitata`, `typescript`, `vite`, `vite-plugin-dts`, `vitest`
|
||
- [ ] 2.5 `tsconfig.json`, `vite.config.ts`, `vitest.config.ts` via `createTestConfig` with `thresholds: 100/100/100/100`
|
||
- [ ] 2.6 Root `README.md` with Modules section linking every co-located `.md`
|
||
|
||
## 3. Error registry
|
||
|
||
- [ ] 3.1 `src/error-codes/error-codes.ts` — `defineCodes("STORAGE_MUX", [...])` with all 14 codes: `ALL_BACKENDS_DOWN`, `NO_BACKENDS_CONFIGURED`, `HEALTHCHECK_FAILED`, `BACKEND_TIMEOUT`, `INVALID_MODE`, `INVALID_WEIGHT`, `WRITE_FANOUT_PARTIAL`, `BACKEND_ABORTED`, `BACKEND_NOT_FOUND`, `BACKEND_NAME_CONFLICT`, `BACKEND_DRAINING`, `WEIGHTED_REQUIRES_WEIGHTS`, `MIRROR_ORIGIN_REQUIRED`, `RECURSION_DEPTH_EXCEEDED`
|
||
- [ ] 3.2 `StorageMuxError extends SuaveplanError<StorageMuxErrorCode>` with `backendName`, `attemptIndex`, `attemptCount`, `mode` discriminants
|
||
- [ ] 3.3 `error-codes.md` + `error-codes.test.ts` — reachability sweep proving every code is thrown from a real call site
|
||
|
||
## 4. Types & schemas
|
||
|
||
- [ ] 4.1 `src/types/backend-entry.types.ts` — `BackendEntry` (`name`, `storage`, `weight?`, `healthcheck?`, `tags?`)
|
||
- [ ] 4.2 `src/types/mux-options.types.ts` — `MuxMode` union, `StorageMuxOptions`, `BackendStats`, `MuxStats`
|
||
- [ ] 4.3 `src/types/mux-options.schemas.ts` — Zod schemas: non-empty backends, mode whitelist, `weight >= 0`, `cooldownMs >= 0`, unique names
|
||
|
||
## 5. Classification
|
||
|
||
- [ ] 5.1 `src/classify/classify.ts` + `.md` + `.test.ts` — pure `classify(err) => "transient" | "semantic" | "abort"` per design §4
|
||
|
||
## 6. Bulkhead
|
||
|
||
- [ ] 6.1 `src/bulkhead/bulkhead.ts` + `.md` + `.test.ts` — `Promise.race` timeout, `AbortSignal.any` composition, hung-promise non-blocking
|
||
|
||
## 7. Health
|
||
|
||
- [ ] 7.1 `src/health/health.ts` + `.md` + `.test.ts` — `BackendHealth` tracker, exponential cooldown capped at `maxCooldownMs`, recovery probe, active healthcheck scheduling via injected `Clock`, draining accounting
|
||
|
||
## 8. Stats
|
||
|
||
- [ ] 8.1 `src/stats/stats.ts` + `.md` + `.test.ts` — per-backend counters, Vitter Algorithm R reservoir-sampled latencies (256 slots), `mux.stats()` snapshot API
|
||
|
||
## 9. Modes (six)
|
||
|
||
- [ ] 9.1 `src/modes/selector.types.ts` — `BackendSelector` + `BackendExecutor` interfaces
|
||
- [ ] 9.2 `src/modes/failover/failover.ts` + `.md` + `.test.ts` — fall-through + retry composition + `ALL_BACKENDS_DOWN`
|
||
- [ ] 9.3 `src/modes/round-robin/round-robin.ts` + `.md` + `.test.ts` — atomic counter, skip down/draining
|
||
- [ ] 9.4 `src/modes/weighted/weighted.ts` + `.md` + `.test.ts` — Walker alias-method, rebuild on mutation, weight validation
|
||
- [ ] 9.5 `src/modes/sticky-by-key/sticky-by-key.ts` + `.md` + `.test.ts` — FNV-1a, deterministic, redistribution on backend removal
|
||
- [ ] 9.6 `src/modes/shield/shield.ts` + `.md` + `.test.ts` — origin-first reads, fan-out writes, `WRITE_FANOUT_PARTIAL` opt-in
|
||
- [ ] 9.7 `src/modes/mirror/mirror.ts` + `.md` + `.test.ts` — `Promise.any` reads, `Promise.allSettled` writes, symmetric peers
|
||
|
||
## 10. Runtime mutation
|
||
|
||
- [ ] 10.1 `src/mutate/mutate.ts` + `.md` + `.test.ts` — `addBackend`, `removeBackend`, `setWeight`, `drain` with in-flight accounting
|
||
|
||
## 11. Observability
|
||
|
||
- [ ] 11.1 `src/observability/events.ts` + `.md` + `.test.ts` — event topic constants and emit helpers consuming `EventBusLike`
|
||
- [ ] 11.2 `src/observability/otel.ts` + `.md` + `.test.ts` — per-attempt child-span helper with attribute set
|
||
|
||
## 12. Mux core
|
||
|
||
- [ ] 12.1 `src/mux/mux.ts` + `.md` — `StorageMux extends AbstractStorage`, per-op routing through selector + bulkhead + `runWithRetry`, AbortSignal.any composition, recursion depth guard
|
||
- [ ] 12.2 `src/mux/mux.test.ts` — construction validation, mode dispatch, abort surfacing, recursion cap
|
||
- [ ] 12.3 `src/mux/mux.spec-conformance.test.ts` — `runStorageConformance` × 6 modes × 3-backend MemoryStorage fleet, plus one two-level recursive mux
|
||
|
||
## 13. Barrel + exports
|
||
|
||
- [ ] 13.1 `src/index.ts` — explicit named re-exports per §6a (NO `export *`)
|
||
- [ ] 13.2 `package.json` subpath exports — one entry per feature directory; verify count matches
|
||
|
||
## 14. Documentation
|
||
|
||
- [ ] 14.1 Sibling `.md` for every implementation `.ts` (≥ 200 words, all required sections, real-import examples)
|
||
- [ ] 14.2 `README.md` documents each of the six modes with a usage example + a section on runtime mutation + stats
|
||
|
||
## 15. Gates
|
||
|
||
- [ ] 15.1 `bun run check`
|
||
- [ ] 15.2 `bunx turbo lint typecheck test --filter=@suaveplan/storage-mux`
|
||
- [ ] 15.3 `bunx turbo test:coverage --filter=@suaveplan/storage-mux` (100/100/100/100)
|
||
- [ ] 15.4 `bun scripts/lint/check-test-runner-imports.ts` → zero output
|
||
- [ ] 15.5 Tier-purity grep `grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-mux/src/` → empty
|
||
- [ ] 15.6 Banned-pattern scan → empty
|
||
- [ ] 15.7 `bun scripts/openspec-conformance.ts @suaveplan/storage-mux`
|
||
|
||
## 16. Manifest & archival
|
||
|
||
- [ ] 16.1 `bun scripts/folder-reorg/generate-manifest.ts`
|
||
- [ ] 16.2 `bun scripts/generate-missing.ts`
|
||
- [ ] 16.3 Update `STATUS.md` and `SUMMARY.md`
|
||
- [ ] 16.4 `bunx markdownlint-cli2 --fix`
|
||
- [ ] 16.5 Fold `specs/storage-mux/spec.md` → `openspec/specs/storage-mux/spec.md`
|
||
- [ ] 16.6 Move change folder → `openspec/archive/<yyyy-mm-dd>-complete-storage-mux/`
|