Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,226 @@
# 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