mesh §3.6: the receipt sealed the model by name, so the name was not a binding #246

Merged
dcharlot merged 2 commits from dcharlot/bind-weights-and-precision into main 2026-10-14 09:10:32 -04:00 AGit

2026-10-14

David Charlot
mesh: the identity rides the whole path, and the keys it adds are exactly two
All checks were successful
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 2m34s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 1m21s
standards workspaces build + test / aac-rs (pull_request) Successful in 2m37s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 55s
standards workspaces build + test / map-rs (pull_request) Successful in 56s
standards workspaces build + test / mesh-rs (pull_request) Successful in 54s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 47s
standards workspaces build + test / openpay (pull_request) Successful in 3m8s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 4m43s
lockfiles / every Cargo.lock is current (pull_request) Successful in 34s
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 5s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
no-vanity-metrics / No new orphan source files (pull_request) Successful in 7s
spec citations resolve / Code cites no section its SPEC lacks (pull_request) Successful in 7s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m15s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m58s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m34s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m11s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m12s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m26s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m24s
tests in CI / every test file is run by a job or listed with its reason (pull_request) Successful in 3s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m56s
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]>
2026-10-14 01:33:23 -04:00
David Charlot
mesh §3.6: the receipt sealed the model by name, so the name was not a binding
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]>
2026-10-14 01:30:11 -04:00