Commit graph

22 commits

Author SHA1 Message Date
212db783a9 audit: bind the scalar statements, and make the accounting identity mean audit
Closes four round-7/8 findings. Certified by the round-12 sweep: five
repositories, both buttons and every self-test, 48/48 GREEN.

── `scalar-statements-unbound` (gpt, round 7, CRITICAL) ────────────────────
The main button bound its 31 certificates' elaborated statements and reachable
specification bodies. This button bound NONE of its thirteen, while
TRUSTED-BASE item 8 said the audit covers "every certificate" — false across
the 44-certificate surface. The finding was raised in round 7, lost from the
round-8 work list by an F-number collision between two reviewers, and re-raised
in round 8.

Proofs/ScalarAudit.lean is generated from each fork's OWN Audit.lean, so the
canonicalisation is provably the same code: pp.all rendering, whitespace
normalisation, transitive specification closure. check-scalar.sh Phase 3c pins
the block's digest, requires the committed copy to match byte-for-byte so a
mismatch can be DIFFED, and cross-checks the auditor's certificate set against
the button's CERTS array.

  dalek ecf3a3f8 · anza 0d942e47 · risc0 4b550a61 · betrusted 4b550a61

risc0 and betrusted share a digest and that is correct, not a collision: their
ScalarSubSpec.lean differs only in doc prose and in `black_box` entries inside
`simp only [...]` lists AFTER `:= by`. Proof scripts. They bind the same
statements over the same specifications, which is the documented scope.

selftest-scalar-statements.sh ships the two attacks the reviewer asked for:

  ok  gutted statement caught (cone unchanged)
  ok  rewritten specification body caught (name and cone unchanged)

The second rewrites a reachable reference body to `id (…)` — DEFINITIONALLY
EQUAL, so the corpus compiles and every proof typechecks and the cone is
byte-identical. Every earlier phase is blind to it.

── `drv-surface-no-cones` + `accounting-certifies-enumeration` (claude) ────
The round-7 accounting identity proved every kernel constant was ENUMERATED.
The reviewer showed enumeration is not audit: their planted claim WAS
enumerated, as DRV|LTLAccAudit.bait.smuggled|theorem with a real cone, and
nothing examined it — rows had no cone, no allowlist covered them, the
statement digest does not reach instruments, and Phase 2b gates DECLARED
AXIOMS, a different question. "Progress of one step, not two."

DRV rows now carry their axiom cone and are pinned in driver-allowlist.txt by
inventory_gate.sh with a DRV tag — the same implementation that pins the
corpus, in both directions, because a second copy of a coverage gate is a
second thing to drift. The axiom policy is per-surface and enforced per
surface: the corpus admits exactly the sanctioned boundary, the instruments
admit none, and an instrument axiom fails EVEN WHEN ALLOWLISTED.

Verified with the reviewer's own payload, both placements:
  before the walk -> UNCLASSIFIED: DRV|…|bait.smuggled|theorem|Classical.choice,Quot.sound,propext
  after  the walk -> ACCOUNTING FAILED names it (kernel-side)

── `drv-naming-heuristic` (claude, round 7) ────────────────────────────────
Retired as load-bearing rather than patched. The rule admits a theorem whose
name extends a constant declared alongside it, and "breaks in one line" —
declare `def bait`, then `theorem bait.smuggled` walks through. It stays as a
fast readable first check; membership in a committed allowlist is what now
carries the weight, and a new row fails closed whatever it is called.

── what round 11 caught, which was mine ───────────────────────────────────
DRV rows first shipped WITHOUT their originating driver. dalek and anza run
two drivers, each declaring its own `corpus`; keyed on name alone those two
distinct declarations produced one byte-identical row, `sort -u` collapsed
them, and the trailers summed to 37 against 36. The estate had already learned
this on the corpus walk — INV rows carry their module because two modules both
declare CurveFieldProofs.zero_spec — and I rebuilt the record without it.

