open-standards/joulecontract/provenance/joulecontract-diff-v0.7-pack.md
Transaction Science 429dc59a83 joulecontract v0.8: the implementability amendment — round five seals the pack
Round five turned the adversarial method on the published artifacts
themselves: a clean-room auditor, forbidden from reading joulecontract-rs,
attempted to build a conformant validator from SPEC + conformance pack
alone. Verdict: receipt-local yes, composed no. Seventeen findings
(provenance/joulecontract-diff-v0.7-pack.md) — the bundle construction
was recoverable only by brute force, the negative vectors demanded
composed verdicts while omitting their sealed contexts, the key-log
event model showed one variant of two, the spec's literal quorum MUST
failed the golden vector, and RFC 8785 (the canonicalization any honest
implementer reaches for) produces the wrong digest.

SPEC v0.8.0 amends v0.7.0 (archived byte-exact; seven verified anchors):
bundle address = digest of LF-joined sorted member addresses; canonical
JSON defined precisely (float-ness preserved — deliberately not JCS);
DSSE PAE in the spec text; quorum scope stated (receipt-scoped k≥2 at
JR-3, per-obligation at JR-4); obligation matching exact with the frame
synthesis rule; the pack declared the normative wire form; timestamps
integer seconds; the KeyLog Added/Revoked enum on the wire.

Pack expands 13 → 22 vectors: evidence-bundle.json ships every member's
bytes (incl. the transition record), every negative embeds its sealed
context, four new mutation negatives (engagement_undisclosed,
recourse_undeclared, static_only_frame, unterminated_chain), three
context-variant vectors (timestamp_stale, breach_unamended,
verifier_revoked_window), and a JR-4 receipt making per-obligation
quorum visible. The EnB object is now sealed evidence and resolved by
the composed validator. Conformance README rewritten as an
implementer's guide; remaining unvectored behaviors stated explicitly.

Also: joule-code pre-existing clippy warnings fixed (workspace now
zero), binding emits 0.8.0.

76 joulecontract tests + 39 joule-code tests green, clippy zero on both
workspaces. Site: Round 5 card + seven-anchor chain, redeployed.

The round's lesson in the standard's own grammar: the pack was making
composed claims whose evidence was not sealed alongside — it was
running evidence:external. v0.8 seals the pack.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-06-10 16:21:44 -04:00

5.5 KiB
Raw Permalink Blame History

JouleContract round five — the clean-room implementability audit

Target: the published artifacts alone — SPEC v0.7, the conformance pack, the READMEs. The auditor was forbidden from reading joulecontract-rs. Question: can a third party build a conformant validator without reading the reference code? Verdict: half. A conformant receipt-local validator plus the DSSE edge is buildable clean-room (the auditor reproduced the contract's content address by experiment, verified the DSSE envelope from the published test seed, and verified the amendment chain byte-exact). A conformant composed validator — the part that distinguishes the standard — was not buildable: its inputs and one of its MUSTs were resolvable only by reading the code. Seventeen findings (P1–P17); the repair is roughly six normative sentences and a dozen vectors, folded as v0.8 — the implementability amendment.


The two blockers

P1 — the evidence bundle existed only in code. The conformant vector's composed verdict depends on a bundle address whose construction no document states and whose member bytes are published nowhere. The auditor recovered the algorithm by brute force — sixteen guesses to find sha256(LF-joined sorted member addresses) — and discovered along the way that the bundle's fourth member is the contract's ceiling-derivation attestation, which the pack's own note misdescribes (P15). §5.1.3's load-bearing operation, resolution within sealed evidence, was unexercisable against real bytes. Closed in v0.8: the bundle construction is one normative paragraph in §5.1.3, and the pack ships every member's bytes (evidence-bundle.json).

P2 — the negative vectors demanded composed verdicts but omitted the composed inputs. Every negative-*.json carried expected_composed and no sealed_context; the verdicts were literally uncomputable from the files. Closed in v0.8: every negative vector embeds its sealed context.

The spec-vs-vector contradiction

P3 — quorum. §6's literal text ("k ≥ 2 at JR-3+", per-obligation discharge model) fails the golden vector, which reaches JR-3 with one verifier per entry. The implementation's actual rule — receipt-level quorum (≥ 2 distinct party-disjoint verifier entities across the discharge log) at JR-3, per-obligation quorum only at JR-4 — was knowable only from the code. Closed in v0.8: the scope is one sentence in §10, and the pack gains a JR-4 vector so per-obligation grading is visible.

The silent-divergence class (serious)

  • P4 — canonical JSON was named, never defined — and it is not RFC 8785. JCS serializes 3.0 as 3; this standard's canonical form keeps the float's decimal point. An honest implementer reaching for the obvious RFC produced the wrong digest. Closed: §7 defines the rules (UTF-8, code-point-sorted keys, no whitespace, shortest-round-trip numbers with float-ness preserved, explicitly NOT JCS), and the pack gains a canonicalization vector with the trap cases.
  • P5 — three wire shapes, none declared normative (§5.1's field list vs Appendix A vs the vectors). Closed: the vector pack is declared the normative wire form; Appendix A's field names aligned; the §5.1↔wire mapping stated.
  • P6 — obligation matching (exact (kind, text) equality; the frame array collapses to one obligation "frame: " + join("+")) was vector-archaeology. Closed: one sentence in §5.1.
  • P7 — demotion order/multiplicity (ordered multiset, one entry per affected discharge, snake_case wire tokens vs §15 display tokens). Closed: one sentence in the pack README.
  • P8 — the gate logic was derivable only by joint inference across seven vectors. Closed: the JR predicate summary in §10 plus the new vectors pin the untested boundaries.
  • P9 — the KeyLog event model (only Added was observable; revocation's two-timestamp shape was unrecoverable). Closed: §6.4 documents the event enum; a verifier_revoked_window vector exercises it.
  • P10/P11 — the amendment record and transition record had no wire form. Closed for the transition: its bytes ship in the pack. Named for the amendment: the contract-amendment wire object remains future work, stated rather than implied.
  • P12 — the missing-vector census: 7 of ~22 demoted states had vectors. Closed: the pack grows to cover the cheap single-mutation states (stale/unanchored timestamps, revoked window, undisclosed engagement, undeclared recourse, static-only frame, open breach, unterminated chain) plus the JR-4 receipt; composition/ratchet/re-anchoring vectors are named future work.

What held

Content addressing reproduced (once the float rule was found); the DSSE envelope verified structurally and cryptographically from the published seed; the MANIFEST checked out completely; the amendment chain verified byte-exact; the seven-obligation derivation and most of the demotion logic were reconstructible. The pack was honest — it was incomplete, and the incompleteness was invisible from inside.

The round's lesson, in the standard's own grammar: the conformance pack made claims (expected_composed) whose evidence (the sealed context, the bundle bytes) was not sealed alongside — the pack itself was running evidence:external against the reference implementation. v0.8 seals the pack.

Prior rounds: joulecontract-diff-v0.3-global.md (semantic), joulecontract-diff-v0.4-self.md (binding), joulecontract-diff-v0.5-self.md (time/party/incentive), joulecontract-diff-v0.6-impl.md (implementation).