5.7 KiB
OpenSpec Change: serialize-spec-conformance
- Change ID: serialize-spec-conformance
- Package:
@suaveplan/serialize - Status: PROPOSED
- Date: 2026-04-26
- Reference:
openspec/specs/serialization/spec.md,openspec/archive/2026-04-19-enterprise-serialize/
Summary
Annotate every requirement in openspec/specs/serialization/spec.md with a **Verified by:** line, pin previously-implicit scenarios with explicit tests, close the SZ-10 (wire-format + options validation) and SZ-11 (OTel instrumentation) gaps in scope, thread <T = unknown> generics through the public deserialize entry points, and rectify the historical "retroactively ticked" hazard in 2026-04-19-enterprise-serialize/tasks.md so bun scripts/openspec-conformance.ts @suaveplan/serialize exits 0.
Current state
@suaveplan/serialize ships a complete tagged-JSON codec, MessagePack profile, Web Streams encoder/decoder, serialize-javascript /unsafe adapter, Zod-validated TypeDefinition registry, structured error hierarchy, and 100/100/100/100 coverage. The conformance gaps are documentary plus three previously-implicit scenarios that no test pinned explicitly:
- 12/12 requirements lack
**Verified by:**lines. - SZ-02 typed-array round-trip — only
Uint8Arrayis tested through the publicserialize → deserializeround-trip; the other seven typed arrays (Int8Array,Int16Array,Int32Array,Uint16Array,Uint32Array,Float32Array,Float64Array) only exercise the registry directly. - SZ-03 envelope shape — the
{ "__t": "<tag>", "v": <encoded> }wire format is implicit in encode tests but not asserted as a structural contract. - SZ-06 MessagePack bit-identical output — "structurally equivalent" is asserted, but the spec MUST is bit-identical bytes for the same input.
archive/2026-04-19-enterprise-serialize/tasks.mdcarriesretroactively ticked.
Non-goals (deferred to follow-up changes)
None. Two implementation gaps were originally deferred here and have since been folded back into this change at the user's request:
- SZ-10 schema validation at the public boundary — closed in scope. New
WireNodeSchema+TaggedEnvelopeSchema+validateWireNodedefenddeserialize/decodeBinaryagainst malformed inputs (throwsMalformedWireError, codeSERIALIZE.CODEC.MALFORMED_WIRE); the existingSerializeOptionsSchema/DeserializeOptionsSchemaare now.safeParse()'d at every public entry point (throwsInvalidOptionsError, codeSERIALIZE.OPTIONS.INVALID). - SZ-11 OTel instrumentation — closed in scope. New
src/observability/metrics.tsexports a lazygetSerializeMetrics()singleton built frommetrics.getMeter('suaveplan.serialize'). Six instruments wired into all four public entry points:serialize.encode.bytes,serialize.decode.bytes,serialize.payload_bytes,serialize.encode.duration_ms,serialize.decode.duration_ms(histograms) andserialize.error.total(counter,code+formatlabels). Verified by tests that wire a real, in-memoryMeterProvider(notvi.mock).
A third refinement was added in the same pass: the public deserialize, decodeBinary, legacyDeserialize, and deserializeEvalSafe entry points now accept an optional <T = unknown> generic so callers receive T instead of unknown at the call site (type-only change, runtime unchanged).
What changes
- Add
**Verified by:**line under every### SZ-NNblock inopenspec/specs/serialization/spec.md. - Add
src/codec/json/json-encode.spec-conformance.test.tscovering:- SZ-02:
Int8Array,Int16Array,Int32Array,Uint16Array,Uint32Array,Float32Array,Float64Arrayend-to-endserialize → deserializeround-trip. - SZ-03: explicit assertion that a
Datevalue encodes to{ __t: "__Date", v: <iso-string> }(envelope shape contract).
- SZ-02:
- Add
src/codec/msgpack/msgpack-encode.spec-conformance.test.tscovering:- SZ-06:
encodeBinary(x)produces byte-identical output across two calls for the same structured input (Map keys insertion-order varied).
- SZ-06:
- SZ-10: add
src/codec/wire/wire.schemas.ts(WireNodeSchema,TaggedEnvelopeSchema,validateWireNode); addMalformedWireErrorandInvalidOptionsErrortosrc/errors/serialize-error.ts; tightensrc/codec/json/json.schemas.tsto.strict()with duck-typedsignal; wire schema validation into all four public entry points; addsrc/codec/wire/wire.schemas.test.tspinning the rejection set. - SZ-11: add
src/observability/metrics.tsexposinggetSerializeMetrics()+resetSerializeMetrics()with lazymetrics.getMeter('suaveplan.serialize'); emitencode.bytes/decode.bytes/payload_bytes/encode.duration_ms/decode.duration_mshistograms anderror.totalcounter from every public entry point; addsrc/observability/metrics.test.tsusing a real in-memoryMeterProviderto assert emission. - Generic threading: thread
<T = unknown>throughdeserialize,decodeBinary,legacyDeserialize,deserializeEvalSafewithexpectTypeOfpins.
Impact
- Public API gains:
MalformedWireError,InvalidOptionsError,WireNodeSchema,TaggedEnvelopeSchema,validateWireNode,getSerializeMetrics,resetSerializeMetrics,SerializeMetricstype. New error codesSERIALIZE.CODEC.MALFORMED_WIREandSERIALIZE.OPTIONS.INVALID. - New tests across three files; new
src/codec/wire/andsrc/observability/directories with co-located.mddocs. openspec/specs/serialization/spec.mdgains 12**Verified by:**lines (SZ-10 / SZ-11 now reference real test IDs, no longer say "follow-up change").- Coverage stays at 100/100/100/100.
- The
*-spec-conformancechange folder name auto-rectifies the historical retroactive-ticking flag in the conformance script.