8.4 KiB
8.4 KiB
Tasks — add-storage-plugin-cache
Every checkbox MUST reflect work actually done in this change.
1. Spec authoring
- 1.1 Read
openspec/specs/storage/spec.mdand confirmAbstractStorage's 15 abstract methods and theNamespacedStoragedecorator precedent - 1.2 Read
openspec/specs/cache/spec.mdand confirmAbstractCache<T>contract (especially whetherkeys()is part of the canonical surface) - 1.3 Read
openspec/archive/2026-05-16-add-cache-storage-redis-backends/for the cache-family conventions - 1.4 Read sibling proposal
openspec/changes/add-drive-plugin-cache/proposal.md— match option shape, error-swallow rules, OTel naming - 1.5 Read
openspec/AGENTS.md§0c, §2b, §6a, §7, §11, §13 - 1.6 Draft
proposal.md,design.md,tasks.md,specs/storage-plugin-cache/spec.md - 1.7 Every requirement in the spec delta carries a
**Verified by:**line naming concrete plannedit(...)titles - 1.8 Build the numbered Requirement/Scenario implementation checklist (§0c step 6) — 13 requirements, ~30 scenarios
2. Scaffolding
- 2.1 Create
packages/storage/storage-plugin-cache/mirroringpackages/storage/storage-uri/conventions - 2.2 Write
package.jsonv0.0.0,"sideEffects": false, subpath exports for every feature module - 2.3 Wire deps:
@suaveplan/error,@suaveplan/storage,@suaveplan/cache,@suaveplan/types,@opentelemetry/api,tslib(allworkspace:^/catalog:) - 2.4 Wire devDeps:
@suaveplan/testing,@suaveplan/storage-file,@suaveplan/storage-http,@suaveplan/cache-storage,@vitest/coverage-istanbul,fast-check,typescript,vite,vite-plugin-dts,vitest - 2.5 Write
tsconfig.json,tsconfig.lib.json,vite.config.ts,vitest.config.ts(viacreateTestConfig) - 2.6 Write
tests/setup/polyfills.tsandREADME.mdskeleton
3. Implementation
- 3.1
src/error-codes/error-codes.ts—defineCodes("STORAGE_CACHE", ["INVALID_OPTIONS", "INNER_MISSING"] as const),CachedStorageErrorclass,key?/namespace?discriminants - 3.2
src/types/options.types.ts—CachedStorageOptions,CachedStorageStats,ResolvedCachedStorageOptionsinterfaces (no inline shapes per §11) - 3.3
src/cached-storage/cached-storage.signal.ts—AbortSignalpre-aborted short-circuit helper - 3.4
src/cached-storage/cached-storage.get.ts—get(key, signal?)read-through with hit/miss/populate - 3.5
src/cached-storage/cached-storage.write.ts—set(key, value, signal?)andsetPartial(...)invalidation - 3.6
src/cached-storage/cached-storage.delete.ts—delete(key, signal?)invalidation - 3.7
src/cached-storage/cached-storage.clear.ts—clear(signal?)namespaced enumeration + fallback warning - 3.8
src/cached-storage/cached-storage.passthrough.ts—exists/list/getMetadata/getWithMetadata/getSize/getItemSize/getPartial/createReadStream/createWriteStreamdelegates - 3.9
src/cached-storage/cached-storage.ts—class CachedStorage extends AbstractStorageassembling the helpers from.get.ts/.write.ts/.delete.ts/.clear.ts/.passthrough.ts/.signal.ts. Constructor validation;cache/inner/getCacheStats()accessors. - 3.10
src/factory/factory.ts—cachedStorage(inner, options): CachedStoragefactory + synchronousvalidateOptions(options)throwingINVALID_OPTIONS/INNER_MISSING - 3.11
src/otel/otel.ts— meter + tracer wiring, countershits/misses/populations/invalidations/errors, histogramentry_bytes, span attribute helpers - 3.12
src/index.ts— explicit named re-exports per §6a
4. Tests (every it(...) carries { timeout: 5000 }, 30000 for async integration)
- 4.1
src/error-codes/error-codes.test.ts— both codes reachable from a real call site, discriminants populated - 4.2
src/factory/factory.test.ts— factory returnsCachedStorage, throwsINNER_MISSINGon nullish inner, throwsINVALID_OPTIONSon missing/malformed cache - 4.3
src/cached-storage/cached-storage.test.ts— instance isinstanceof AbstractStorageANDinstanceof CachedStorage,cache/innerexposed by reference,getCacheStatssnapshot semantics - 4.4
src/cached-storage/cached-storage.get.test.ts— hit short-circuits inner, custom namespace in key, miss populates,shouldCachegate, 16 MiB ceiling,undefinednot cached, cache.get rejection falls through, cache.set rejection swallowed - 4.5
src/cached-storage/cached-storage.write.test.ts—setinvalidates post-success, failedsetdoes not invalidate,setPartialinvalidates whole entry,invalidateOnWrite:falseskips, cache.delete rejection swallowed - 4.6
src/cached-storage/cached-storage.delete.test.ts—deleteinvalidates post-success, faileddeletedoes not invalidate,invalidateOnDelete:falseskips - 4.7
src/cached-storage/cached-storage.clear.test.ts— namespaced enumeration viacache.keys(), warning + skip whenkeys()absent,invalidateOnClear:falseskips - 4.8
src/cached-storage/cached-storage.passthrough.test.ts— every passthrough method delegates without touching the cache (oneit(...)per method) - 4.9
src/cached-storage/cached-storage.signal.test.ts— pre-aborted skips cache work inget, pre-aborted skips invalidation inset - 4.10
src/otel/otel.test.ts— counters increment, histogram observed, span outcome attribute correct, errors_totaloplabel populated - 4.11
tests/integration/memory-in-front-of-http.test.ts— realMemoryCache+ realstorage-http(in-process fetch stub): two reads, inner invoked once - 4.12
tests/integration/file-cache-survives-restart.test.ts— realStorageCacheover realstorage-file+ in-memory inner: cache survives wrapper re-instantiation - 4.13
tests/integration/namespaced-isolation.test.ts— two wrappers / two namespaces / one cache: isolation ongetand onclear - 4.14 No imports from
vitestorbun:testin test files — all via@suaveplan/testing/runner(rule 11) - 4.15 No
vi.mock/jest.mock; all dependencies are real per CLAUDE.md rule 2
5. Documentation
- 5.1 Sibling
.mdfor every implementation.ts(≥200 words, basic + advanced examples, full API reference) - 5.2
README.mdlinks every module under## Modules - 5.3 README documents the six composition recipes from
design.md(memory→S3, OPFS→HTTPS, file→Bunny, Redis→S3,wrap()SWR, compose withstorage-uri) - 5.4 README documents the relationship to
@suaveplan/cache-storage(the inverse direction) and toadd-drive-plugin-cache(the drive-tier twin) - 5.5 README documents the
shouldCache/maxEntryBytesdefaults and the rationale (16 MiB matchesdrive-mesh-protocol's frame ceiling anddrive-plugin-cache's ceiling)
6. Gates
- 6.1
bun run check - 6.2
bunx turbo lint typecheck test --filter=@suaveplan/storage-plugin-cache - 6.3
bunx turbo test:coverage --filter=@suaveplan/storage-plugin-cache— 100/100/100/100 Istanbul - 6.4
bunx turbo build --filter=@suaveplan/storage-plugin-cache - 6.5
bun scripts/lint/check-test-runner-imports.ts(rule 11) - 6.6 Tier-purity grep —
grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-plugin-cache/src/returns empty - 6.7 Banned-pattern scan returns empty (§2)
- 6.8 Missing-
.mdscan returns empty (§4) - 6.9 Non-root
export *scan returns empty (§6a) - 6.10 Bare-version scan returns empty (§1)
- 6.11 Folder-naming scan (§10) — children under
cached-storage/andfactory/do not include the parent's singular form as a suffix - 6.12 Spec-reconciliation gate — every
### Requirementis implemented; every**Verified by:**test name exists verbatim (§13 step 0) - 6.13 File-size scan — every
src/**/*.ts≤ 300 lines, every*.test.ts≤ 500 lines
7. Manifest & archival
- 7.1 Add package to
MANIFEST.mdviabun scripts/folder-reorg/generate-manifest.ts - 7.2 Update
STATUS.md(genesis-original — add to "Completed" once shipped) - 7.3 Update
SUMMARY.mdwith the one-paragraph description - 7.4
bunx markdownlint-cli2 --fixon touched docs - 7.5 Fold
specs/storage-plugin-cache/spec.mdintoopenspec/specs/storage-plugin-cache/spec.md - 7.6 Move
openspec/changes/add-storage-plugin-cache/→openspec/archive/<yyyy-mm-dd>-add-storage-plugin-cache/