mesh §3.6: the receipt sealed the model by name, so the name was not a binding #246
Loading…
Reference in a new issue
No description provided.
Delete branch "dcharlot/bind-weights-and-precision"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
SPEC §3.6 asks a decision receipt to bind the weight digest and the precision; the mesh/1 wire had no field for either, so no conforming implementation could. What was sealed was a caller-supplied recipe String - re-point that name at other weights and every past receipt still verifies while describing a decode some other model produced. ModelRecipe/RecipeComponent existed for this and were dead (3 refs, all their own definitions). Adds weights_digest (a ContentId, so a non-digest cannot be reported and the validator I wrote for it was deleted as unreachable) and precision (a String - open vocabulary), both OPTIONAL and skipped when absent, so the published pack's bytes do not move. ServeOutcome loses Copy; measured as free, with the real cost appearing only at the receipt read - three hoists and one clone. Tests falsified three ways, each catching a different assertion, including wire-keeps-it/core-drops-it. Details in the two commit messages.
SPEC §3.6 asks for this in as many words: "A receipt for a decision SHOULD therefore bind the weight digest and the precision ... a receipt that omits precision does not reproduce." The `mesh/1` wire had no field for either, so no conforming implementation could satisfy it. What the receipt sealed was obj.insert("recipe".into(), json!(self.recipe)); a caller-supplied `String`. Re-point that name at different weights, a different quantization or a different revision and every receipt sealed under it still verifies, while describing a decode some other model produced. The reproducibility §3.6 claims for a decision — one forward pass, no sampling, therefore checkable by re-execution — rested on nothing. `ModelRecipe` and `RecipeComponent` were already in mesh-schema for this, with components, references and merge weights. Three references in the whole tree, all three their own definitions: dead since they were written. So `weights_digest` and `precision` are sealed into the signed core, both OPTIONAL, both skipped when absent. Absence is the tag: it says the receipt binds the model by name alone, which is the weaker basis, and lets a verifier tell the two apart instead of assuming. `canonical_cbor` orders keys itself, so a receipt that reports neither seals to the bytes it sealed to before these fields existed — the published pack's vectors verify unchanged, which the pack test confirms. `weights_digest` is a `ContentId`, not a hex `String`. The field's presence is a claim to re-executability, so a 32-byte address makes that claim structural: a digest that is not one cannot be reported, and the validator I first wrote for it was deleted as unreachable. `precision` is a `String` because the vocabulary is open and this standard does not own it — an enum would refuse a precision that really ran, and a catch-all variant records less than the name. `ServeOutcome` loses `Copy`, which `precision` made impossible. Measured rather than assumed: removing the derive produced no error anywhere in the workspace beyond the new fields themselves. The real cost appeared only once the receipt read from the outcome — three reads hoisted above the move, and one clone where the receipt and the response both want it. The reference engine reports neither and says why: it answers from a table, so it has no weights to address. `reporting_model_identity` makes the fields exercisable end to end without changing the vectors, mirroring `reporting_delivered_fidelity`. The gguf engine also reports neither, with the honest reason — its weights are a real file, so a digest is obtainable once at load, and until it does that absent beats something cheaper that would look like a binding. The test asserts the third property, which is the one that matters: a receipt differing only in its identity seals to different bytes. Without that, fields could reach the wire and never reach what the signature covers — present to a reader, absent from the seal, which is the exact shape of a binding that is not one. A different digest gives a different core again. Co-Authored-By: Claude Opus 5 <[email protected]>The receipt-level test pins that the identity changes the sealed bytes. This one pins the path: two meshes differing only in whether the engine reports what ran, same recipe name on both, and the envelopes must differ. If the identity were carried but not sealed they would match, and a verifier comparing signatures could not tell a receipt bound to these weights from one bound to any others. It also asserts the added keys are *exactly* `precision` and `weights_digest`, which catches an extra key as well as a missing one — the same assertion shape the delivered-fidelity test uses, and the reason that test is worth copying. Falsified three ways, each caught at a different assertion: - projection removed → the wire assertion fires - digest projected as a constant → the hex assertion fires - wire keeps it, `signed_core_bytes` drops it → the signed-core assertion fires, which is the "carried but not sealed" case the test is named for and the third is the one a weaker test would have missed. No conformance vector is added, deliberately: #183 took the same decision for `delivered_fidelity`. The published pack's value is that its bytes do not move, and `reporting_model_identity` exists so the fields are exercisable without touching it. Co-Authored-By: Claude Opus 5 <[email protected]>