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

8.1 KiB

Tasks — enterprise-browser-storage-browser

Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify. Retroactive ticking is the failure mode this template guards against.

1. Spec authoring

  • 1.1 Read ../suaveplan/enterprise/storage-browser.md end-to-end
  • 1.2 Read openspec/specs/storage/spec.md
  • 1.3 Read openspec/AGENTS.md Sections 13 and 14
  • 1.4 Draft proposal.md from openspec/templates/proposal.md
  • 1.5 Fill in ## Source mapping table — every requirement cites a line range OR appears in the "no source line" table with justification
  • 1.6 Draft specs/storage/spec.md from openspec/templates/spec-delta.md
  • 1.7 Every requirement in the spec delta carries a **Verified by:** line naming concrete it(...) titles

2. Implementation

  • 2.1 Scaffold packages/browser/storage/storage-browser/ with directory tree per proposal.md
  • 2.2 Write package.json v0.0.0, "sideEffects": false
    • Dependencies: @suaveplan/storage workspace:^, @suaveplan/crypto workspace:^, @suaveplan/events workspace:^, @suaveplan/error workspace:^, @suaveplan/types workspace:^, @suaveplan/logger workspace:^, @suaveplan/broadcast-channel workspace:^, @suaveplan/schema-registry workspace:^, zod catalog:forms, @opentelemetry/api catalog:observability, tslib catalog:build
    • DevDependencies: vitest catalog:testing, @vitest/coverage-istanbul catalog:testing, fake-indexeddb (^6.0.0) — must be added to catalog:testing before implementation, @playwright/test catalog:testing, @suaveplan/testing workspace:^
    • Subpath exports: ".", "./indexeddb", "./localstorage", "./register", "./types"
  • 2.3 Write tsconfig.json, vitest.config.ts (pool: forks, fileParallelism: false)
  • 2.4 Implement src/error-codes.ts: STORAGE_QUOTA_EXCEEDED, STORAGE_KEY_NOT_FOUND, STORAGE_ENCRYPTION_FAILED, STORAGE_COMPRESSION_FAILED, STORAGE_CONCURRENCY_CONFLICT; registerStorageBrowserErrorCodes() called eagerly
  • 2.5 Implement src/types/types.ts: StorageEntry, StorageMetadata, BatchOperation, StorageEvent, ListOptions, IndexedDBStorageConfig, LocalStorageStorageConfig; write types.md
  • 2.6 Implement src/indexeddb/store/store.ts: IDB openDB, upgrade handler, CRUD transactions (get/set/delete/clear/list)
  • 2.7 Implement src/indexeddb/ttl/ttl.ts: proactive sweep via requestIdleCallback + expiresAt cursor; batch-delete expired rows
  • 2.8 Implement src/indexeddb/migrations/migrations.ts: MigrationRunner accepting step-wise callbacks by schema version
  • 2.9 Implement src/indexeddb/adapter/indexeddb.ts: IndexedDBStorage composing store + TTL + migrations; batch(ops) in single IDB transaction; optimistic concurrency via updatedAt compare-and-set; OTel metrics + BroadcastChannel invalidation
  • 2.10 Implement src/localstorage/store/store.ts: Base64+JSON envelope, namespace prefixing, CRUD
  • 2.11 Implement src/localstorage/ttl/ttl.ts: TTL expiry on read + periodic sweep
  • 2.12 Implement src/localstorage/adapter/localstorage.ts: LocalStorageStorage composing store + TTL; OTel metrics + BroadcastChannel invalidation
  • 2.13 Implement src/wrappers/encrypted/encrypted.ts: EncryptedStorage wrapping any AbstractStorage; AES-GCM via @suaveplan/crypto; throw STORAGE_ENCRYPTION_FAILED on key error
  • 2.14 Implement src/wrappers/compressed/compressed.ts: CompressedStorage with CompressionStream gzip above configurable threshold; throw STORAGE_COMPRESSION_FAILED on stream error
  • 2.15 Implement src/sync/broadcast/broadcast-sync.ts: BroadcastStorageSync emitting set/delete/clear on a typed channel
  • 2.16 Implement src/observable/observe-storage/observe-storage.ts: observeStorage(storage, key): AsyncIterable<StorageEvent> backed by BroadcastChannel
  • 2.17 Implement src/register.ts: registerIndexedDBStorage() and registerLocalStorageStorage() — explicit factory registration, intentionally not a side-effect
  • 2.18 Wire OTel metrics: counter storage.<operation>_total{backend}, histogram storage.op_ms{backend,operation}, counter storage.ttl_evictions_total