Rows now carry their driver, and the gate FAILS CLOSED ON DUPLICATE RECORDS
naming the collision: two declarations sharing one entry means one is covered
by the other's, which is exactly how a real declaration hides. The trailer
now checks what the drivers EMITTED, not what survives de-duplication —
conflating "the run was truncated" with "two rows were identical" is what let
a record-format defect present itself as an arithmetic complaint.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-03 12:15:26 +02:00
9c78ce88ae P2-b: close classes 9 and 15 — the instruments, and the places nothing looked
CLASS 15 — a Lean file where no phase was looking. The dead-file scan read
Proofs/*.lean and gen/LTLAcc/*.lean and nothing else. A module at the
verification root, or under any other gen/ subdirectory, was neither
compiled nor rejected — while remaining importable by name, since LEAN_PATH
contains both roots. That is a source of the corpus that no phase reads and
no pin covers, which is exactly what the dead-file gate exists to forbid; it
was simply looking in two places instead of everywhere. Now nothing may live
in either root but the two enumerated sets.

CLASS 9 — the instruments' own declaration surface. AxiomCheck.lean and
Inventory.lean perform the audit and are therefore not corpus, so nothing
inventoried what THEY declare. Inventory.lean now walks both: AxiomCheck by
module index, and itself as the module still being elaborated, whose
declarations are the ones the environment reports with no originating
module. That is what makes the inventory cover the instrument that produces
it rather than exempting itself.

The policy is not "declare nothing" — this file legitimately declares its
machinery. It is that an instrument may declare only inert definitions. An
axiom here would widen the trusted base without appearing in any
certificate's cone; a theorem here would be a claim no certificate covers
and no allowlist pins. A flat ban on theorems was WRONG and was measured to
be wrong: defining a function by well-founded recursion makes the elaborator
emit its own obligations, and axiomCone._proof_1 rejected this very file.
The distinction that holds is whether a theorem is a claim someone wrote or
an artefact of a definition declared alongside it — an artefact's name
extends the name of a constant declared with it.

Observed surface: 18 declarations, 16 def and 2 generated obligations, no
axiom, no standalone claim.

The drivers are byte-pinned already, so this does not pin WHICH definitions
they contain — that would add a thing to maintain without adding a thing to
catch. It adds the property byte-pinning cannot give: that no instrument
declares an axiom or a claim, whatever its bytes are.

selftest_audit.sh: 10 cases -> 14. Case 12 uses an INDENTED axiom, because
Phase 1's source grep catches an unindented one and the point is to reach
the kernel-side walk standing behind it.

TWO DEFECTS IN THE TEST HARNESS, found while adding the cases.

· The scratch tree copied verification/ only, but the button also reads
  README.md and STATEMENT-MAP.md from the repository root. check.sh
  therefore ALWAYS died in Phase 3c in the scratch tree, which made every
  `if check.sh; then <attack not caught>` guard unfirable — check.sh could
  not pass in there even with no attack at all. Only the diagnostic greps
  were doing any work. The documents are now copied, and the negative test
  below proves the guard is live: with the driver-surface check disabled,
  check.sh PASSES a tree whose inventory driver declares
  `axiom driver_cheat : False`.
· Case 9 was the last case when it was written and left its rogue gen file
  in place. Harmless then; the new cases inherited it. Cleaned up between
  the blocks rather than inside case 9, so that case still tests what it did.

Also fixed while here: Phase 3b compared the compile manifest against
Inventory.lean by grepping the WHOLE FILE for a backticked module name, so
prose counted — a doc comment naming a module broke the count, and in the
other direction a doc mention of a module missing from the array would have
satisfied the presence check and hidden the omission. It now reads the
arrays.

Both new gates negative-tested by removal. Button green, self-test green.
2026-07-31 14:18:40 +02:00
b575e857e3 verification: bind statements and specification bodies (P1-a); un-stale the fidelity pin
STATEMENT BINDING (Phase 3d). The coverage gate pins every constant's name,
kind and axiom cone, both directions, and none of selftest_audit.sh's nine
attacks defeat it. It is nevertheless blind to what a declaration SAYS — and
that is demonstrated here rather than argued:

  Wrapping one branch of `LTLAcc.pinAccept`'s body in `id (…)` is
  definitionally equal. Every downstream proof still compiles. The name, the
  kind, the type and the axiom cone are unchanged. The inventory gate reports
  "222 constants, environment == allowlist" — GREEN.

That edit is harmless by construction; the point is that nothing stood between
it and a genuinely vacuous redefinition of a specification. Proofs/Inventory.lean
now also emits, for every inventoried constant, its fully-elaborated TYPE, and
for every definition its fully-elaborated BODY — 266 lines over 222 constants.
Proof terms are deliberately absent: by proof irrelevance a theorem's content
is its statement. check.sh Phase 3d binds the SHA-256 and the block is
committed as AUDIT-MANIFEST.txt so a mismatch is DIFFED, not merely reported.

The existing gate is untouched, per the standing rule that the port flows FROM
this repo, not to it: INV lines are byte-identical, inventory_gate.sh is
unchanged, and all nine of its attacks still fail as before.

selftest_statements.sh replays the defeq edit as case 1, asserting BOTH that
the coverage gate passes it and that Phase 3d catches it — so if the coverage
gate ever grows to see this, the test says so instead of quietly re-labelling.
Cases 2-4 cover a hand-edited committed block, a truncated block, and a
constant inventoried without a statement.

FIDELITY PIN (unrelated, found while running the button). Phase 4 had been
failing since 2026-07-23: LIED_PIN_DIV expected 3,867 divergences between the
Lean model and the deployed consistency verifier, and observed 0. Cause is
pacta ddbb5a4, which restored the RFC 9162 2.1.4.2 Step-7 terminal `sn == 0`
condition; that one conjunct removes every divergence in the pinned
73,573-case family. KNOWN-GAPS gap 14 already recorded the closure on the day
it landed — only this constant was stale, so the button had been red for five
days with nobody running it. The pin now reads 0 with the history in a comment.
Nothing about the paper, public log entry 13, or the attested commit 172a1d0
changes; the historical divergence stays reproducible at the tagged pre-fix
commit.

KNOWN-GAPS gap 16 records what the binding does not buy: identity, not
meaning; an author who edits and re-pins in one commit is caught by review and
not by the script; and proof terms are unbound by design.

Button green end to end: ATTESTATION GREEN (Lean + fidelity).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 09:07:13 +02:00
2da0a79981 Review round 4: F1* absorbed (lied-size boundary), acceptCons_sound, kit reproducibility
Round-3 verdicts: GPT-5.6 conditionally approves (blockers closed, one
portability finding); the Claude reviewer's Socratic addendum produced
F1*, the strongest finding of the series — deployed verify_consistency
and mechanized ConsRec are NOT extensionally equal. Reproduced exactly
(witness verify_consistency(1,3,R2,R3,P(2→3))=True vs ConsRec reject;
3,405 divergences n<60; strictly one-sided; power-of-two seeding
mechanism confirmed in source).

- KNOWN-GAPS gap 14: witness, mechanism, one-sidedness, and the
  pinned-pair side condition under which Theorem 3 transfers to the
  deployed verifier (pacta's pin-store flow supplies it by
  construction). No pacta code change; deployed behavior matches
  upstream RFC 9162 implementations.
- fidelity: lied-size family — 73,573 boundary cases, 3,867 expected
  divergences PINNED, one-sided direction asserted per case. Banner
  rescoped: agreement over pinned families, not extensional equality.
- Theorem3.lean: acceptCons_sound (F2) — soundness over the named
  acceptCons predicate, n₀=0 discharged from the non-prefix premise,
  size bound derived from acceptance via new consRec_some_le. Cones
  read from #print axioms; CONES/AxiomCheck/allowlist updated
  (218 → 222 constants, diff = the two theorems + two generated
  auxiliaries).
- F3/GPT§7: verification/lean-toolchain pin + run_bare.sh (reviewer's
  standalone runner, plain public lean — verified green: 61 cones, 222
  constants, gate green) + AENEAS_ENV override in check.sh and
  selftest_audit.sh.
- F4: awk field-equality replaces regex-with-dots in Phase 3b.
- F5: git-tracked .pyc removed (worse than reported — it was in the
  repo, not just the kit); __pycache__ gitignored; round-4 kit ships a
  corpus MANIFEST.sha256 + pinned commit (also GPT's governance
  condition).

check.sh exit 0, ATTESTATION GREEN; selftest exit 0, 9/9 + control.
Live LTL untouched (12 leaves, bcd15f9d…); attestation still gated on
ePrint decision + author review + explicit operator order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 15:07:57 +02:00
9972ab4198 Review round 3: environment-derived audit surface, self-contained kit
Round-2 external reviews (GPT-5.6 + second Claude) converged on the
coverage gate being evadable (H1/NEW-1); GPT additionally proved the
kit's fidelity target could not run (H2) and the namespace-collision
attack that defeats any source-regex fix. This round adopts GPT's
required correction in full:

- Proofs/Inventory.lean: declaration inventory read from the compiled
  Lean environment — every constant of every corpus module, fully
  qualified, unfiltered (compiler auxiliaries and _private mangles
  pinned too), with kind and axiom cone; own cone walker cross-checked
  in-process against core collectAxioms (hard error on divergence).
- verification/inventory-allowlist.txt: all 218 constants pinned.
- inventory_gate.sh: fail-closed diff both directions (UNCLASSIFIED /
  STALE), INV-COUNT truncation guard, exactly-one-axiom invariant.
- check.sh Phase 3b rewritten around the gate + manifest⇔inventory
  drift checks + CONES⇔inventory cone cross-check (two independent
  computations must agree). EXCLUDE table gone (sha256/Bytes are
  ordinary audited entries now).
- selftest_audit.sh: 9 adversarial cases against the production gate
  (attributed/indented/private/instance, namespace collision, smuggled
  axiom, deleted decl, unmanifested Proofs/ and gen/ modules) + positive
  control — all defeated (GPT release condition 2).
- M1: recursive orphan-olean guard (caught a stray dev artifact on its
  first run), gen/ dead-file check, corpus-wide single-axiom pin.
- L1/NEW-2: acceptIncl_sound drops the redundant hm (derived from
  hacc.1); cone unchanged.
- M2/M3: STATEMENT-MAP counts 230,271/230,016; non-vacuity guard
  wording narrowed to what the guards actually certify.
- README layer table: stale L4/pin-store rows fixed (missed by both
  round-2 reviewers AND the round-2 revision — found in self-review).
- KNOWN-GAPS 12 (audit-gate lineage + residual limits), 13 (round-2 kit
  target not self-contained); gap 2 count fixed.
- RESPONSE-TO-REVIEWERS.md: round-3 disposition of every finding.

Kit round 3 additionally ships the complete stdlib-only import closure
of pacta.transparency (content-addressed vs pacta 3d81d53), the
clean-extraction fidelity transcript (exit 0, 230,271+230,016, zero
mismatches), the ATTESTATION GREEN check.sh transcript, and the
self-test transcript.

The live LTL remains untouched (12 leaves, root bcd15f9d…);
attestation stays blocked pending ePrint decision + author review +
explicit operator order.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-12 00:32:18 +02:00
260ad64511 revision round 1: address both external reviews (GPT-5.6 + second Claude)
No theorem was wrong; every fix is spec-surface, audit-mechanism, docs,
or harness coverage. Changes:

LEAN (Claude F1, GPT M4):
- acceptIncl: the consumer's inclusion accept (m<n ∧ Root=some r) is now
  a named object, not just a theorem hypothesis. Root alone accepts
  out-of-range m; acceptIncl pins the guard.
- acceptIncl_complete / acceptIncl_sound: route Thm 1/2 through it.
- extractCons_correct_paper: Thm 3 at the paper's exact quantifiers
  (n₀≤n₁, no separate 0<n₀; n₀=0 discharged since D₀=[]=take 0).

SCRIPT (GPT H1/H2, Claude F3):
- Phase 3b: fail-closed audit-surface COVERAGE — every named decl under
  Proofs/ and gen/ must be in CONES or a documented EXCLUDE (sha256,
  Bytes); anonymous gen instances count-pinned; every CONES key must be
  queried by AxiomCheck (no pin-but-never-check). Tested: an
  unclassified theorem now makes the button exit 1.
- H2: distinct markers — LEAN GREEN always, ATTESTATION GREEN only when
  fidelity actually ran; SKIP/absent-pacta no longer emit the strong
  marker. Attestation gate keys on ATTESTATION GREEN.
- Phase 0: orphan-olean guard (every Proofs/*.olean needs a sibling
  .lean); deleted 6 orphans; untracked all *.olean/.lake from git and
  gitignored them (root cause of the F3 tarball leak).

HARNESS (Claude F1, GPT M3):
- added out-of-range families (m≥n, m>n, n₀>n₁, n₀=0); re-pinned counts
  230,271 / 230,016 (match the reviewer's independent RFC difftest
  exactly); narrowed 'exhaustive' wording to the tested domain.

DOCS: README stale rows fixed (freeze banner no longer contradicts
table); KNOWN-GAPS gap 3 reworded (general Lemma 2 = specializations),
+gaps 9 (cost), 10 (pin init), 11 (acceptIncl resolved); STATEMENT-MAP
+acceptIncl rows, +Lemma-2-general note, +constant-vs-property
clarification for §10(i).

Button: EXIT 0, coverage complete, ATTESTATION GREEN, 230,271/230,016.
56 pinned cones over an ENFORCED surface. LTL untouched (12, bcd15f9d).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 22:53:47 +02:00
a3b8b3ecea S7 drill (2nd pass): pin the load-bearing gen/ instance cones; document audit surface
Audit-of-the-coverage-audit. Its 18 added cone values re-verified against
the observed #print outputs (all match). Methodology blind spots found:
abbrev Bytes (bare alias, no cone content — excluded by nature) and the
two ANONYMOUS gen/ instances, which are silently load-bearing
(DecidableEq Hash powers ConsRec's 'if C = []' and pinAccept's root
compare; Inhabited Hash powers every getD default). Transitivity covered
them, but no hand-waves before external review: cones read and pinned —
instInhabitedHash = [propext], instDecidableEqHash = AXIOM-FREE. The
audit-surface definition is now documented in check.sh itself.

Button verified by exit code: EXIT 0, ALL GREEN, FIDELITY GREEN.
54 pinned cones. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:07:08 +02:00
db45c0e33f S7 Fable re-audit: close the cone-audit COVERAGE gap (52/52), verify button by exit code
Re-derived S7 as Fable, practicing the standing rule (check exit code +
ALL GREEN, not tail). Confirmed committed button genuinely exits 0.

FINDING: the cone audit had a COVERAGE gap — 34 of 52 proven objects
were pinned; 18 (incl. core defs kbelow/hleaf/hnode and the pin-store
defs pinAccept/pinExtract/acceptCons, plus intermediate lemmas) were
never cone-audited. Transitively safe (Phase 1 forbids axiom under
Proofs/, Phase 2 forbids sorry, universally) — but 'transitively
covered' is not good enough for an externally-reviewed corpus. Closed:
every proven theorem/def now has its EXACT cone pinned, read from
#print axioms (not guessed). Coverage now 52/52, empty unaudited list.

Cones of note: hleaf/hnode = [LTLAcc.sha256] only; kbelow and the pure
arithmetic/list helpers = no hash axiom; the def-level objects that
touch MTH carry the single sha256 boundary. No surprise axioms anywhere.

Button verified: EXIT 0, ALL GREEN, FIDELITY GREEN, 164,479/164,224
pinned. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:56:17 +02:00
43f2c40cac S6: Proposition 1 (pin-store safety) — the last theorem, Merkle-layer share
The consumer pin store (§5.4) as a transition predicate; the paper's
Prop 1(1) fully mechanized:
- pinAccept: same-size ⇒ root match; smaller ⇒ reject (rollback); larger
  ⇒ consistency proof verifies. Mirrors sthstore.py.
- pinAccept_monotone: an accepted step never shrinks the pin (definitional).
- pin_prefix_correct: an honest advance where D is NOT the prefix of D'
  makes pinExtract output a genuine collision — same-size routes to
  extractMTH (whole-tree Lemma 2), grow routes to extractCons (Theorem 3).
  Explicit named-extractor form ⇒ non-vacuous (pin_prefix_nonvacuous
  pinned).
- fork_distinct: the Merkle share of Prop 1(2) — different roots at equal
  size commit to different content. EUF-CMA transferable-evidence is
  signature-layer, OUT OF SCOPE and documented in the file header (not
  smuggled).

Cones: single hash axiom (pin_prefix_correct adds Classical.choice via
functional induction downstream). 33 certs green. Fable statement-audit:
matches paper Prop 1(1); Prop 1(2) scope-bounded honestly. LTL untouched.

Every §6 statement is now kernel-checked: Lemma 1, Theorems 1-3,
whole-tree Lemma 2, Proposition 1. Remaining: S7 fidelity, S8 freeze.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:23:49 +02:00
02a45eae36 S5.4: THEOREM 3 COMPLETE — extractCons assembly + non-vacuity witness
The paper's hardest theorem is fully kernel-checked. extractCons joins
the two proven halves: extractConsNode's collision (via consRecBinding,
steps 1-2) or the descent extractMTH D₀ (D₁.take n₀) (step 3, S4).

- extractCons_correct: acceptance ConsRec n₀ |D₁| C ⊤ (MTH D₀) =
  some (MTH D₀, MTH D₁) with D₀ ≠ D₁.take n₀ (and |D₀| = n₀ ≤ |D₁|,
  0 < n₀) ⇒ IsCollision of THIS function's output. Statement matches
  paper Thm 3 verbatim (the n₀ = 0 escape is vacuous there: [] is
  always the real prefix). Compiled on first attempt — the pre-verified
  skeleton held exactly.
- extractCons_nonvacuous (queued requirement honored): on a non-rewrite
  input the output is provably NOT a collision — choice-proof.

Cones: extractCons_correct [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] — single hash axiom, no collision-resistance assumed
anywhere. 29 certs green. Fable statement-audit passed. LTL untouched
(12 leaves, bcd15f9d).

Corpus now holds kernel-checked: Lemma 1, Theorem 1, Theorem 2,
Theorem 3 (+ whole-tree Lemma 2). Remaining: Prop 1 (S6), fidelity
harness (S7), freeze (S8).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 20:07:20 +02:00
f47663b890 S5.3 drill (2nd pass): anchor refactor-equivalence provenance to git history
The previous drill's equivalence theorems were only as strong as their
RHS matching the ACTUAL historical base (not a from-memory
reconstruction) and 'nothing else changed' being true. Both now verified
against the repository itself: git show cfde9b2 confirms the RHS forms
verbatim; git diff cfde9b2..8795e82 confirms the refactor is base-only
(eight lines). Provenance recorded in Refactor.lean's header so the
argument is self-contained: unchanged remainder (git) + equal base
(kernel) => whole-function equality. 26 certs green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:55:38 +02:00
406750887f S5.3 Fable re-audit: machine-verify the ConsRec base refactor (permanent artifact)
Re-derived S5.3 (all done under an Opus switch) from zero. consRecBinding
STATEMENT re-confirmed faithful to paper Thm 3 steps 1-2 (y=MTH D₁ = the
hash-fold condition; some=>collision / none=>x=MTH(D₁.take n₀) = the two
Lemma-2 outcomes); non-vacuous (some-branch is a SPECIFIC-pair IsCollision,
not pigeonhole-provable; none-branch a real equality needing hcons).

FINDING + FIX: Opus changed ConsRec's base definition (list-match →
decidable if) with only 'recompiled clean' as evidence — a definition
that mirrors the deployed verifier. Now machine-checked: consRec_base_
false_eq / consRec_base_true_eq prove the decidable-if base EQUALS the
exact list-match forms it replaced. Kept as PERMANENT cone-audited
theorems (F1 discipline: keep the evidence), not a throwaway probe.

QUEUED for S5.4: extractCons_correct (Theorem 3 endpoint) MUST carry a
permanent non-vacuity witness like extractIncl_nonvacuous/extractMTH_
nonvacuous. S7 must re-confirm the NEW ConsRec base vs Python.

26 certs green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:25:04 +02:00
8795e82865 S5.3: consRecBinding PROVEN — Theorem 3 steps 1-2 kernel-checked (the hardest object)
The single hardest proof in the corpus is complete, no sorry. Under the
value-equality invariant y = MTH D₁, an accepting ConsRec fold either
makes extractConsNode output a genuine collision or its first component
is the honest prefix root MTH(D₁.take n₀).

- ConsRec base changed from list-match to decidable if (if C=[] /
  if C.length=1) — same root-cause fix as Root, avoids WF-unfold
  exhaustiveness obligations; more faithful to the deployed Python.
  Whole chain (Basic..Consistency) rebuilt clean.
- consRecBinding by ConsRec.induct (10 cases): 4 base/singleton, 3
  rejection/none contradictions, 2 recursive (n₀≤k, n₀>k). The n₀>k
  none-branch is where all S5.1 infrastructure interlocks:
  kbelow_prefix_eq (prefix splits at same k) + take_take_le +
  take_drop_prefix assemble x = hnode s xx into MTH(D₁.take n₀). The
  collision branches use append_inj (fixed-width Hash) + MTH_split.
- take_all helper (take-whole-list).

Cone [propext, Classical.choice, LTLAcc.sha256, Quot.sound] — single hash
axiom. 24 certs green. Fable statement-audit: matches paper Thm 3
steps 1-2. LTL untouched (12 leaves, bcd15f9d).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 19:18:26 +02:00
cfde9b2cd7 S5 stage 2: extractConsNode extractor defined (consistency, Theorem 3 steps 1-2)
The consistency collision extractor: walks the ConsRec new-root fold in
parallel with the honest size-n tree of D₁ and returns the concrete
colliding node preimage pair at the first level where the fold's hnode
argument pair diverges from the honest node — or none if the fold is
genuine all the way down (binding holds). Both branches verified faithful
to ConsRec's hnode argument order (n₀≤k: y' left / s right; n₀>k: s left
/ y' right). Termination via kbelow bounds.

Deliberate honest checkpoint: the DEFINITION compiles and is cone-audited
[propext, LTLAcc.sha256, Quot.sound]; the binding CORRECTNESS proof — the
single hardest object in the corpus — is stage 3, kept for a fresh
session rather than a rushed long turn. 22 certs green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:55:51 +02:00
6a7d93ccb7 S5 stage 1: consistency infrastructure (kbelow prefix-split + list surgery)
Theorem 3's binding (steps 1-2) turns on one non-obvious arithmetic fact,
isolated and proven here before the main proof:
- pow2_exp_unique / kbelow_eq_of_pow2_between: kbelow is pinned by its
  three defining inequalities (power-of-two, k<n≤2k), so a prefix that
  spills past the left subtree splits at the SAME point.
- kbelow_prefix_eq: with k=kbelow n, 2≤n, k<n₀≤n ⇒ kbelow n₀ = k (the
  fact the n₀>k recursion branch needs to align MTH(D₁.take n₀) with the
  fold).
- take_take_le, take_drop_prefix: the list-surgery identities relating
  (D.take n₀) to D.take k and (D.drop k).take (n₀-k).
Cones pinned; 21 certs green. Deliberate honest checkpoint — binding +
extractCons assembly is the next stage. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 17:53:43 +02:00
be232507cc S4: descent extractor extractMTH (Theorem 3 step 3 + F2 restoration), non-vacuous
The 'descend' step of the paper's Theorem 3, built extractor-first per
the S3.5 lesson (never a bare '∨ collision'):

- extractMTH (D D'): total function that, given two equal-length leaf
  lists sharing a Merkle root, walks the common-shape tree to the first
  divergence and returns the concrete colliding preimage pair (a node
  pair, or a leaf pair at the bottom).
- extractMTH_correct: |D|=|D'| ∧ D≠D' ∧ MTH D = MTH D' →
  IsCollision (extractMTH D D'). Proven by functional induction on
  extractMTH; composite case uses MTH_split + append_inj (fixed-width
  Hash) to split node preimages or exhibit the node collision.
- extractMTH_nonvacuous: equal lists → output NOT a collision (pinned),
  so the conclusion is false for some inputs ⇒ choice-proof.

This also RESTORES, in explicit non-vacuous form, the receipt-uniqueness
content of Lemma 2 deleted in the S3.5 cleanup (re-audit F2): the honest
Merkle fold is injective up to a collision.

18 certs green. Fable statement-audit passed (matches paper Thm 3 step 3
verbatim). LTL untouched (12 leaves, bcd15f9d).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:46:58 +02:00
67d9cfed43 S3.5 Fable re-audit: permanent non-vacuity witness + honest ledger
Adversarial re-derivation of S3.5 (drill after harness switch). Core
verdict CONFIRMED: extractIncl_correct is faithful and non-vacuous.
Three methodical flaws found and resolved:
- F1: the non-vacuity proof existed only as a deleted probe — evidence
  discarded. Now permanent: extractIncl_nonvacuous proves the
  extractor's output on a NON-forgery input is NOT a collision, so the
  correctness conclusion is false for some inputs and cannot be
  discharged by pigeonhole/choice. Guards against future drift back
  into vacuity. Cone pinned.
- F2 (queued for S4): deleting root_binding discarded the receipt-
  uniqueness content of Lemma 2 (left disjunct: P = Path m D) along
  with its vacuous disjunct. To be restored in extractor form during
  S4; the S4 consistency walk inlines the same argument regardless.
- F3: README still claimed 'root_binding done' — a deleted theorem
  advertised as delivered. Ledger corrected.

15 certs green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 16:38:34 +02:00
e270872a27 S3.5: explicit collision extractor — Theorem 2 made non-vacuous, vacuous forms removed
The S3 Socratic re-audit found incl_sound was kernel-perfect but VACUOUS:
its '... ∨ HasCollision' disjunct (∃ x y, x≠y ∧ sha256 x = sha256 y) is
provable by pigeonhole ALONE (sha256: infinite List UInt8 → finite
32-byte Hash), so the theorem said nothing about forgeries. Even a
data-carrying {p // IsCollision p} disjunct fails (Classical.choice
inhabits it). The only faithful rendering of the paper's 'explicit
algorithm 𝓔' is a NAMED FUNCTION whose correctness is a claim about ITS
OUTPUT.

- extractIncl (m D d P): total function that walks the honest tree and
  returns the concrete colliding preimage pair at the first divergence
  (a node preimage pair, or the leaf preimage pair at the bottom).
- extractIncl_correct: d ≠ D[m] ∧ accepting-receipt →
  IsCollision (extractIncl …).1 (extractIncl …).2. A statement ABOUT the
  fixed function's output; pigeonhole cannot discharge it.
  ADVERSARIAL CHECK (probe, since removed): proved
  ¬ IsCollision (extractIncl 0 [[7]] [7] []) — i.e. on a NON-forgery input
  the output is provably NOT a collision, so the conclusion is genuinely
  false for some inputs ⇒ non-vacuous, choice-proof.
- Removed the vacuous theorems entirely (incl_sound, root_binding,
  hnode/hleaf_inj_or_collision, HasCollision def) so no hollow statement
  survives in a corpus destined for the log. Kept the real building
  blocks (hnode_preimage_inj [propext]; eq_dropLast helper moved to
  Completeness; Binding.lean deleted).

extractIncl_correct cone [propext, Classical.choice, LTLAcc.sha256,
Quot.sound]. THE button green (14 certs). Fable statement-audit passed.
LTL untouched (12 leaves, bcd15f9d).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 15:31:21 +02:00
eb3df7503f S3/L4-L5: root binding (Lemma 2, Path instance) + Theorem 2, constructive
The crux layer — the statement whose HAND proof once carried the frontier
coverage bug is now kernel-checked.

- gen: hash outputs refactored to Hash = {l : List UInt8 // l.length = 32}.
  MECHANIZATION FINDING: the paper's pair-coincidence step ('equal hnode
  values of distinct argument pairs are a collision') is load-bearing on
  FIXED-WIDTH outputs — with unconstrained byte strings x++s = X++Y does
  not split. hnode_preimage_inj (cone: propext) makes this explicit via
  List.append_inj on equal-length components. Queued as a half-sentence
  for the paper's next cycle.
- HasCollision := ∃ x y, x ≠ y ∧ sha256 x = sha256 y — appears ONLY as a
  conclusion, never a hypothesis (no collision-resistance assumed).
- hnode_inj_or_collision / hleaf_inj_or_collision: the per-node dichotomy.
- root_binding: any accepting reconstruction from (v,P) to the honest root
  either IS the honest receipt (leaf hash AND full path P = Path m D — case
  (ii) pinning every consumed sibling) or exhibits a collision. Motive
  quantifies (v,P); induction on Path; k-fold discipline.
- incl_sound (Theorem 2, position binding): accepting a wrong leaf at m
  yields a collision. Cone [propext, Classical.choice, LTLAcc.sha256,
  Quot.sound] — the single hash axiom, pinned in check.sh. ALL GREEN.

Also: Root n=1 branch changed from list-match to decidable 'if P = []'
(well-founded unfolding generated a spurious exhaustiveness obligation);
Root_one_cons added. Fable-5 statement-audit passed. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 13:21:17 +02:00
8cf6153610 S2 Fable-5 re-audit: close the kbelow/RFC-fidelity gap (kbelow_pow2)
Adversarial statement-level re-verification of everything S2 shipped,
against paper SS5.3 and the deployed Python verifier: Path recursion,
Root_left/Root_right fold shapes (none exactly where the deployed code
rejects), incl_complete as Theorem 1 verbatim (getD default unreachable
under m < |D|), MTH([]) = H(epsilon) per RFC. All faithful.

One genuine gap found and closed: the kbelow lemmas bounded k but never
established k is a power of two, leaving 'our split point = the RFC
split point' as by-construction folklore. kbelow_pow2 (cone: propext,
Quot.sound) now pins it: 2^j = k < n <= 2k = 2^(j+1) uniquely
determines the RFC 9162 split. THE button green. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 12:26:04 +02:00
801ae08fe8 L3: Theorem 1 (inclusion completeness) kernel-checked
Path (prover-side inclusion path, paper SS5.3) with termination via the
kbelow bounds; self-contained list lemmas (getD_take, getD_drop - no
stdlib-name dependence); equation lemmas MTH_single/MTH_split/Root_one/
Root_left/Root_right (Option.map form; matcher side conditions closed
explicitly); Theorem 1 by functional induction on Path with a k-fold
discipline against the let-bound split point.

incl_complete cone: [propext, Classical.choice, LTLAcc.sha256,
Quot.sound] - pinned exactly in check.sh alongside Path.
THE button green end to end. LTL untouched.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 12:06:20 +02:00
8d67e9519c L1+L2: hashing shapes, domain separation, MTH/Root/ConsRec with termination
Accumulator pyramid layers 1-2, mechanizing paper SS5.3/SS6 groundwork:
- gen/LTLAcc/HashExternal.lean: the single sanctioned axiom, opaque
  sha256 (no properties assumed - the soundness theorems downstream are
  constructive collision extractors).
- Proofs/Basic.lean: hleaf/hnode (0x00/0x01 domain stamps); Lemma 1
  (domsep) proven AXIOM-FREE; kbelow (largest power of two below n)
  with pos/lt/le-two bound lemmas; MTH, Root (Option = rejection),
  ConsRec (four cases, b-flag, pinned anchor) - all with kernel-checked
  termination via the kbelow bounds.
- check.sh: estate discipline (stub audit, axiom-smuggling gate,
  lean-guard compilation, boundary-exact per-certificate cone audit).
  All green; observed cones pinned exactly.

Zero contact with the live LTL: no appends, no server, accumulator
frozen at 12 leaves throughout this project.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 23:58:00 +02:00