Files
suaveplan/openspec/archive/2026-05-10-enterprise-cbor/tasks.md
2026-05-27 23:29:53 +01:00

227 lines
21 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Tasks — `@suaveplan/cbor` (RFC 8949 codec + RFC 9052 COSE bridge)
> Every checkbox below MUST reflect work actually done in this change. Do not retroactively tick boxes. Do not tick a box for a test that doesn't exist or doesn't pin the requirement it claims to verify.
## 1. Spec authoring
- [ ] 1.1 Read `../suaveplan/enterprise/cbor.md` end-to-end
- [ ] 1.2 Read RFC 8949 (CBOR), RFC 9052 (COSE), RFC 8742 (CBOR Sequences), draft-mcnally-deterministic-cbor (dCBOR) end-to-end
- [ ] 1.3 Read `openspec/AGENTS.md` Sections 13, 14, 14a
- [ ] 1.4 Confirm `proposal.md` covers every public-surface item, every subpath, and every dependency
- [ ] 1.5 Confirm `specs/cbor/spec.md` carries one requirement per logical capability with ≥2 scenarios each
- [ ] 1.6 Confirm every requirement has a `**Verified by:**` line naming concrete `it(...)` titles in concrete test file paths
- [ ] 1.7 Source-mapping table cites every RFC section consumed
## 2. Package scaffold
- [ ] 2.1 Create `packages/vault/cbor/` with `src/{encoder,decoder,streaming,sequences,tags,cose,cwt,cddl,diagnostic,errors,observability,schemas,__benchmarks__}/`
- [ ] 2.2 `package.json` — `@suaveplan/cbor@0.0.0`, `type: module`, `sideEffects: false`, deps include `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/crypto-curve`, `@suaveplan/crypto-cipher`, `@suaveplan/crypto-hash`, `@opentelemetry/api`, `tslib`, `zod` via catalog refs only (no bare versions)
- [ ] 2.3 `package.json` `exports` map declares `.`, `./encoder`, `./decoder`, `./streaming`, `./sequences`, `./tags`, `./deterministic`, `./cose`, `./cwt`, `./cddl`, `./diagnostic`, `./errors` (12 subpaths) — each mapped to its built `dist/<feature>/<feature>.js` + `.d.ts` pair
- [ ] 2.4 `tsconfig.lib.json` extends `@suaveplan/typescript-config/library.json`
- [ ] 2.5 `vite.config.ts` with multi-entry build (one entry per subpath) + `vitest.config.ts` calling `createTestConfig()` from `@suaveplan/testing/config`
- [ ] 2.6 `README.md` with package name, description, `bun add` install, quick-start example, Modules table linking every co-located `.md`
## 3. Implementation — encoder
- [ ] 3.1 `src/encoder/encoder.ts` — `Encoder` class + `encode(value, options?): Uint8Array` for major types 0–7 (RFC 8949 §3.1)
- [ ] 3.2 Preferred-serialization shortest-form integer rule (RFC 8949 §4.2.1) — always-on
- [ ] 3.3 `src/encoder/indefinite.ts` — opt-in indefinite-length arrays / maps / byte strings / text strings (RFC 8949 §3.2)
- [ ] 3.4 `src/encoder/half-float.ts` — IEEE-754 binary16 encode (Major 7 / value 25); opt-in via `EncoderOptions.preferFloat16`
- [ ] 3.5 `src/encoder/deterministic.ts` — RFC 8949 §4.2 deterministic profile (sorted map keys via lexicographic comparison over canonical encoded key bytes; shortest-form ints; no indefinite-length; no half-float)
- [ ] 3.6 `src/encoder/dcbor.ts` — dCBOR profile under `./deterministic` subpath; reduction rules; rejects `undefined`
- [ ] 3.7 `src/encoder/encoder.types.ts` — `EncoderOptions`, `IndefiniteLengthMarker`, related types
## 4. Implementation — decoder
- [ ] 4.1 `src/decoder/decoder.ts` — `Decoder` class + `decode(bytes, options?): unknown` for every major type
- [ ] 4.2 `src/decoder/well-formed.ts` — `isWellFormed(bytes)` per RFC 8949 §5.1; trailing-byte rejection in strict mode
- [ ] 4.3 `src/decoder/half-float.ts` — binary16 decode (always available)
- [ ] 4.4 `src/decoder/decoder.types.ts` — `DecoderOptions`, `Tagged<T>`, `DecoderMode`
- [ ] 4.5 Lax-mode opt-in (`mode: "lax"`) per RFC 8949 §5.2 basic validity only
## 5. Implementation — streaming pull-decoder
- [ ] 5.1 `src/streaming/pull-decoder.ts` — `PullDecoder` over `Uint8Array` and `AsyncIterable<Uint8Array>` / `ReadableStream<Uint8Array>` exposing `next(): Promise<IteratorResult<unknown>>`
- [ ] 5.2 Bounded memory — heap delta under fixed cap regardless of stream size; verified by test
- [ ] 5.3 `AbortSignal` honored — `next()` rejects with `CBOR.MALFORMED` when signal aborts mid-item
## 6. Implementation — sequences (RFC 8742)
- [ ] 6.1 `src/sequences/sequences.ts` — `encodeSequence(items, options?): Uint8Array`, `decodeSequence(bytes, options?): unknown[]`, `decodeSequenceStream(stream, options?): AsyncIterable<unknown>`
- [ ] 6.2 Empty-sequence handling (returns empty `Uint8Array` / empty array)
- [ ] 6.3 Strict-mode rejects partial trailing bytes; lax-mode tolerates and stops
## 7. Implementation — tag registry
- [ ] 7.1 `src/tags/registry.ts` — `TagRegistry` with `register`/`unregister`/`lookup`; double-register throws
- [ ] 7.2 `src/tags/tagged.ts` — `Tagged<T>` value object
- [ ] 7.3 Built-in tag handlers for 0 (date/time), 1 (epoch), 2 (positive bignum), 3 (negative bignum), 21 (base64url), 22 (base64), 23 (base16), 24 (encoded CBOR — re-enters decoder), 32 (URI), 35 (regex), 55799 (self-describe)
- [ ] 7.4 Unknown tag in strict mode returns `Tagged{ tag, value }` rather than throwing (RFC 8949 §3.4 — unknown tags MUST NOT cause decode failure)
## 8. Implementation — COSE bridge (`./cose` subpath)
- [ ] 8.1 `src/cose/cose-key.ts` — `decodeCoseKey(bytes): CoseKey` returning `OkpKey | Ec2Key | RsaKey | SymmetricKey` per RFC 9052 §7
- [ ] 8.2 `src/cose/jwk.ts` — `coseKeyToJwk(key): JsonWebKey` and `jwkToCoseKey(jwk): CoseKey` round-trip
- [ ] 8.3 `src/cose/alg-map.ts` — `coseAlgToCryptoAlg(alg)` mapping IANA COSE alg labels to `@suaveplan/crypto-curve` algorithm IDs (-7 ES256, -8 EdDSA, -35 ES384, -36 ES512, -257 RS256, …)
- [ ] 8.4 `src/cose/cose.types.ts` — `CoseKey` discriminated union by `kty`; `CoseAlg`, `CoseKty`, `CoseCurve`, `CoseKeyOp` literal unions
- [ ] 8.5 `src/cose/headers.ts` — typed `CoseHeader` map + label registry (RFC 9052 §3.1: `alg` 1, `crit` 2, `content type` 3, `kid` 4, `IV` 5, `Partial IV` 6, `counter signature` 7); rejects malformed with `COSE_HEADER_INVALID`
- [ ] 8.6 `src/cose/sig-structure.ts` — `buildSigStructure(context, protectedHeader, externalAad, payload)` per RFC 9052 §4.4 covering all five contexts (`Signature`, `Signature1`, `CounterSignature`, `MAC`, `MAC0`); deterministic-encoded
- [ ] 8.7 `src/cose/sign1.ts` — `encodeCoseSign1` / `decodeCoseSign1` / `verifyCoseSign1` (RFC 9052 §4.2, CBOR tag 18); wires `@suaveplan/crypto-curve` ECDSA / EdDSA verifiers
- [ ] 8.8 `src/cose/sign.ts` — `encodeCoseSign` / `decodeCoseSign` (RFC 9052 §4.1, CBOR tag 98); per-signer header carriage; `verifyCoseSign(msg, key)` returns matching signer index
- [ ] 8.9 `src/cose/encrypt0.ts` — `encodeCoseEncrypt0` / `decodeCoseEncrypt0` / `decryptCoseEncrypt0` (RFC 9052 §5.2, CBOR tag 16); wires AES-GCM / ChaCha20-Poly1305 via `@suaveplan/crypto-cipher`
- [ ] 8.10 `src/cose/encrypt.ts` — `encodeCoseEncrypt` / `decodeCoseEncrypt` (RFC 9052 §5.1, CBOR tag 96); per-recipient KEK lookup
- [ ] 8.11 `src/cose/mac0.ts` — `encodeCoseMac0` / `decodeCoseMac0` / `verifyCoseMac0` (RFC 9052 §6.2, CBOR tag 17); wires HMAC-SHA-{256,384,512} via `@suaveplan/crypto-hash`
- [ ] 8.12 `src/cose/mac.ts` — `encodeCoseMac` / `decodeCoseMac` (RFC 9052 §6.1, CBOR tag 97); multi-recipient MAC
## 8a. Implementation — CWT (`./cwt` subpath, RFC 8392)
- [ ] 8a.1 `src/cwt/claims.ts` — typed `CwtClaims` discriminator; IANA labels 1–7 (`iss` `sub` `aud` `exp` `nbf` `iat` `cti`) + `private[label]` slot
- [ ] 8a.2 `src/cwt/encode.ts` — `encodeCwt(claims, options): Uint8Array` with deterministic profile; `Date` ↔ epoch via tag 1
- [ ] 8a.3 `src/cwt/decode.ts` — `decodeCwt(bytes): CwtClaims`; rejects malformed with `UNEXPECTED_TYPE`
- [ ] 8a.4 `src/cwt/verify.ts` — `verifyCwt(token, key, options): Promise<CwtVerifyResult>` orchestrating `verifyCoseSign1` / `verifyCoseMac0` plus time-window check via injectable `Clock`; throws `CWT_EXPIRED` / `CWT_NOT_YET_VALID` / `CWT_AUDIENCE_MISMATCH` / `CWT_ISSUER_MISMATCH` / `CWT_SIGNATURE_INVALID`
- [ ] 8a.5 `src/cwt/cwt.types.ts` — `CwtClaims`, `CwtVerifyOptions`, `CwtVerifyResult`, `CwtPrivateClaim`
## 8b. Implementation — CDDL (`./cddl` subpath, RFC 8610)
- [ ] 8b.1 `src/cddl/lexer.ts` — RFC 8610 ABNF token stream (identifiers, literals, control operators, comments, ranges)
- [ ] 8b.2 `src/cddl/parser.ts` — `parseCddl(source): CddlAst` covering every RFC 8610 §3 production; rejects malformed source with `CDDL_PARSE_FAILED` carrying `{ line, column }`
- [ ] 8b.3 `src/cddl/ast.ts` — typed AST: `CddlAst`, `CddlRule`, `CddlType`, `CddlGroup`, `CddlOccurrence`, `CddlChoice`, `CddlControl`, `CddlGenericParam`
- [ ] 8b.4 `src/cddl/built-ins.ts` — RFC 8610 §3.8 prelude (`uint`, `nint`, `int`, `bstr`, `tstr`, `bool`, `nil`, `null`, `undefined`, `tdate`, `time`, `number`, `text`, `bytes`, `float`, `float16`, `float32`, `float64`, `any`)
- [ ] 8b.5 `src/cddl/control-ops.ts` — `.size`, `.bits`, `.regexp`, `.cbor`, `.within`, `.and`, comparison ops, `.default`
- [ ] 8b.6 `src/cddl/validate.ts` — `validateCbor(value, schema, options): CddlValidationResult` walking decoded value against rule by name; emits `{ path, expected, got }` on mismatch; throws `CDDL_VALIDATION_FAILED` / `CDDL_RULE_NOT_FOUND` / `CDDL_UNDEFINED_REFERENCE`
- [ ] 8b.7 `src/cddl/cddl.types.ts` — option / result types, error path discriminator
## 8c. Implementation — partial-item streaming (extends `./streaming`)
- [ ] 8c.1 `src/streaming/chunk-decoder.ts` — `ChunkDecoder` over `AsyncIterable<Uint8Array>` / `ReadableStream<Uint8Array>` emitting `ChunkDecoderEvent` (`string-chunk` / `bytes-chunk` / `array-start` / `array-end` / `map-start` / `map-end` / `tag` / `value` / `break`)
- [ ] 8c.2 Bounded memory regardless of item size — verified by 10 GiB indefinite-byte-string test asserting heap-delta cap
- [ ] 8c.3 AbortSignal honored — emits final `STREAM_INTERRUPTED` error event mid-item
- [ ] 8c.4 `src/streaming/chunk-decoder.types.ts` — `ChunkDecoderEvent` discriminated union; `ChunkDecoderOptions`
## 9. Implementation — diagnostic notation
- [ ] 9.1 `src/diagnostic/diagnostic.ts` — `toDiagnostic(value): string` per RFC 8949 §8; debug-only
- [ ] 9.2 Sibling `.md` explicitly states "never invoke from hot paths"
## 10. Implementation — errors, schemas, observability
- [ ] 10.1 `src/errors/error-codes.ts` — `defineCodes("CBOR", [...])` registry: `MALFORMED`, `UNEXPECTED_TYPE`, `UNKNOWN_TAG_STRICT`, `NESTED_TOO_DEEP`, `LENGTH_OVERFLOW`, `INDEFINITE_NOT_ALLOWED`, `NON_DETERMINISTIC`, `STREAM_INTERRUPTED`, `COSE_SIG_INVALID`, `COSE_AEAD_FAILED`, `COSE_HEADER_INVALID`, `COSE_RECIPIENT_NOT_FOUND`, `CWT_EXPIRED`, `CWT_NOT_YET_VALID`, `CWT_AUDIENCE_MISMATCH`, `CWT_ISSUER_MISMATCH`, `CWT_SIGNATURE_INVALID`, `CDDL_PARSE_FAILED`, `CDDL_VALIDATION_FAILED`, `CDDL_RULE_NOT_FOUND`, `CDDL_UNDEFINED_REFERENCE`; `CborError extends SuaveplanError<CborErrorCode>`
- [ ] 10.2 `src/observability/metrics.ts` — `cbor.encode_duration_ms` + `cbor.decode_duration_ms` histograms; `cbor.tag_total{tag}` + `cbor.error_total{code}` counters
- [ ] 10.3 `src/schemas/schemas.ts` — Zod schemas for `EncoderOptions`, `DecoderOptions`, `TagHandler`, `CoseKey` discriminated union, `Jwk`
- [ ] 10.4 Every code in the registry is thrown by ≥1 production code path; verified by `error-codes.test.ts`
## 11. Implementation — bounded recursion / item count
- [ ] 11.1 Encoder + decoder honor `maxDepth` (default 64); throw `CBOR.NESTED_TOO_DEEP` on overflow
- [ ] 11.2 Decoder honors `maxItems` (default 1_000_000); throws `CBOR.LENGTH_OVERFLOW` when an indefinite-length container or a CBOR Sequence exceeds the cap
- [ ] 11.3 Both caps configurable via `EncoderOptions` / `DecoderOptions`
## 12. Root barrel + subpath barrels
- [ ] 12.1 `src/index.ts` — explicit named re-exports (no `export *`); namespace re-exports allowed for sub-namespaces (e.g. `export * as cose from "./cose/index.js"` is permitted per CLAUDE.md §10)
- [ ] 12.2 Every `<feature>/index.ts` uses explicit named re-exports per AGENTS.md §6a
- [ ] 12.3 `package.json` `exports` map matches the 10 subpaths declared in §2.3
## 13. Tests (runner-neutral imports — `@suaveplan/testing/runner` only)
- [ ] 13.1 `src/encoder/encoder.test.ts` — major types 0–7; preferred-serialization shortest-form
- [ ] 13.2 `src/encoder/encoder.spec-conformance.test.ts` — RFC 8949 Appendix A vectors round-trip
- [ ] 13.3 `src/encoder/indefinite.test.ts` — opt-in emission; rejected under `deterministic: true`
- [ ] 13.4 `src/encoder/half-float.test.ts` — binary16 encode for normal, subnormal, ±0, ±Inf, NaN, boundary values
- [ ] 13.5 `src/encoder/deterministic.test.ts` — sorted keys; shortest-form; no indefinite; throws `NON_DETERMINISTIC` on forced indefinite
- [ ] 13.6 `src/encoder/dcbor.test.ts` — dCBOR reduction rules; rejects `undefined`
- [ ] 13.7 `src/encoder/property.test.ts` — fast-check round-trip on arbitrary structures
- [ ] 13.8 `src/encoder/deterministic-property.test.ts` — fast-check byte-identical output across two runs on shuffled-key maps
- [ ] 13.9 `src/decoder/decoder.test.ts` — every major type decode; `Tagged<T>` for unknown tags
- [ ] 13.10 `src/decoder/decoder.spec-conformance.test.ts` — RFC 8949 §5.1 well-formedness rules; lax vs strict
- [ ] 13.11 `src/decoder/well-formed.test.ts` — `isWellFormed` returns false for every §5.1 failure class
- [ ] 13.12 `src/decoder/half-float.test.ts` — decode parity; subnormals; payload-preserving NaN
- [ ] 13.13 `src/streaming/pull-decoder.test.ts` — one-item-per-`next()`; bounded memory under 1 GiB sequence; AbortSignal honored
- [ ] 13.14 `src/sequences/sequences.test.ts` — RFC 8742 encode + decode; empty sequence; mixed types; partial-trailing-byte handling
- [ ] 13.15 `src/tags/registry.test.ts` — register / lookup / unregister; double-register throws; unknown tag yields `Tagged`
- [ ] 13.16 `src/tags/builtin.test.ts` — every built-in tag (0, 1, 2, 3, 21, 22, 23, 24, 32, 35, 55799) round-trips
- [ ] 13.17 `src/cose/cose-key.test.ts` — RFC 9052 §7 EC2/OKP/RSA/symmetric sample keys decode to typed union
- [ ] 13.18 `src/cose/jwk.test.ts` — ES256/Ed25519/RS256/symmetric round-trip
- [ ] 13.19 `src/cose/alg-map.test.ts` — every supported alg label maps; unknown label throws `UNEXPECTED_TYPE`
- [ ] 13.20 `src/diagnostic/diagnostic.test.ts` — RFC 8949 §8 sample strings produce byte-identical output
- [ ] 13.21 `src/errors/error-codes.test.ts` — every registry code thrown by ≥1 production path; `expectTypeOf` pins the literal union
- [ ] 13.22 `src/observability/metrics.test.ts` — histograms + counters recorded under in-memory `MeterProvider`
- [ ] 13.23 `src/schemas/schemas.test.ts` — every Zod schema rejects malformed options
- [ ] 13.24 `src/index.test.ts` — every documented subpath resolves
- [ ] 13.25 `src/cose/sig-structure.test.ts` — RFC 9052 §4.4 vectors for all five contexts byte-identical
- [ ] 13.26 `src/cose/sign1.test.ts` — RFC 9052 Appendix C.2 ES256 / EdDSA round-trip; tampered sig throws `COSE_SIG_INVALID`; tagged + tagless inputs
- [ ] 13.27 `src/cose/sign.test.ts` — multi-signer encode + decode; verify-any returns matching index
- [ ] 13.28 `src/cose/encrypt0.test.ts` — RFC 9052 Appendix C.3 AES-GCM-256 vector decrypts; tampered → `COSE_AEAD_FAILED`; ChaCha20-Poly1305 round-trip
- [ ] 13.29 `src/cose/encrypt.test.ts` — multi-recipient round-trip; per-recipient KEK lookup
- [ ] 13.30 `src/cose/mac0.test.ts` — HMAC-SHA-{256,384,512} round-trip; tampered MAC → `COSE_SIG_INVALID`
- [ ] 13.31 `src/cose/mac.test.ts` — multi-recipient MAC round-trip
- [ ] 13.32 `src/cose/headers.test.ts` — header label registry round-trip; malformed → `COSE_HEADER_INVALID`
- [ ] 13.33 `src/cwt/claims.test.ts` — every IANA claim label round-trips; `Date` ↔ epoch tag 1
- [ ] 13.34 `src/cwt/encode.test.ts` — deterministic claim-set encoding stable across runs
- [ ] 13.35 `src/cwt/decode.test.ts` — RFC 8392 Appendix A vectors decode to typed claims
- [ ] 13.36 `src/cwt/verify.test.ts` — `COSE_Sign1`-wrapped CWT verify; `CWT_EXPIRED` / `CWT_NOT_YET_VALID` / `CWT_AUDIENCE_MISMATCH` paths
- [ ] 13.37 `src/cddl/lexer.test.ts` — token-stream coverage incl. comments, control ops, ranges
- [ ] 13.38 `src/cddl/parser.test.ts` — every RFC 8610 §3 production; malformed source → `CDDL_PARSE_FAILED` with `{line,column}`
- [ ] 13.39 `src/cddl/validate.test.ts` — RFC 8610 Appendix CWT + COSE_Sign schemas validate against real RFC vectors; mismatch surfaces `{path,expected,got}`
- [ ] 13.40 `src/cddl/built-ins.test.ts` — every prelude type accepts conforming + rejects non-conforming
- [ ] 13.41 `src/cddl/control-ops.test.ts` — every control operator tested both directions
- [ ] 13.42 `src/streaming/chunk-decoder.test.ts` — event sequence for indefinite-length text-string in 3 chunks; bounded heap over 10 GiB synthetic byte-string; AbortSignal → `STREAM_INTERRUPTED`
- [ ] 13.43 Every `it(...)` carries `{ timeout: 5000 }` (or `{ timeout: 30000 }` for the streaming + property tests)
- [ ] 13.44 Runner-neutral imports — `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/vault/cbor/src/` returns zero output
## 14. Documentation
- [ ] 14.1 Sibling `.md` for every implementation `.ts` (≥200 words, basic + advanced examples, full API reference) — `encoder.md`, `indefinite.md`, `half-float.md`, `deterministic.md`, `dcbor.md`, `decoder.md`, `well-formed.md`, `pull-decoder.md`, `chunk-decoder.md`, `sequences.md`, `registry.md`, `tagged.md`, `builtin.md`, `cose-key.md`, `jwk.md`, `alg-map.md`, `headers.md`, `sig-structure.md`, `sign.md`, `sign1.md`, `encrypt.md`, `encrypt0.md`, `mac.md`, `mac0.md`, `claims.md` (cwt), `encode.md` (cwt), `decode.md` (cwt), `verify.md` (cwt), `lexer.md` (cddl), `parser.md` (cddl), `validate.md` (cddl), `built-ins.md` (cddl), `control-ops.md` (cddl), `diagnostic.md`, `error-codes.md`, `metrics.md`, `schemas.md`
- [ ] 14.2 README.md links every co-located `.md` under `## Modules`
- [ ] 14.3 Update `CLAUDE.md` package listing — add the `cbor` paragraph under the existing Vault grouping (after `vault-sync`)
## 15. Benchmarks
- [ ] 15.1 `src/__benchmarks__/encode.bench.ts` — encode 1 KiB COSE_Key, 100 KiB array of small items, 1 MiB nested map
- [ ] 15.2 `src/__benchmarks__/decode.bench.ts` — decode the same payloads
- [ ] 15.3 `src/__benchmarks__/cose-key.bench.ts` — `decodeCoseKey` on EC2/OKP/RSA samples
- [ ] 15.4 README.md publishes baseline mitata numbers
## 16. Pre-archive verification (the 5-gate triple)
- [ ] 16.1 `bun run check` — Biome 0 errors / 0 warnings / 0 info across the monorepo
- [ ] 16.2 `bunx turbo lint typecheck test --filter=@suaveplan/cbor` — green
- [ ] 16.3 `bunx turbo test:coverage --filter=@suaveplan/cbor` — 100/100/100/100
- [ ] 16.4 `bun scripts/openspec-conformance.ts @suaveplan/cbor` — exit 0, no `error` findings
- [ ] 16.5 `bunx turbo build --filter=@suaveplan/cbor` — green
## 17. Pre-archive 7-row checklist (AGENTS.md §13)
- [ ] 17.1 Generics threaded — `Tagged<T>`, `TagHandler<T>`, `PullDecoder` item type; `expectTypeOf` assertions on every literal-type claim (`CborErrorCode`, `CoseKey`, `CoseAlg`)
- [ ] 17.2 Zod schemas wired at every declared boundary (`encode`, `decode`, `decodeCoseKey`, `jwkToCoseKey`, tag registration)
- [ ] 17.3 Every error code in `error-codes.ts` thrown by ≥1 production path
- [ ] 17.4 Strict-mode behaviors tested both on (`mode: "strict"`) and off (`mode: "lax"`)
- [ ] 17.5 OTel instruments emitted from production paths and asserted with a real in-memory `MeterProvider`
- [ ] 17.6 Lifecycle dedup — N/A (codec is stateless beyond the optional `TagRegistry` instance; registry double-register throws and is tested)
- [ ] 17.7 No retroactive tick — every box above reflects work done in this change
## 18. Banned-pattern + barrel-shape + runner-import scans
- [ ] 18.1 `grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only" packages/vault/cbor/src/ --include="*.ts"` — zero output
- [ ] 18.2 Barrel-shape scan per AGENTS.md §13 step 4a — zero output for any `<feature>/index.ts`
- [ ] 18.3 Runner-import scan per AGENTS.md §13 step 4b — zero output
- [ ] 18.4 Missing-`.md` scan — zero output
- [ ] 18.5 Bare-version scan — zero output
- [ ] 18.6 Subpath count matches feature module count (12 subpaths: `.`, `./encoder`, `./decoder`, `./streaming`, `./sequences`, `./tags`, `./deterministic`, `./cose`, `./cwt`, `./cddl`, `./diagnostic`, `./errors`)
- [ ] 18.7 Security scan — no hardcoded secrets
## 19. Root-doc refresh
- [ ] 19.1 `bun scripts/folder-reorg/generate-manifest.ts` — refresh `MANIFEST.md`
- [ ] 19.2 `bun scripts/generate-missing.ts` — refresh `MISSING.yml`
- [ ] 19.3 `STATUS.md` — move `cbor` row to `Completed`
- [ ] 19.4 `SUMMARY.md` — add per-package paragraph
- [ ] 19.5 `bunx markdownlint-cli2 --fix` then a second `bunx markdownlint-cli2` pass with exit 0
- [ ] 19.6 `git status` shows only intentional changes
## 20. Archive
- [ ] 20.1 Fold spec delta into `openspec/specs/cbor/spec.md` (canonical, status ACTIVE)
- [ ] 20.2 Move `openspec/changes/enterprise-cbor/` → `openspec/archive/<yyyy-mm-dd>-enterprise-cbor/`
- [ ] 20.3 Confirm `MANIFEST.md` lists `@suaveplan/cbor` after refresh