3. Tests

  • 3.1 Unit tests at src/indexeddb/store/store.test.ts: CRUD, concurrent transactions (fake-indexeddb)
  • 3.2 Unit tests at src/indexeddb/ttl/ttl.test.ts: sweep deletes expired, leaves fresh
  • 3.3 Unit tests at src/indexeddb/migrations/migrations.test.ts: versioned upgrade callbacks
  • 3.4 Unit tests at src/indexeddb/adapter/indexeddb.test.ts: full CRUD, TTL, batch, concurrency conflict, cross-tab invalidation event, quota exceeded
  • 3.5 Spec-conformance tests at src/indexeddb/adapter/indexeddb.spec-conformance.test.ts: type-level conformance, OTel metric assertions with real MeterProvider, strict-mode behaviours
  • 3.6 Unit tests at src/localstorage/store/store.test.ts: Base64 round-trip, namespace isolation
  • 3.7 Unit tests at src/localstorage/ttl/ttl.test.ts: expiry on read, sweep
  • 3.8 Unit tests at src/localstorage/adapter/localstorage.test.ts: full CRUD, TTL, namespace, broadcast events
  • 3.9 Unit tests at src/wrappers/encrypted/encrypted.test.ts: encrypted round-trip, raw storage sees ciphertext
  • 3.10 Unit tests at src/wrappers/compressed/compressed.test.ts: large payload compressed, small payload passes through
  • 3.11 Unit tests at src/sync/broadcast/broadcast-sync.test.ts: cross-tab invalidation event, clear event propagation
  • 3.12 Unit tests at src/observable/observe-storage/observe-storage.test.ts: iterator yields on remote mutation, iterator completes on abort
  • 3.13 Unit tests at src/register.test.ts: import does not register, explicit registration works
  • 3.14 Unit tests at src/error-codes.test.ts: all codes registered
  • 3.15 Integration test at tests/integration.test.ts: both adapters via same AbstractStorage interface; encrypted + compressed wrappers stacked over IndexedDB; batch operations atomicity. ≤500 lines.
  • 3.16 Playwright E2E at e2e/storage-browser.spec.ts: Chromium, WebKit, Firefox; cross-tab invalidation; quota-exceeded guard surfaces STORAGE_QUOTA_EXCEEDED; encrypted payload not readable in raw IDB
  • 3.17 Every it(...) carries { timeout: 5000 } (or 30000 for genuinely async)

4. Documentation

  • 4.1 Sibling .md for every implementation .ts (≥200 words, basic + advanced examples, full API reference)
  • 4.2 Root README.md with subpath table, cross-tab guide, wrapper composition, links to every co-located .md
  • 4.3 Update CLAUDE.md package listing if this is a new package

5. Pre-archive verification (the 5-gate triple)

  • 5.1 bun run check — Biome 0 errors / 0 warnings / 0 info across the whole monorepo
  • 5.2 bunx turbo lint typecheck test --filter=@suaveplan/storage-browser — green
  • 5.3 bunx turbo test:coverage --filter=@suaveplan/storage-browser — 100/100/100/100
  • 5.4 bun scripts/openspec-conformance.ts @suaveplan/storage-browser — exit 0, no error findings
  • 5.5 bunx turbo build --filter=@suaveplan/storage-browser — green

6. Pre-archive 7-row checklist (AGENTS.md Section 13)

  • 6.1 Generics threaded; expectTypeOf assertions on every literal-type claim
  • 6.2 Zod schemas wired at every declared boundary
  • 6.3 Every error code in error-codes.ts thrown by at least one production path
  • 6.4 Strict-mode behaviors tested both on and off
  • 6.5 OTel instruments emitted from production paths and asserted with a real in-memory MeterProvider
  • 6.6 Lifecycle dedup tested where claimed (in-flight Promise, idempotent set/load)
  • 6.7 No retroactive tick — every box above reflects work done in this change

7. Archive

  • 7.1 Fold spec delta into openspec/specs/storage/spec.md
  • 7.2 Move openspec/changes/enterprise-browser-storage-browser/ → openspec/archive/<yyyy-mm-dd>-enterprise-browser-storage-browser/
  • 7.3 Refresh MANIFEST.md if package set or deps changed