ctrlrun.canonical_bytes — function, defined at src/ctrlrun/action.py:201
v0.1 §2.3, promoted rather than written (SPEC-v0.6 §6.2,
§9.1). canonicalize(action) builds a fixed six-key payload and calls this, so there is
provably one implementation rather than two that agree today: the receipt chain (§6.2) and
the policy hash (§7.1) both need the canonical form of a document, and the alternative was
a private second canonicalizer, which §6.2 forbids by name.
Sorted keys recursively, separators=(",", ":"), ensure_ascii=False, UTF-8, and float
rejected at any depth. The rejection is not json.dumps’s doing — allow_nan=False only
catches NaN and the infinities — so it is checked here, because a canonicalizer that
silently encoded 0.1 would make two hosts with different libm disagree about a hash.
ctrlrun.action/v1 is unchanged by this promotion and T164b is the corpus that proves it.