# Tasks — enhance-packer-browser-streaming ## 1. Spec authoring - [x] 1.1 Read `../suaveplan/enterprise/packer-browser.md` (§4 items 1, 2, 4, 6) - [x] 1.2 Read `openspec/specs/packer-browser/spec.md` (Cluster A — PACKER-BROWSER-1..12) - [x] 1.3 Read `openspec/AGENTS.md` Sections 13 and 14 - [x] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md` - [x] 1.5 Fill in `## Source mapping` — every requirement cites audit line range - [x] 1.6 Draft `specs/packer-browser/spec.md` (Cluster B delta — PACKER-BROWSER-13..16) from `openspec/templates/spec-delta.md` - [x] 1.7 Every requirement carries a `**Verified by:**` line naming concrete `it(...)` titles ## 2. Implementation - [ ] 2.1 Add `KEY_UNAVAILABLE` to `src/error-codes.ts` (+ metadata) and matching i18n keys to `src/i18n/messages.ts` - [ ] 2.2 Implement `src/keyring/keyring.ts` — `KeyringProvider`, `emptyKeyring()`, `MemoryKeyring` - [ ] 2.3 Implement `src/encrypted-archive/encrypted-archive.ts` — `SP-ENC1` wire format encoder/decoder, AES-256-GCM helpers wrapping `aeadEncrypt`/`aeadDecrypt` - [ ] 2.4 Wire `keyring` into `PackedResourceLoader.load` (decrypt step before unpack) - [ ] 2.5 Implement `src/loader/loader-stream.ts` — `PackedResourceLoader.loadStream` async iterator yielding decoded entries as they arrive - [ ] 2.6 Wire `PackedResourceManager.loadStream` pass-through with cache insertion - [ ] 2.7 Implement `src/range-response/range-response.ts` — RFC 7233 §2.1 single-range parser + `Response` builder (`Content-Range`, `Accept-Ranges`, `Content-Length`, `If-Range` etag handling, 206/200/412/416) - [ ] 2.8 Wire `serveRangeResponse` into `service-worker.ts` `fetch` handler behind `rangeThreshold` option (default 256 KiB) - [ ] 2.9 Implement `src/content-address/content-address.ts` — `computeArchiveContentHash`, `buildContentAddressedUrl`, `cleanupOutdatedCaches` - [ ] 2.10 Wire `cleanupOutdatedCaches` into service-worker `activate` handler when `contentAddressed` is on; recognise `legacyUrlPattern` in `fetch` handler when supplied - [ ] 2.11 Register `PACKER_BROWSER_FLAGS.URL_CONTENT_ADDRESSED` via `flags/flags.ts` and surface from `PackedResourceManager` - [ ] 2.12 Add 5 new exports to `src/index.ts` barrel (explicit named per §6a) ## 3. Tests - [ ] 3.1 `src/loader/loader-stream.test.ts` — yields entries as decoded; pre-aborted signal short-circuits; mid-stream abort terminates cleanly; coexists with batch `load`; order-of-completion preserved - [ ] 3.2 `src/manager/manager.test.ts` — manager's `loadStream` writes to cache as entries arrive (additive scenarios on the existing file; existing tests remain green) - [ ] 3.3 `src/range-response/range-response.test.ts` — single-range 206, full 200, threshold gate, multi-range → 416, `If-Range` matching 206, `If-Range` mismatch 412, malformed range 416, `Accept-Ranges: bytes` always present - [ ] 3.4 `src/service-worker/service-worker.test.ts` — additive: SW fetch handler emits 206 for ranged request above threshold; falls through to full body below threshold; `activate` invokes `cleanupOutdatedCaches`; legacy URL pattern still resolves when configured - [ ] 3.5 `src/keyring/keyring.test.ts` — `emptyKeyring` rejects every keyId with `KEY_UNAVAILABLE`; `MemoryKeyring` returns ≥3 fixture keys; unknown keyId rejected - [ ] 3.6 `src/encrypted-archive/encrypted-archive.test.ts` — round-trip AES-GCM encrypt/decrypt; magic-prefix detection; tampered ciphertext rejected; missing-key path throws `KEY_UNAVAILABLE` - [ ] 3.7 `src/loader/loader.test.ts` — additive: loader detects encrypted prefix, decrypts via keyring, then unpacks; missing-key surfaces `KEY_UNAVAILABLE`; tampered ciphertext surfaces `LOAD_FAILED` - [ ] 3.8 `src/content-address/content-address.test.ts` — `computeArchiveContentHash` deterministic for same entries; differs across format-version byte; `buildContentAddressedUrl` produces `/packed/.bundle.asset`; legacy form preserved when flag off; `cleanupOutdatedCaches` deletes everything not in keep list - [ ] 3.9 `src/error-codes.test.ts` — additive: `KEY_UNAVAILABLE` present + reachable from production - [ ] 3.10 Every `it(...)` carries `{ timeout: 5000 }` (30000 for genuinely async) ## 4. Documentation - [ ] 4.1 Sibling `.md` for every new `.ts` (≥200 words; basic + advanced examples) - [ ] 4.2 Update `src/loader/loader.md`, `src/manager/manager.md`, `src/service-worker/service-worker.md`, `src/error-codes.md`, `src/i18n/messages.md` to describe Cluster B additions - [ ] 4.3 Update `README.md` if Quick Start surface changed (new exports listed) ## 5. Pre-archive verification (the 5-gate triple) - [ ] 5.1 `bun run check` — Biome 0/0/0 - [ ] 5.2 `bunx turbo lint typecheck test --filter=@suaveplan/packer-browser` — green - [ ] 5.3 `bunx turbo test:coverage --filter=@suaveplan/packer-browser` — 100/100/100/100 - [ ] 5.4 `bun scripts/openspec-conformance.ts @suaveplan/packer-browser` — exit 0 - [ ] 5.5 `bunx turbo build --filter=@suaveplan/packer-browser` — green ## 6. Pre-archive 9-row checklist - [ ] 6.1 Generics threaded; `expectTypeOf` assertions on every literal-type claim - [ ] 6.2 Zod schemas wired at every declared boundary (encrypted archive header) - [ ] 6.3 Every error code in `error-codes.ts` thrown by ≥1 production path (incl. new `KEY_UNAVAILABLE`) - [ ] 6.4 Strict-mode behaviors tested both on and off where applicable - [ ] 6.5 OTel instruments emitted from production paths (existing four still wired) - [ ] 6.6 Lifecycle dedup / in-flight Promise behaviour tested where claimed - [ ] 6.7 AbortSignal honoured at every yield in streaming path - [ ] 6.8 Tier purity preserved (browser-tier; no Node imports outside tests) - [ ] 6.9 No retroactive tick — every box reflects work done in this change ## 7. Archive - [ ] 7.1 Fold `specs/packer-browser/spec.md` into `openspec/specs/packer-browser/spec.md` (append PACKER-BROWSER-13..16, preserve 1..12) - [ ] 7.2 Move `openspec/changes/enhance-packer-browser-streaming/` → `openspec/archive/-enhance-packer-browser-streaming/` - [ ] 7.3 Refresh `MANIFEST.md` via `bun scripts/folder-reorg/generate-manifest.ts`