Files
2026-05-27 23:29:53 +01:00

6.0 KiB

Tasks — storage-http

Every checkbox MUST reflect work actually done in this change.

1. Spec authoring

  • 1.1 Read openspec/specs/storage/spec.md and confirm capability boundary
  • 1.2 Read openspec/AGENTS.md §0c, §13, §14
  • 1.3 Read openspec/archive/2026-05-12-storage-file/ end-to-end as the closest precedent
  • 1.4 Read openspec/archive/2026-04-24-enterprise-storage-s3/ for network-backed adapter precedent (OTel + retry classification)
  • 1.5 Draft proposal.md from openspec/templates/proposal.md
  • 1.6 Draft design.md covering file layout, status→code map, range fallback, ETag cache, uploader contract, tier purity
  • 1.7 Draft specs/storage-http/spec.md from openspec/templates/spec-delta.md
  • 1.8 Every requirement in the spec delta carries a **Verified by:** line naming concrete planned it(...) titles
  • 1.9 Build the numbered Requirement/Scenario implementation checklist (per §0c step 6)

2. Scaffolding

  • 2.1 Create packages/storage/storage-http/ mirroring packages/storage/storage-file/ conventions
  • 2.2 Write package.json v0.0.0, "sideEffects": false, subpath exports for every feature module
  • 2.3 Wire deps: @suaveplan/error, @suaveplan/storage, @suaveplan/retry, @suaveplan/types, @opentelemetry/api, zod, tslib (all workspace:^ / catalog:)
  • 2.4 Wire devDeps: @suaveplan/testing, @vitest/coverage-istanbul, fast-check, mitata, typescript, vite, vite-plugin-dts, vitest
  • 2.5 Write tsconfig.json, tsconfig.lib.json, vite.config.ts, vitest.config.ts (via createTestConfig)
  • 2.6 Write tests/setup/polyfills.ts and README.md skeleton

3. Implementation

  • 3.1 src/error-codes.ts — defineCodes("STORAGE_HTTP", [...]) with 10 codes, StorageHttpError class, key/httpStatus/url discriminants
  • 3.2 src/types/http-storage.types.ts — HttpStorageOptions, FetchLike, HttpUploader, EtagCacheOptions interfaces
  • 3.3 src/types/http-storage.schemas.ts — HttpStorageOptionsSchema (Zod) with baseUrl http(s) + trailing-slash validation
  • 3.4 src/fetch-adapter/fetch-adapter.ts — defaultFetch() wrapping globalThis.fetch, AbortSignal propagation, network-reject → NETWORK_ERROR
  • 3.5 src/key-sanitizer/key-sanitizer.ts — sanitizeKey strict boundary + default per-segment encodeURIComponent
  • 3.6 src/range/range.ts — Range header builder + 206/200/416 disambiguation
  • 3.7 src/etag-cache/etag-cache.ts — in-memory LRU keyed by sanitised key
  • 3.8 src/uploader/uploader.ts — HttpUploader contract + presignedPutUploader(presigner) helper
  • 3.9 src/retry-helper/retry-helper.ts — transient status classifier + Retry-After honour via @suaveplan/retry
  • 3.10 src/otel/otel.ts — meter/tracer wiring, recordOp(op, outcome, statusClass, ms, bytes?)
  • 3.11 src/http-storage/http-storage.ts — HttpStorage extends AbstractStorage with all abstract methods, capabilities matrix, READ_ONLY enforcement, AbortSignal
  • 3.12 src/registry/registry.ts — declare module "@suaveplan/storage" + registerHttpProvider() (idempotent)
  • 3.13 src/index.ts — explicit named re-exports per §6a (no export * in any non-root barrel)

4. Tests

  • 4.1 src/key-sanitizer/key-sanitizer.test.ts — valid, traversal, absolute, null bytes, control chars, overlong (>2048), separator normalisation, per-segment encoding
  • 4.2 src/fetch-adapter/fetch-adapter.test.ts — forwards to globalThis.fetch, AbortSignal propagation, network-reject → NETWORK_ERROR
  • 4.3 src/range/range.test.ts — 206 verbatim, 200 fallback + counter increment, 416 → NOT_FOUND
  • 4.4 src/etag-cache/etag-cache.test.ts — LRU eviction, 304 returns cached body, 200 replaces entry
  • 4.5 src/uploader/uploader.test.ts — presignedPutUploader round-trip; injected uploader invoked exactly once on set
  • 4.6 src/retry-helper/retry-helper.test.ts — transient classifier, Retry-After honour under FakeClock, non-retry on 401
  • 4.7 src/otel/otel.test.ts — metrics via real in-memory MeterProvider; error span via in-memory SpanExporter
  • 4.8 src/http-storage/http-storage.test.ts — GET round-trip, 404 → undefined, HEAD exists/metadata, AbortSignal pre/mid, READ_ONLY on set/list/clear, set with uploader invokes put once
  • 4.9 src/http-storage/http-storage.spec-conformance.test.ts — type conformance, capabilities matrix, every STORAGE_HTTP code reachable, tier-purity grep
  • 4.10 src/registry/registry.test.ts — createStorage({ type: "http", … }) returns HttpStorage; idempotent re-registration
  • 4.11 Every it(...) carries { timeout: 5000 } (or 30000 for genuinely async)
  • 4.12 No imports from vitest or bun:test in test files — all via @suaveplan/testing/runner (§7)

5. Documentation

  • 5.1 Sibling .md for every implementation .ts (≥200 words, basic + advanced examples, full API reference)
  • 5.2 README.md links every module under ## Modules
  • 5.3 README documents composition recipe: storage-http behind storage-file NamespacedStorage for cross-process disk cache

6. Gates

  • 6.1 bun run check
  • 6.2 bunx turbo lint typecheck test --filter=@suaveplan/storage-http
  • 6.3 bunx turbo test:coverage --filter=@suaveplan/storage-http (100/100/100/100 Istanbul)
  • 6.4 bun scripts/lint/check-test-runner-imports.ts (rule 11)
  • 6.5 Tier-purity grep — grep -rnE "from ['\"](node:|bun:)" packages/storage/storage-http/src/ returns empty
  • 6.6 bun scripts/openspec-conformance.ts @suaveplan/storage-http

7. Manifest & archival

  • 7.1 Add package to MANIFEST.md via bun scripts/folder-reorg/generate-manifest.ts
  • 7.2 Update STATUS.md (genesis-original — add to "Completed" once shipped)
  • 7.3 Fold specs/storage-http/spec.md into openspec/specs/storage-http/spec.md
  • 7.4 Move openspec/changes/add-storage-http/ → openspec/archive/<yyyy-mm-dd>-add-storage-http/