# Tasks — storage-mux > Every checkbox MUST reflect work actually done in this change. ## 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/retry/retry.ts` to confirm `runWithRetry` + `RetryStrategy` surface - [ ] 1.5 Draft `proposal.md`, `design.md`, `specs/storage-mux/spec.md`, `tasks.md` - [ ] 1.6 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 - [ ] 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`, `vite.config.ts`, `vitest.config.ts` via `createTestConfig` - [ ] 2.6 `README.md` root with Modules section linking every `.md` ## 3. Error registry - [ ] 3.1 `src/error-codes.ts` — `defineCodes("STORAGE_MUX", [...])` with 8 codes (`ALL_BACKENDS_DOWN`, `NO_BACKENDS_CONFIGURED`, `HEALTHCHECK_FAILED`, `BACKEND_TIMEOUT`, `INVALID_POLICY`, `INVALID_WEIGHT`, `WRITE_FANOUT_PARTIAL`, `BACKEND_ABORTED`) - [ ] 3.2 `src/error-codes.ts` — `StorageMuxError extends SuaveplanError` with `backendName`, `attemptIndex`, `attemptCount` discriminants ## 4. Types & schemas - [ ] 4.1 `src/types/backend-entry.types.ts` — `BackendEntry` interface (`name`, `storage`, `weight?`, `healthcheck?`) - [ ] 4.2 `src/types/mux-options.types.ts` — `MuxPolicy` discriminated string union, `StorageMuxOptions` - [ ] 4.3 `src/types/mux-options.schemas.ts` — Zod schemas validating non-empty backends, policy whitelist, weight ≥ 0 ## 5. Selectors / policies - [ ] 5.1 `src/policies/selector.types.ts` — `BackendSelector` interface (`select(op, key?, healthy): BackendEntry`) - [ ] 5.2 `src/policies/classify.ts` + `.md` + `.test.ts` — pure error classification per design §4 table - [ ] 5.3 `src/policies/failover/failover.ts` + `.md` + `.test.ts` — fall-through + retry composition - [ ] 5.4 `src/policies/round-robin/round-robin.ts` + `.md` + `.test.ts` — atomic counter, skip down - [ ] 5.5 `src/policies/sticky-by-key/sticky-by-key.ts` + `.md` + `.test.ts` — FNV-1a hash, deterministic - [ ] 5.6 `src/policies/shield/shield.ts` + `.md` + `.test.ts` — read cascade, write fan-out, partial signal ## 6. Health - [ ] 6.1 `src/health/health.ts` + `.md` + `.test.ts` — `BackendHealth` tracker, exponential cooldown, recovery probe, active healthcheck via injected `Clock` ## 7. Mux core - [ ] 7.1 `src/mux/mux.ts` + `.md` — `StorageMux extends AbstractStorage`, per-op routing through `BackendSelector` + `runWithRetry`, per-backend timeout race, AbortSignal linking - [ ] 7.2 `src/mux/mux.test.ts` — construction validation, policy dispatch, abort surfacing - [ ] 7.3 `src/mux/mux.spec-conformance.test.ts` — `runStorageConformance` × 4 policies × 3 `MemoryStorage` backends ## 8. Observability - [ ] 8.1 `src/observability/events.ts` + `.md` + `.test.ts` — event topic constants (`storage-mux.backend.down`, `.recovered`, `.failover`, `.fanout-partial`), `emit*` helpers consuming `EventBusLike` - [ ] 8.2 `src/observability/otel.ts` + `.md` + `.test.ts` — span attribute helpers (`mux.backend.name`, `mux.attempt.index`, `mux.policy`) ## 9. Barrel + exports - [ ] 9.1 `src/index.ts` — explicit named re-exports per §6a (NO `export *`) - [ ] 9.2 `package.json` subpath exports match every feature directory ## 10. Tests - [ ] 10.1 `src/error-codes/error-codes.test.ts` — every `STORAGE_MUX_*` code reachable from a real call site - [ ] 10.2 All test imports via `@suaveplan/testing/runner` (§7) - [ ] 10.3 Every `it(...)` carries `{ timeout: 5000 }` (or 30000 for async health) - [ ] 10.4 Backend isolation test: hung-promise primary does NOT block fallback within `backendTimeoutMs` ## 11. Documentation - [ ] 11.1 Sibling `.md` for every implementation `.ts` (≥ 200 words, all required sections) - [ ] 11.2 `README.md` documents each policy with a usage example ## 12. Gates - [ ] 12.1 `bun run check` - [ ] 12.2 `bunx turbo lint typecheck test --filter=@suaveplan/storage-mux` - [ ] 12.3 `bunx turbo test:coverage --filter=@suaveplan/storage-mux` (100/100/100/100) - [ ] 12.4 `bun scripts/lint/check-test-runner-imports.ts` - [ ] 12.5 Tier-purity grep `grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-mux/src/` returns empty - [ ] 12.6 Banned-pattern scan returns empty - [ ] 12.7 `bun scripts/openspec-conformance.ts @suaveplan/storage-mux` ## 13. Manifest & archival - [ ] 13.1 `bun scripts/folder-reorg/generate-manifest.ts` - [ ] 13.2 `bun scripts/generate-missing.ts` - [ ] 13.3 Update `STATUS.md` and `SUMMARY.md` - [ ] 13.4 `bunx markdownlint-cli2 --fix` - [ ] 13.5 Fold `specs/storage-mux/spec.md` → `openspec/specs/storage-mux/spec.md` - [ ] 13.6 Move change folder → `openspec/archive/-add-storage-mux/`