Response normalization for the cross-backend differential.
The differential (describe_differential_conformance_tests) sends one
conformance case to two backends, each over its own database with its own
keeper, so every id, timestamp and token differs between the two answers
even when the backends agree. normalize_differential_response maps a
response onto a canonical {status, body, header_names} that keeps what a
client can rely on and drops what is fresh per run:
- Positional placeholders. Each volatile value is replaced by its kind
and the order it was first seen in that response: the first UUID becomes
<uuid:1>, the next distinct one <uuid:2>, and a repeat of the first is
<uuid:1> again. So identity relations within one response survive (the
account_id of a listed row equals the caller's), while the values
themselves may differ across backends. The kinds, each matched anywhere
inside a string, are VOLATILE_VALUE_PATTERNS: API tokens, API token
ids (tok_…), lowercase UUIDs, second-precision UTC timestamps
(ISO8601_SECONDS, the canonical wire shape) and bare 64-character
lowercase hex digests (random session ids). Only the canonical spelling
of each is placeholdered — an uppercase UUID or a millisecond timestamp
stays raw and shows up as a difference, since a format divergence is a
wire divergence. - Content addresses are compared verbatim. A
blake3:-prefixed hash
names bytes, and equal bytes hash equal on both spines, so it is left as
is: a differing fact hash is a real divergence. A fact whose bytes embed a
volatile value differs by design, which is a ledger row's job. - Object key order doesn't matter. Keys are sorted, and the walk that
numbers placeholders visits them in that order, so two bodies whose keys
differ only in order normalize the same.
- Array order does. Arrays keep their order — a list's order is part of
its contract.
- Header names, not values.
header_names is the sorted set of response
header names minus VOLATILE_HEADER_NAMES: framing and per-request
headers (date, content-length, transfer-encoding, connection,
keep-alive) that each HTTP server sets its own way. Values are not
compared at all — set-cookie carries a fresh credential on every login,
and the header values that are contracts are pinned per backend by the
conformance table's expect.headers and its no-fingerprint floor. A
header one backend sends and the other doesn't is still a difference.
diff_differential_responses lists every place two normalized responses
differ, one entry per leaf path, which is what a known-divergence ledger
entry names.
Placeholders are positional, not tied to fixture identity: they keep which
values repeat within a response, not which fixture entity each one is, so
two ids that appear once each and trade places normalize equal.
Placeholder numbering is per response and per kind. Object keys are
normalized too, but sorted by their raw text, so a record keyed by ids would
order its entries by values that differ across backends — no conformance
response is shaped that way today.