Files
suaveplan/openspec/archive/2026-05-17-add-storage-uri-plugin-fallback/tasks.md
2026-05-27 23:29:53 +01:00

114 lines
8.7 KiB
Markdown

# Tasks — add-storage-uri-plugin-fallback
> Every checkbox MUST reflect work actually done in this change.
## 1. Spec authoring
- [ ] 1.1 Read `openspec/AGENTS.md` §0, §0a, §0b, §0c, §13, §14
- [ ] 1.2 Read `openspec/project.md`
- [ ] 1.3 Read `openspec/specs/storage-uri/spec.md` to confirm `UriPlugin` + `StorageUriResolver` contract
- [ ] 1.4 Read `packages/storage/storage-uri/src/` (resolver, types, conformance) for recursive-dispatch surface
- [ ] 1.5 Read `packages/flow/retry/src/` to confirm `RetryStrategy` surface for documentation cross-links
- [ ] 1.6 Read `openspec/changes/complete-storage-mux/` for the multi-backend failover precedent (error classification, fan-out semantics, span hierarchy)
- [ ] 1.7 Draft `proposal.md`, `design.md`, `specs/storage-uri-plugin-fallback/spec.md`, `tasks.md`
- [ ] 1.8 Numbered Requirement/Scenario checklist per §0c step 6 — every requirement and every scenario in the delta tracked as an unchecked item until implementation lands
## 2. Scaffolding
- [ ] 2.1 Create `packages/storage/storage-uri-plugin-fallback/`
- [ ] 2.2 `package.json` v0.0.0, `"sideEffects": false`, subpath exports per feature directory
- [ ] 2.3 Deps: `@suaveplan/error`, `@suaveplan/events`, `@suaveplan/storage-uri`, `@suaveplan/types`, `@opentelemetry/api`, `zod`, `tslib`
- [ ] 2.4 DevDeps: `@suaveplan/storage`, `@suaveplan/storage-file`, `@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("FALLBACK_URI", [...])` with all 8 codes: `NO_CANDIDATES`, `INVALID_URI`, `ALL_CANDIDATES_FAILED`, `WRITE_MIRROR_FAILED`, `LIST_UNION_FAILED`, `ABORTED`, `MAX_DEPTH_EXCEEDED`, `CIRCULAR_REFERENCE`
- [ ] 3.2 `FallbackUriError extends SuaveplanError<FallbackUriErrorCode>` with `uri`, `candidateIndex`, `candidateUri`, `candidateErrors`, `mirrorOutcomes`, `cycleChain`, `depth` discriminants (only the ones relevant to each code are populated)
- [ ] 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/policy.types.ts` — `FallbackPolicy` (`errorClass`, `maxParallel`, `stopOnAbort`, `writeMode`, `deleteMode`, `listMode`, `listFailureMode`, `maxDepth`, `maxCandidates`), `FallbackPluginOptions`
- [ ] 4.2 `src/types/policy.schemas.ts` — Zod schemas: `maxParallel >= 1`, `maxDepth >= 1 && <= 8`, `maxCandidates >= 1 && <= 64`, mode enums
- [ ] 4.3 `src/types/candidate.types.ts` — `CandidateError`, `CandidateAttempt`, `MirrorOutcome`
## 5. URI grammar
- [ ] 5.1 `src/parse-uri/parse-fallback-uri.ts` — `parseFallbackUri(uri)` returning `{ candidates: readonly string[]; canonicalUri: string }`; `buildFallbackUri(uris)` with `URLSearchParams`; reject empty / oversized / non-`fallback` scheme / non-empty authority
- [ ] 5.2 `src/parse-uri/parse-fallback-uri.md` + `.test.ts` — exhaustive validation matrix; fast-check round-trip property `buildFallbackUri ∘ parseFallbackUri = identity` on a small generator
- [ ] 5.3 `src/parse-uri/abort-any.ts` — `AbortSignal.any` shim guarded by `typeof AbortSignal.any !== "function"`
## 6. Classification
- [ ] 6.1 `src/classify/classify.ts` + `.md` + `.test.ts` — pure `classify(err, policy) => "transient" | "semantic" | "abort"` per design §7; consumes `@suaveplan/error#SuaveplanError` for code inspection
## 7. Recursion context
- [ ] 7.1 `src/recursion/depth-context.ts` + `.md` + `.test.ts` — `DepthContext` value type, `withChildContext` helper that bumps depth + extends visited set, depth-cap enforcement throwing `MAX_DEPTH_EXCEEDED`, visited-set canonicalisation via `parseFallbackUri` reserialisation
## 8. Walk strategies
- [ ] 8.1 `src/walk/sequential.ts` + `.md` + `.test.ts` — ordered fall-through; emits attempt-failed events; throws `ALL_CANDIDATES_FAILED` on exhaustion with `candidateErrors` populated
- [ ] 8.2 `src/walk/parallel.ts` + `.md` + `.test.ts` — `Promise.any`-style race with concurrency cap (`maxParallel`); cancels pending candidates via `AbortSignal.any` once one succeeds
## 9. Operations (full UriPlugin surface)
- [ ] 9.1 `src/ops/read.ts` + `.md` + `.test.ts` — `get(key)` + `createReadStream(key, signal)` + range `{ offset, length }` propagation
- [ ] 9.2 `src/ops/write.ts` + `.md` + `.test.ts` — `set(key, bytes)` + `createWriteStream(key, body, signal)`; `writeMode: "all"` → `Promise.allSettled` + `WRITE_MIRROR_FAILED`; `writeMode: "first-success"` → walk + background settle
- [ ] 9.3 `src/ops/metadata.ts` + `.md` + `.test.ts` — `exists` + `getMetadata` sequential walk
- [ ] 9.4 `src/ops/delete.ts` + `.md` + `.test.ts` — mirror delete with `deleteMode: "any-success" | "all"` (default `"any-success"`)
- [ ] 9.5 `src/ops/list.ts` + `.md` + `.test.ts` — union with dedup; `listMode: "union" | "concat"`; `listFailureMode: "throw" | "partial"`
## 10. Observability
- [ ] 10.1 `src/observability/events.ts` + `.md` + `.test.ts` — topic constants (`storage-uri.fallback.attempt-failed`, `.attempt-succeeded`, `.write-mirror-partial`, `.cycle-detected`, `.depth-exceeded`); emit helpers consuming `EventBusLike`
- [ ] 10.2 `src/observability/otel.ts` + `.md` + `.test.ts` — counter `storage_uri.fallback_total{candidate_index, scheme, outcome}`, histogram `storage_uri.fallback_walk_ms{outcome}`, parent + per-attempt child spans with attribute set
## 11. Plugin factory
- [ ] 11.1 `src/plugin/plugin.ts` + `.md` — `fallbackUriPlugin(options)`; wires walk + ops + observability + recursion; binds to `fallback` scheme; produces `UriPlugin` whose `adapter` exposes the full streaming-extended `StorageLike` surface
- [ ] 11.2 `src/plugin/plugin.test.ts` — construction validation (missing resolver, bad policy), abort short-circuit, sequential success, sequential exhaustion, parallel race, mirror write, cycle, depth cap
- [ ] 11.3 `src/plugin/plugin.spec-conformance.test.ts` — `runUriPluginConformance(plugin, fixtures)` pass with `validUris` / `invalidUris` / `buildRoundTrip` populated from `parseFallbackUri` / `buildFallbackUri`
## 12. Fallback-specific conformance
- [ ] 12.1 `src/conformance/fallback-conformance.ts` + `.md` — `runFallbackConformance(resolver, fixtures)` exporting the harness consumers (audio-IR loader, drive+CDN pairing) call from their own tests
- [ ] 12.2 `src/conformance/fallback-conformance.test.ts` — self-test against the in-package plugin: K-fail-then-success, all-fail, abort, cycle, depth, mirror-write
## 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` — install, URI grammar, simple example (`fallbackUriPlugin` registered alongside other plugins), advanced example (mirror writes + dedup list across CDN + drive), policy reference, complementarity note linking to `@suaveplan/storage-mux`
## 15. Gates
- [ ] 15.1 `bun run check`
- [ ] 15.2 `bunx turbo lint typecheck test --filter=@suaveplan/storage-uri-plugin-fallback`
- [ ] 15.3 `bunx turbo test:coverage --filter=@suaveplan/storage-uri-plugin-fallback` (100/100/100/100)
- [ ] 15.4 `bun scripts/lint/check-test-runner-imports.ts` → zero output for this package
- [ ] 15.5 Tier-purity grep `grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-uri-plugin-fallback/src/` → empty
- [ ] 15.6 Banned-pattern scan `grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only" packages/storage/storage-uri-plugin-fallback/src/ --include="*.ts"` → empty
- [ ] 15.7 Barrel-shape scan per §6a → empty
- [ ] 15.8 Bare-version scan → empty
- [ ] 15.9 Missing `.md` scan → empty
- [ ] 15.10 README present
- [ ] 15.11 Subpath export count matches feature module count
- [ ] 15.12 `bun scripts/openspec-conformance.ts @suaveplan/storage-uri-plugin-fallback`
- [ ] 15.13 Dependent sweep `bunx turbo test --filter=...@suaveplan/storage-uri-plugin-fallback`
## 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-uri-plugin-fallback/spec.md` → `openspec/specs/storage-uri-plugin-fallback/spec.md`
- [ ] 16.6 Move change folder → `openspec/archive/<yyyy-mm-dd>-add-storage-uri-plugin-fallback/`