Skip to content

v1.108.192 - #377 P3: one immutable snapshot, start to finish

Choose a tag to compare

@jgravelle jgravelle released this 27 Jul 15:48

The two edges @mightydanp pinned adversarially on #377 after reviewing 1.108.183 through .188. Both turned out to be the same defect wearing different clothes: the pipeline read the evidence more than once and assumed the reads agreed.

Fixed: an absence receipt no longer resolves through a mutable key

A receipt has a full snapshot-bound SHA-256 identity. The absence record it linked to did not. The absent:<sha12> key is sha256(tool, repo, query, scope)[:12] with no snapshot in it, so re-running the same query over a different tree overwrites the record at the same key, and finalization followed absence_ref back to whatever sat there at validation time.

Both directions were reachable, and both now have a regression:

  • a receipt minted over a provable scan refused, because a later scan of the same query went stale — destroyed proof;
  • a receipt minted over a stale scan attested, because a later scan was clean — borrowed proof, and the dangerous one.

The producer now freezes a deep copy of the scan into the envelope, and validation runs the refusal rules over that. absence_refusal remains the only implementation of those rules; what changed is the input it is handed, not the rules. Receipts minted before this release fall back to the legacy lookup rather than becoming uncitable.

Fixed: validation, rendering and hashing consume one resolution

_validate_evidence resolved each envelope, then _render_receipt_detail performed its own receipts.lookup. The store is bounded and LRU, so a receipt evicted between the two produced a body with no receipt block, and a sha256 computed over that body, while the attestation said the evidence was proved. The rendered artifact and its own receipt disagreed.

render_handoff now takes the map validation already built. The regression asserts on the seam as well as the symptom, so it holds if the eviction policy changes.

Changed

This is a published-schema change and is stated as one. schemas/evidence-receipt.schema.json sets additionalProperties: false, so absence_record had to be added there. Additive and optional: a receipt minted before this release validates unchanged, and the schema string stays jcodemunch.evidence/v1 because nothing required changed and no existing field changed meaning.

One shipped behaviour changed. Editing the live _absences record can no longer re-judge a receipt already minted from it. A v1.108.183 test expressed "this scan became unciteable" by mutating that record after the mint; that technique was never valid, and its invalidity is precisely the fix, since an in-place edit is indistinguishable from a different scan overwriting the same key. The test's actual claim is unchanged and now asserted at mint time, and a new test pins the changed semantics directly.

This does not settle whether a receipt should expire because the tree moved on after minting. That is a different axis, it is the expiry taxonomy, and it remains open under Phase 2 P3.

Tests

6189 passed, 7 skipped. New tests/test_v1_108_192.py (10). Every fix verified non-vacuous by reverting it: 3 failures on the absence half, 2 on the resolution half. No tool-count or INDEX_VERSION change.