WIP: saw-core-lean — SAWCore→Lean 4 backend - #3214
Draft
septract wants to merge 624 commits into
Draft
Conversation
Contributor
|
It seems to have dumped a lot of lean-only stuff in |
adaptTo is now the single raw/wrapped adaptation chokepoint, total over the calculus's allowed adapters and loud on the forbidden ones: - adaptTo rho t: identity at the same position; raw -> runtime via Pure.pure; non-lambda terms accepted at function position. Wrapping a function, demanding a wrapped value raw without a bind context, or wrapping a motive throws the new ForbiddenAdaptation error (never to be caught and defaulted). - translatedTermAsWrapped is DELETED. All ~20 call sites (bind chains, wrapped-formal tables, equality subjects, recursor case bodies, ArrayValue lifts, top-level def wraps in Term.hs and CryptolModule.hs) now route through adaptTo/adaptToRuntime. adaptWrappedFormal survives as a thin monadic wrapper over adaptTo. - subjectTerm (equality subject emission) is monadic and adapts at the declared EqualitySubjectRep's position. Emitted Lean verified BYTE-IDENTICAL to the Slice 0 baseline across all 151 artifacts - no current row exercises a forbidden adaptation. Conformance exit 0 (188 OK rows); smoketest 54/54. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bindingShapeOfTerm / bindingShapeOfLeanTermM (shape guessed from the emitted Lean AST) are deleted. Shape is an output of translation or a record in Gamma, never re-derived from generated syntax: - Variables read their introduction site's BindingInfo from Gamma (keyed through namedEnvironment; absent keeps the historical raw default). - Module-identifier constants reuse the shape the ident dispatch already computed (translateIdentWithArgsWithShape) instead of re-guessing from the emitted term; imported realizations derive shape from the constant's SAWCore type. - Sites where the old guess was structurally constant (App heads, bare global references) now state BindingRaw explicitly with a note pointing at the Slice 4 convention that will own them. - A shared-let name referenced before its Gamma record is now a loud internal error instead of a silent raw guess. Fence: smoketest 54/54; conformance exit 0 (188 OK); emitted Lean byte-identical to baseline. drivers/arithmetic and drivers/conformance_stream fail with stale goldens, verified PRE-EXISTING by rebuilding at 89a6cef (before this refactor); tracked in TODO.md. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…3 groundwork) The shared/unshared term walk now takes the expected position as an explicit Maybe parameter: translateAt enters translateSharedAt (Just rho); legacy translateTermWithShape/translateTermUnsharedWithShape are Nothing-specializations. The position applies to the current term only - deliberately an argument, not a reader field, so a declared rho can never leak one level too deep into subterms that did not declare one. Case arms consume it family by family as Slice 3 lands (3a value lambdas, 3b index binders, 3c motives, 3d let sharing). Behavior-inert: smoketest 54/54, conformance exit 0, emitted Lean byte-identical to baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
drivers/arithmetic and drivers/conformance_stream goldens predated the 2026-07-03 position-callee-conventions work (55e4fe0, 4294528) and have been failing since. Every diff hunk was verified to be one of the two intended emission changes from that work, nothing else: - unsafeAssert obligations state the Eq universe explicitly: @eq Num x y -> @eq.{1} Num x y (t11/t12). - Stream recursor case handlers own the Pure.pure lift, with motive Except String Nat instead of Nat - error behavior of the scrutinee case is preserved inside the handler rather than wrapped outside. Both rows now exit 0; make conformance exit 0; emitted-Lean baseline snapshot recaptured (166 files). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ExpectFunctionPosition now carries Maybe FunctionConvention (per-binder positions + result position, calculus 'R(FunctionConvention c, Pi) = F(c)'); Nothing remains as the tracked convention-not-yet-declared bridge that Slice 4 shrinks. - translateBinderAt: binder introduction optionally driven by a declared position; Nothing reproduces the legacy flag-driven wrap exactly (translateBinder' is its specialization). A convention demanding a runtime value for a sort binder throws ForbiddenAdaptation. Gamma records the declared position verbatim. - translateLambdaAtConvention + a Lambda arm in translateTermUnsharedWithShapeAt: a lambda entered at a declared convention takes binder wraps and body position from the convention, not from shouldWrapBinder re-derivation. Arity mismatch rejects. - Producers: MkStream index functions and non-dependent UseArgFunction wrapped-helper lambdas compute their convention once (the value-slot predicates are now convention-internal at those sites, per plan Slice 3.4) and push it down. They translate the lambda in place (bypassing the sharing lookup) because the legacy path destructured it inline - preserves byte-parity for shared lambdas. Dependent (typeIxs /= []) and sort-binder lambdas stay legacy until Slice 3b. Implemented by an Opus subagent against the written work order; independently reviewed and re-validated. Fence: smoketest 54/54; conformance exit 0 (188 OK); emitted Lean byte-identical to baseline; position trace confirms the new path live on obligations/mkstream_total (ExpectRaw index binder -> ExpectRuntimeValue result, no inconsistent productions). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Extends the Slice 3a machinery to dependent value lambdas: both producer sites (MkStream index functions and UseArgFunction wrapped- helper lambdas) drop their non-dependent guards, so every lambda they accept translates at a fully-declared FunctionConvention. - Index binders (variable free in later binder types / result type) declare ExpectRaw RawIndexPosition; sort-typed type binders declare ExpectRaw RawTypePosition. - translateBinderAt: a sort-typed binder at declared RawTypePosition allocates its universe under the legacy SortBinderAsType mode when Phase-beta is enabled (reproducing translateBindersSelective's per-binder enterCtx), scoped to that binder's type translation only. - The slot predicates remain convention-internal at the producers. Implemented by an Opus subagent against a written work order; independently reviewed and re-validated. Fence: smoketest 54/54; conformance exit 0 (188 OK); emitted Lean byte-identical to baseline; additionally ~150 driver rows (incl. llvm/salsa20 verification rows) all golden-green under this binary. The dependent positions are correct-by-parity but likely dormant in the current corpus: most helper function formals are non-dependent by construction, and the dependent lambda family mostly flows through the still-legacy generic Lambda case (remaining Slice 3/4 work). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Recursor motives now translate at a declared MotiveConvention instead
of rediscovering their shape through the blanket skipBinderWrap flag:
- MotiveConvention { mcBinderPositions, mcResultMode }: the calculus's
'motive binder positions' and 'motive result position' fields
(doc/2026-07-02, section Recursors / Eq.rec) as data. The result
mode is deliberately not a FunctionConvention result position: a
motive body is a TYPE-level expression, and a value-computing motive
wraps its body type in Except String (wrapExcept), never a
Pure.pure value lift.
- motiveConventionFor derives the convention at the recursor dispatch
from recursorNumIxs and the already-classified RecursorConvention
result mode: index binders declare ExpectRaw RawIndexPosition, the
eliminated scrutinee ExpectRaw StructuralRecursorFieldPosition.
Neither is RawTypePosition, so sort-typed motive binders keep the
surrounding sortBinderMode (unlike value-lambda type-binder slots).
- translateMotiveAtConvention introduces binders via translateBinderAt
at the declared positions and replaces the where-local
translateRecursorMotive; its 'set skipBinderWrap True' site is
deleted. Motive translation is traced/stamped at
ExpectRaw RawMotivePosition.
Fence: smoketest 54/54; conformance exit 0 (188 OK); emitted Lean
byte-identical to the 312-file baseline, including freshly re-emitted
recursor-heavy driver rows (conformance_stream, records,
conformance_record, cryptol_module_stream_fibs,
cryptol_module_rec_ones - all exit 0); position trace live on
conformance_stream (rho=ExpectRaw RawMotivePosition, consistent).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
translateTermLetAt: the let-sharing entry takes the expected position; the shared RHSs still translate at their own natural positions with exact Gamma records (Slices 1-2), and the BODY - whose value the let-chain delivers - now translates at the demanded position. translateTermLetWithShape is the Nothing specialization; translateLambdaAtConvention passes its declared result position. A shared RHS demanded at an incompatible position already fails loudly in adaptTo (ForbiddenAdaptation); emitting separate bindings for genuinely position-polymorphic shares stays future work, to be pinned by a fixture if one ever demands it. Fence: smoketest 54/54; conformance exit 0; emitted Lean byte-identical to baseline; sharing-heavy driver rows re-run under this binary (cryptol_module_dag_sharing green; cryptol_chained_projection_share fails on a PRE-EXISTING stale golden of the known @eq.{1} class - golden dated 2026-07-01, emission byte-identical to the pre-3d baseline; tracked for the golden sweep). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Swept all 18 driver rows whose goldens contained un-universed @eq (candidates for staleness since the 2026-07-03 emission changes). Outcome: - REFRESHED (every hunk verified in a known intended class): conformance_proof_obligations, cryptol_chained_projection_share, llvm_point_verify, offline_lean (t1-t7), offline_lean_e_series (E1-E7) - the @eq.{k} explicit-universe class; plus llvm_popcount_verify and cryptol_running_sum_verify - the checked-access class (atWithDefaultM / saw_throw_error replaced by atWithProof_checkedM with visible h_bounds obligations, from 27d749f/d16870367). All refreshed rows re-run green including Lean elaboration. - NOT refreshed: llvm_chacha20_core_verify. Its current emission does NOT elaborate: the checked-access contract feeds a wrapped shared index (x__... : Except String Nat) raw into the LT.lt bounds proposition and atWithProof_checkedM - CheckedArgRaw performs no adaptation, so a wrapped actual escapes into raw positions. Pre-existing from the 2026-07-03 checked-access work; goldens kept at the last elaborating emission. Recorded in TODO.md as a live specimen for Slice 4: the checked-application convention must bind wrapped index actuals through error-preserving Bind.bind before the proposition/helper consume them, or reject. Conformance exit 0; emitted-Lean baseline recaptured (312 files). Sweep executed by an Opus subagent (classes 1-2) with supervisor verification and class-3 handling. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Completes the golden sweep. The four remaining bounds-overhaul rows (llvm_chacha20_q_verify, llvm_eq_u128_verify, llvm_salsa20_q_verify, offline_lean_popcount32) were refreshed and re-run green INCLUDING Lean elaboration - unlike llvm_chacha20_core_verify, whose emission does not elaborate and which stays red as the Slice 4 specimen. Also records a second pre-existing upstream regression pair, verified failing at pre-refactor commit 89a6cef: cryptol_chacha20_core_iterate and cryptol_chacha20_iround_zero reject with 'Refusing to translate primitive Prelude::Stream@core' (wrapped- scrutinee recursor convention) while their goldens expect successful translation. Tracked in TODO.md for an upstream decision (restore a stream-comprehension translation path or migrate to an expected- rejection category); NOT golden-refreshed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Slices 0-3 landed fully byte-identical, so the behavioral risk the plan spread across Slices 2-4 is concentrated in Slice 4; give it the same sub-slice discipline as Slice 3. 4a fixes the live llvm_chacha20_core_verify specimen (wrapped index at raw checked- application slots) and is the first reviewed-diff fence; 4b covers phase-beta definitions + partial-op unification and the dormant dependent-convention fixtures; 4c retires CalleeTransitional and decomposes the dispatch. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lice 4a) The calculus's callee-convention vocabulary is now data (ArgMode with TypeArg/IndexArg/RuntimeArg/ProofArg/FunctionArg/... and ResultMode), and the checked-application contracts are re-expressed in it: the old three-way CheckedArgRaw bucket splits into its true IndexArg (width, index) and TypeArg (element type) slots, matching the checked helpers' Lean signatures. CheckedApplicationArgMode is deleted. The interpreter (checkedApplicationHelperArgsFor / lowerCheckedApplicationHelperArgs) returns per-actual verdicts: - RuntimeArg adapts through adaptTo ExpectRuntimeValue; TypeArg and RawValueArg adapt at their raw positions (forbidden shapes throw). - IndexArg with a WRAPPED actual - a runtime-computed index - is sequenced through an error-preserving Bind.bind ahead of the bounds obligation, in application order; the bound RAW variable is what both the proposition and the checked helper consume. The wrapped value is never opened and never defaulted. A function-shaped index throws ForbiddenAdaptation. - The contract's declared ResultMode drives the result shape. - A proof-carrying generator bound that is itself a runtime-computed index rejects loudly (no convention for it yet). This fixes the live specimen drivers/llvm_chacha20_core_verify: previously the wrapped shared index escaped raw into LT.lt and atWithProof_checkedM and the emission did not elaborate. It now emits Bind.bind idx (fun v_idx_k => obligation over v_idx_k; helper ... v_idx_k h_bounds_) and is green end-to-end including elaboration; goldens refreshed to the corrected emission (the only row whose emission changed - everything else byte-identical to baseline). New fast fence row obligations/vector_at_runtime_index pins the bind-chain shape (bvToNat of a bitvector sum as the index to at). Fence: smoketest 54/54; conformance exit 0 (189 OK incl. the new row); snapshot byte-identical except the specimen; specimen and fixture green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… step 1) PartialOpWrapped's private two-way arg-mode enum is deleted; its tables now declare calculus ArgModes (bitvector widths IndexArg, value operands RuntimeArg), and the wrapped partial-op lowering shares the checked-application machinery: - lowerProofCarryingActuals generalizes the 4a lowering over the obligation name stem, proof-script builder, proposition, and helper head; lowerCheckedApplicationHelperArgs and buildWrappedProofCarryingApplication are now thin instantiations (h_bounds_/boundsProofScript vs h_nonzero_/partialOpProofScript). - A WRAPPED actual at a width IndexArg slot is sequenced through the error-preserving bind chain instead of escaping raw into the nonzero proposition - the same protection 4a added for vector indices, closing the same latent hazard class for bvUDiv/bvURem/ bvSDiv/bvSRem/ecSDiv/ecSMod before any row trips it. Proof-primitive contracts deliberately NOT relabeled yet: their raw slots are raw-LOGICAL translations (withRawTranslationMode), so an ArgMode relabel has no behavioral content until 4c unifies the tables with the SAWCore signatures in hand. Fence: smoketest 54/54; conformance exit 0 (189 OK incl. the partial_* zero-divisor rows); emitted Lean byte-identical to baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CalleePhaseBetaDefinition analysis: the convention derivation must take supplied type actuals (paramActualAlreadyExpected is the last emitted-AST-inspection class); legacy bind semantics per formal family; migration via a behavior-inert equivalence assert against argumentBindPlan before the swap. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tep 2) Two loud equivalence asserts now run inside 'applied', both inert (the legacy bind plan remains authoritative): 1. phaseBetaArgModesFor derives the CalleePhaseBetaDefinition argument modes from the callee's SAWCore Pi type + supplied actuals; phaseBetaBindFromMode recomputes the bind plan from the modes; any disagreement with argumentBindPlan throws. Silent across smoketest, conformance (189 OK), and the full driver corpus (subagent sweep + re-runs). 2. polymorphicFormalInstantiatedExpectedSrc is the source-based replacement for the emitted-Lean-type instantiation predicate (the translator's last emitted-AST-inspection class), asserted equivalent to it. The first candidate (shouldWrapBinder on the instantiation) was REJECTED BY THE ORACLE on the smoketest: value-domain instantiation (PairValue's a := Vec 8 Bool) is not the same question as wrapped-representation instantiation - those formals bind to raw Lean constructor formals. The corrected candidate (Pi-instantiation only) is silent across smoketest, conformance, and the polymorphic-heavy driver rows (records/tuples/sequences/offline_lean). The emitted-type predicate is quarantined in polymorphicFormalInstantiatedExpected with a note; the swap deletes it once the two-oracle binary has swept the remaining driver rows. Fence: smoketest 54/54; conformance exit 0; emitted Lean byte-identical to baseline. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…it regression The two-oracle driver sweep surfaced three failing rows, all bisect-or-class verified PRE-EXISTING: - cryptol_module_popcount and cryptol_module_salsa20_q: bounds- overhaul golden staleness (atWithDefaultM/saw_throw_error -> h_bounds obligations, from the 2026-07-03 checked-access work). Missed by the earlier @Eq-grep sweep because their goldens contain no bare @eq. Refreshed per-row; both re-run green including elaboration. - sawcore_prelude_auto_emit: write_lean_sawcore_prelude rejects on a function-carrier equality in a prelude lemma (the 55e4fe0 raw-logical slice's pinned rejection) while its golden expects successful emission. Verified failing at pre-refactor commit 89a6cef. Tracked in TODO.md as likely resolved by Slice 5.4's function-carrier equality convention; goldens NOT refreshed. Both convention oracles remained silent across the entire driver corpus (zero hits for either assert string). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…th (4b step 3) The CalleePhaseBetaDefinition convention is now authoritative on 'applied''s full-application path: phaseBetaArgModesFor derives the argument modes from the callee's SAWCore Pi type plus the SUPPLIED SOURCE actuals (polymorphic formals classify by polymorphicFormalInstantiatedExpectedSrc - Pi-instantiation from the source term, no emitted-Lean inspection), and the bind plan is computed from the modes. The two inert oracles that proved equivalence across the whole corpus are removed with the swap. Legacy argumentBindPlan/argumentBindPlanFromWrapped remain only on the eta/partial-application path and the PartialOpRaw path, each quarantined until its own step; the emitted-type predicate polymorphicFormalInstantiatedExpected survives solely for the eta path and is deleted with it. Fence: smoketest 54/54; conformance exit 0; emitted Lean byte-identical to baseline (zero changed/missing corpus-wide). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hs (4b step 4) argumentBindPlan / argumentBindPlanFromWrapped and the emitted-type instantiation predicate polymorphicFormalInstantiatedExpected are DELETED. Every consumer now derives its bind plan from the declared convention (phaseBetaArgModesFor + phaseBetaBindFromMode): - applied's eta/partial-application path: eta formals present the convention's declared representations (RawValueArg formals wrapped, missing Nat IndexArg formals wrapped-and-rebound, types/props/ functions raw); the bind plan covers supplied prefix + eta vars uniformly. A missing polymorphic formal classifies as a raw value formal (no instantiating actual to consult) - exactly the legacy Var-never-Except behavior. - etaExpandWrappedFunctionResult (recursor function-result eta): modes with no supplied actuals. - lowerPartialOpContract's PartialOpRaw path (divNat family). One classification correction en route: Num is TypeArg, not IndexArg - it is Cryptol's singleton width classifier (per shouldWrapBinder's contract), and TypeArg's never-bind is exactly the legacy semantics in ALL cases, where IndexArg would have diverged on a (nonexistent today) wrapped Num actual. No shape or bind decision is inferred from emitted Lean TERMS anywhere in the translator now. Two type-classification self-mirrors remain (bindingShapeOfType; the Except/Pi peel in applyKnownFunctionWithShape), documented as 4c demotion targets. Fence: smoketest 54/54; conformance exit 0 (189 OK); emitted Lean byte-identical corpus-wide; partial-application-heavy driver rows (sequences, conformance_stream, records, cryptol_module_popcount) re-run green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… unreachable Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p 1a) phaseBetaFunctionValueModesFor derives the argument modes of a phase-beta FUNCTION VALUE callee (bound variable/constant with phase-beta formals) from its SAWCore Pi type - a distinct family from the raw-formal targets: RawValueArg here means the emitted formal is WRAPPED. An inert assert in applyKnownFunctionWithShape requires the derived modes to reproduce the peelLeanPiTypes/isExceptStringType inspection exactly. The oracle already rejected one candidate: var-headed formals special-cased raw (correct for the raw-target family) - but THIS family's emitted Pi wraps them (shouldWrapBinder is True for variables), which the fix/iterate smoketest shapes caught immediately. The corrected candidate is the exact shouldWrapBinder mirror. Fence: smoketest 54/54; conformance exit 0, zero disagreements; emitted Lean byte-identical. Driver-corpus sweep next; swap follows oracle silence. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ts (4c step 1b) applyKnownFunctionWithShape's formal expectations now come from the declared function-value convention (phaseBetaFunctionValueModesFor); the per-argument peelLeanPiTypes/isExceptStringType inspection is deleted. Equivalence was proven corpus-wide by the inert oracle (58/58 driver rows + conformance, zero disagreements; the oracle rejected one bad candidate en route - var-headed formals wrap in this family, unlike the raw-target family). The RESULT-type peel remains as the one type self-mirror on this path, tracked with bindingShapeOfType for demotion. Also records the user-reviewed 'deliberate emission-quality debts' in TODO.md, each isolated at exactly one SUSPECT-marked chokepoint: - phaseBetaBindFromMode: RawValueArg binds RAW actuals too (identity but monadic noise) - fix is bind-iff-wrapped as a dedicated reviewed-diff slice after Slice 5. - phaseBetaArgModesFor: var-headed formals past the Pi-instantiation lookup are ASSUMED value-domain - fix is instantiation-directed modes with the dependent FunctionArg convention. The two-family asymmetry itself (raw-formal external targets vs wrapped-formal translated function values) is documented as FORCED, not a debt: propositions need raw operands; partial application over computed prefixes needs effectful closure interfaces. Fence: smoketest 54/54; conformance exit 0 (189 OK); emitted Lean byte-identical corpus-wide. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…step 2) ProofPrimitiveArgMode (Raw/Wrapped) is deleted; ppcArgModes are calculus ArgModes assigned from the SAWCore Prelude signatures: widths IndexArg, raw-logical equality subjects RawValueArg, source proof terms ProofArg, carriers TypeArg, wrapped runtime operands RuntimeArg. Interpretation is UNCHANGED: every raw-family mode translates under withRawTranslationMode (proof primitives state propositions over raw logical terms); RuntimeArg adapts to wrapped. The labels give Slice 5 the declared slot roles it needs when the equality-subject conventions take over these rows. Fence: smoketest 54/54; conformance exit 0 (189 OK incl. all proof_* obligation rows); emitted Lean byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…p 3) Audit finding: the old CalleeConvention enum was vestigial - only its CalleeRawLogical arm was ever consumed; every other constructor (including CalleeTransitional, returned for ~every callee) was constructed or declared but never scrutinized. The dispatch's real classifier has been the declarative guard chain over the contract tables all along, and Slices 4a-4c gave those tables declared ArgMode slots. So CalleeTransitional retires by deleting the vestigial enum rather than filling it in: rawLogicalCalleeForIdent/ForRecursor return Maybe RawLogicalCallee (the one real classification the enum performed), and CalleeConvention is gone. A NOTE at the old declaration site records the design decision for the Slice 7 lint. Also confirmed en route: the UseMapsToWrapped interpreter already binds wrapped actuals at raw slots (shouldBindRaw), so the 4a hazard class does not exist on that path; its UseArgShape vocabulary stays, documented, as table-local. Fence: smoketest 54/54; conformance exit 0 (189 OK); emitted Lean byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
bindingShapeOfType documented as a convention-internal self-mirror (legal inputs: types the calling function itself just built from a known wrap decision; the forbidden class - shape from emitted TERMS - was deleted in Slices 2/4b). Slice 4 marked complete in TODO with the 4c closing inventory. Fence: smoketest 54/54; conformance exit 0; emitted Lean byte-identical. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Restructure plan Slice 5 into 5a-5c (status: the six load-bearing proof rows are already green positives; they become the fence, not the target). 5a names and bounds the standalone-proposition convention: - subjectRepFromTranslatedOperands -> standaloneEqualitySubjectRep, with the convention contract in its doc comment: rho_eq := joint produced domain of the operands' declared production records (ttShape, stamped by producers - never emitted-AST inspection, never carrier type names). One convention among several, not a universal authority. - equalityPropositionAtSubjectRep documented as the entry point for surrounds that declare rho_eq (unsafeAssert, declared raw); the raw-mode-vs-adaptTo two-pipeline seam is documented for 5b to reconcile via the Eq.rec field set. - lowerRawLogicalCallee documented as the standalone consumer; the Eq__rec all-raw demand is marked as the conservative subset the 5b field set replaces. - Subject-rep decisions join the SAW_LEAN_TRACE_POSITIONS trace ([subjectRep] who/operand shapes/rep) so every rho_eq choice is auditable. Fence: smoketest 54/54, conformance exit 0, emitted Lean byte-identical to the slice0 baseline (626 artifacts). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…th, cache-schema bump Lint (precedence audit F-A/F-B): the "axiom wins" claim was only true across lines — a lexer-fatal on the SAME line as an axiom (e.g. `axiom v₁' : False`, legal primed identifier) aborts the line scan before the axiom rule runs and exits 2; the awk header and both kernel comments now say exactly that (per-line precedence, both-lines-printed output shape), rather than fixing the courtesy layer's granularity (churn the pivot rejected — every path still rejects). The one-line `if (code == 0)` precedence guard, shown unpinned by mutation, gains the axiom_then_fatal kernel-selftest case (axiom line 3, raw string line 5 → axiom-decl-in-user-file). SHIP-4 audit residues: cache prefix bumped lean- → lean2- (reuse is marker-existence only, so a pre-fix marker-plus-hole cache would have short-circuited forever; the bump orphans it as inert debris); abort message states the writable-tree precondition; the SmokeTest pin is named by guard, not by the already-stale :350 line number; fix_error_elem's surfaced .known-gap text now records the 2026-07-30 mechanical re-establishment (display-only text; the pinned .known-gap.expected is untouched). Verified: smoketest 94 passed; both selftests ALL CASES OK (incl. axiom_then_fatal); doc-claim-lint green. Full suite not re-run for this batch: the deltas are comments, a selftest fixture, a test name, display text, and a schema bump on the branch no suite exercises — the 1344s green sweep at a88d1d8 remains the gate for all behavior the suite observes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Five steps on branch saw-core-lean-0.02-closeout: (1) observation gaps — SHIP-2 data-mode row, SHIP-3 ship-list check, toolchain pin convergence, FXC-6; (2) threat-model re-score of the wave-3 SHOULD FIX list; (3) CI round-trip (user action) for the W5-2 determination; (4) wave 5 as the verdict wave; (5) exit criterion FIXED IN ADVANCE, with a failure clause that reopens the convergence diagnosis rather than just the finding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ence, FXC-6 SHIP-3: new support/ship-list-check.sh (data-files stanza ≡ tracked runtime assets; non-recursive-glob precondition; Builtins.hs relFiles duplicates; bundle_files ships the trees). Red-direction verified by stanza mutation. Wired into the suite. SHIP-2: new support/data-mode-selftest.sh — the wave-4 verifier's one-off procedure made repeatable: synthetic datadir built from the stanza, env -u SAW_LEAN_ROOT + saw_datadir + XDG_CACHE_HOME, the E1 replay goal admitted through the data-files branch, lean2- schema marker asserted, old-schema dir asserted absent. The ONLY execution of resolveLeanReplayAssets' installed branch (everything else exports SAW_LEAN_ROOT). Cold 7.5s / warm 4.3s observed; persistent gitignored cache re-stages on any shipped-byte change. Wired into the suite + clean verb. Toolchain pins CONVERGED: examples/saw-lean/proof/lean-toolchain 4.29.1 -> 4.32.0; demo lake build green, both discharges elaborate. The destructive clobber class is structurally gone; README Step-3 warning and getting-started caution retired to keep-pins-in-sync notes; TODO item closed (the share-the-build-tree hygiene question stays open as 0.03-grade). FXC-6: the recognizer's one unqualified ident test is now module-qualified (dtIdent == mkIdent preludeName "Stream"); smoketest 94 passed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sed; strict-verb env passthrough CP-3+K-5 (re-scored in-model MEDIUM, fixed): the anti-trivialization probe read ANY nonzero exit as "not trivial" — tool failure failing OPEN inside the trust kernel, against rule C3. Now the only accepted failure is the tactic failing INSIDE the probe (Lean reports it at triviality-probe.lean:2); a timeout, import failure, crash, or empty transcript fails closed under the new token triviality-probe-inconclusive. Hand-mutation verified (simulated timeout with empty transcript on the honest control stage yields the token); waiver row carries that evidence; kernel selftest ALL CASES OK (control admits, trivgoal still rejects, coverage + waiver-audit green). Gate-path divergences (partial): SAW_LEAN_FAIL_ON_KNOWN_GAPS added to Test.hs's env passthrough — the strict verb's variable was honored on the Makefile path and silently dropped on the cabal path. SAW_LEAN_ROOT stays deliberately absent (comment records why). The remaining divergences (env-construction ownership, binary identity, CI third path) are re-scored and dispositioned in the ledger, not refactored here. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… LIB-W2-2 precision; census prune W3-REF-1 (re-scored HIGH -> LOW by an opus pass, mechanism fixed): checkedEvidenceScript's tactic was the emitter's one emission of bare identifiers built as a raw STRING — invisible to the smoketest's Lean.Ident extractor, with exactly nine citations registered nowhere. The script now renders from checkedEvidenceSimpSet :: [Lean.Ident], which feeds contractEmittedNames: registration by construction; a user definition named after any simp lemma is refused by the F-7 gate instead of silently captured. Rendering is byte-identical (order preserved; goldens unmoved). The lint's over-claiming comment narrowed to inline-literals scope. Verified churn-free (no .cry/.sawcore defines any of the nine); smoketest 94 passed. LIB-W2-2 (re-scored MEDIUM-HIGH -> LOW): the drifted line range covered two functions; the sorry-placeholder half was already closed by two kernel gates, and the actually-cited unsafeAssertProofScript docstring now mirrors the Lean-side "PRECISION (LIB-W2-2)" injectivity paragraph its twin gained on 2026-07-29. Member count corrected: two latent (Integer, Rational -> LIB-W2-3), not three; bitvector was withdrawn. Census/oracle prune: the first sweep after the SHIP-2 selftest landed counted its 8 untracked staged library copies as emitted artifacts (lib1-census 362 vs 354). Both discovery walks now prune .data-mode-cache with the rationale recorded; census and oracle selftest re-verified green standalone. Ledger: dispositions written for every wave-3 SHOULD FIX item — the list is now fully threat-model-scored (two fixed, two closed-by-pin/downgraded-with-fix, one dispositioned to 0.03). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…glob); cold leg; pin equality
Step-2 audit F1 (MEDIUM, demonstrated end-to-end): the tightened
triviality discriminator still failed OPEN on resource give-ups —
Lean reports "maximum recursion depth" AT the probe's line 2, so a
goal rfl provably closes at higher depth was admitted as "not
trivial", landing exactly on the collapsed-computation shape a
trivialized emission produces. The accept condition is now an
ALLOWLIST of refutation shapes measured on the pinned toolchain
("error: Tactic .* failed" / "unsolved goals"); give-ups,
warnings-only, and any future phrasing fail closed — an explicit
availability-for-soundness trade on a pinned toolchain. New
trivgoal_deep selftest case pins the give-up branch; the env-class
waiver (whose rationale the audit showed false for the timeout
branch, F2) is deleted per the redundancy rule — the token now has
a live crafted-stage pin.
Step-1 audit F1/F2 (MEDIUM): both new scripts now expand stanza
globs with :(glob) pathspec magic — bare git pathspecs glob
recursively where Cabal's do not, so the synthetic install could be
richer than a real cabal install and the stanza check could pass
where cabal misses files. F6: ship-list-check gains the
demo/library toolchain pin-equality check (the convergence retired
the doc warnings that were the previous guard). F3: the data-mode
selftest runs a COLD leg (per-run scratch XDG — staging observed
every sweep) plus the WARM persistent-cache leg (reuse path); both
legs assert admission + schema. F4 reconciled by measurement: a
from-scratch library build is ~3.2s, the cold leg ~7.5s;
getting-started's "a few minutes" was the elan-download case and
now says so; the 120s-cap residue is retired to the network-bound
download sliver, unmeasurable here and not a CI exposure.
Verified: ship-list-check ALL OK (incl. pin equality), data-mode
both legs OK (11.5s total), kernel selftest ALL CASES OK (control
admits, trivgoal rejects, trivgoal_deep fails closed, coverage +
waiver-audit green).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ver the transcript The final audit proved (from Lean 4.32 source) that give-ups can be LAUNDERED behind an allowlist-matching line: SynthInstance catches runtime exceptions and rethrows "failed to synthesize" as a plain error the probe's tactic alternatives absorb, surfacing the clean last-alternative refutation (its F1, MEDIUM); and throwNestedTacticEx formats give-ups as "Tactic X failed with a nested error:" with the depth text on following lines (F2, latent). The refutation allowlist is now paired with a give-up DENYLIST over the whole transcript (maximum recursion depth / (deterministic) timeout / nested error / failed to synthesize). Residual recorded in the comment: a future toolchain's NEW launder phrasing behind an allowlist-matching line — the four markers cover the pinned toolchain's known channels. Evidence: denylist mutation-verified (a synthetic laundered transcript — allowlist first line + failed-to-synthesize body — fails closed with the right token); the audit's structural derivation stands as the reachability argument; a bounded empirical construction attempt (Decidable unification against a deep defeq) failed CLEAN without a give-up, recorded honestly — the direct give-up branch keeps its live trivgoal_deep pin, the launder branch rides the structural proof + transcript mutation. Also: ship-list pin-equality gains its existence guard (both files missing compared ""="" and printed OK — audit INFO); ledger W3-REF-1 marked landed (audit F3: the fix had landed in the same commit the entry called it "queued"). Verified: ship-list ALL OK; kernel selftest ALL CASES OK (control admits — healthy transcripts carry no denylist marker; trivgoal and trivgoal_deep reject with their tokens). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…6b38 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…pagation, ledger pass Wave 5 (doc/2026-07-30_release-gate-audit-wave5.md, 7 agents) judged the branch against the plan's pre-committed §5 criterion: gate NOT met at 237310f — clauses 1+2 fail on BOOKKEEPING (zero CRITICALs, zero translator/kernel defects, failure clause did not fire). The one MEDIUM (W5C-1, CONFIRMED): CONFORMANCE.md advertised positive obligation coverage for the *WithProof contracts withdrawn as unsound 2026-07-25 (LIB-2) — a propagation-failure class with instances in four files plus shipped docstrings, invisible to doc-claim-lint by construction. Remediation step 1 (documentation propagation, prose/comment only): CONFORMANCE.md rows 60/61/83/111/167/176/181/183/188 restated to the on-disk truth (known-gap statuses, rejections named instead of withdrawn contract shapes — restoration hazard, W5C-1 verify); statuses legend gains `realized`; architecture.md, proof-cookbook.md, STATUS.md, FixRecognizer.hs Haddock, and the SAWCorePrimitives.lean _raw docstrings all carry the dated S-2/LIB-2 withdrawal truth; the refutation row's own header names the three refuted fields (W5C-9). One adjacent inaccuracy found and fixed during the rewrite (row 83's prefix-partial claim vs its own goldens). Remediation step 2 (ledger pass): four stale-open rows closed (F7, FXC-6, SHIP-2, SHIP-3 — all had landed); the CP-3 entry records its two supersessions (DC5-1); wave-5's 14 residue dispositions folded into the STILL OPEN list (3 closures, 2 sharpenings); new WAVE 5 section records the verdict and the six clause-2 survivors with PROPOSED dispositions awaiting user acceptance. Small script fixes: selftest clean verb now actually cleans (DC5-4, $$ of the cleaning shell); ship-list sub-check (c) owns its verdict line (DC5-5). Checks at this commit: doc-claim-lint green (283 identifiers), ship-list ALL OK, kernel-selftest clean verb verified. Full sweep owed at the release commit (clause 3 is per-commit). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…lease commit Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ty-gate A/B, rule amendment Deletion-biased review of the trust kernel at 1cb4bdf, with its first draft adversarially refuted before reaching the user: the drafted control-probe redesign of the triviality gate was demonstrated END-TO-END to admit a trivialized emission the current kernel fails closed (no [K] check catches goal-formation defects — the gate is the only defense in its class), re-opened the CP-3 timeout fail-open, and rested on a false premise about the completed path. The published review presents the honest menu: Option A (harden in place per the reviewer's amended design, CODE-neutral) vs Option B (delete the gate outright per the threat model's own load-bearing list, ~-50 lines, residual documented) — recommendation B by D2 precedent; user decision. Also: re-accretion measured against the D2 baseline (+16 kernel CODE lines concentrated in two places, +240 comment/test lines); full check inventory classified [K]/[M]/[T]; lint token collapse deferred to 0.03 (mirror-implementation blast radius); standing rule adopted in amended form (no fix may flip an outcome class from reject to proceed; prefer mechanical discriminators); plan 3b reserve recorded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ion B, user decision) The gate was a text-discriminated negative probe outside the threat model's load-bearing list whose decoder went through three same-day audit rounds (fail-open -> position check -> refutation allowlist -> allowlist+denylist) and ended coupled to one toolchain's error phrasing with an unpinned denylist half — the empirical proof it could not be kept "small enough to be kept honest". Deleted per doc/2026-07-31_kernel-design-review.md §3.1 Option B: both tokens (goal-formation-trivial, triviality-probe-inconclusive), the probe, both regexes, ~50 kernel lines; trivgoal/trivgoal_deep retired with their subject; compact tombstone at the gate's site. The residual is DOCUMENTED, not silent: residual-trust.md §3.2e states plainly that a trivialized emission discharged unnoticed is admitted, what defends the class instead (differential corpus at development time; goal visibility at discharge time; the required conjunction), and that any re-entry is an emission-side structural check, never a replay-side message parser. The trust-surface prose at §3.2b-adjacent and replay-design.md's three mentions updated; the CI-divergence instance the wave-2 record measured by this gate's existence dissolves with it. contributing.md gains rule C7 (the courtesy-layer fix rule, adopted with the design reviewer's amendment): resolutions are deletion / kernel question / documentation / fail-closed-unrecognized; no fix may flip an outcome class from reject to proceed without its own audit; mechanical discriminators over text ones. This deletion is itself such a flip — sanctioned by user decision and receiving its own audit per the rule. Ledger: CP-3 entry carries its final supersession; R5-RES-11/14 dissolve. Verified: both selftests ALL CASES OK (coverage meta-guard green with no dead waivers), doc-claim-lint green; kernel CODE 232 -> 219 lines, tokens 33 -> 31, zero error-message regexes left in the trust path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The deletion commit numbered the new goal-formation residual §3.2e, but §3.2e already exists (LIB-1, cited by README:68 and STATUS:253); the selftest note also said §3.2d, which is the type-image section. Caught by the deletion audit's partial run before it was killed by an auth expiry. All five referrers renumbered to the genuinely free §3.2f; pre-existing §3.2d/§3.2e citations verified untouched; doc-claim-lint green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… cost honesty, stale prose The gate-deletion execution-fidelity audit (per rule C7) found no blocker, no collateral damage, and no smuggled change — the deletion is clean, the meta-guard consistent, C7's provenance verbatim. Its five prose findings, fixed: F-C: the decision log gains D5 (the deletion is the same class of user-decided kernel scope call as D1-D4 and must be findable where they are). F-B: residual-trust §3.2f now states each defense WITH its limit — the corpus cannot reach novel replay goals (the review's accepted cost, verbatim), and visibility is defeated by deep-evaluation trivializations (the very shape that decided the deletion) — so the catalog reads no stronger than the decision. F-A: the selftest real_goal() comment and replay-design's F9 backstop paragraph no longer describe the deleted gate in the present tense. F-D/F-E: the design review §5 carries measured outcomes beside its predictions (CODE 219 not ~185; the honest claim is zero error-message text in ACCEPT conditions — three reject-direction greps remain and can only tighten) instead of quietly restating them. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d; dispositions accepted
User decision 2026-07-31 ("go ahead with the fast path"), executed:
OBL-1 FIXED AND PINNED: the five stream-helper obligation rows
shared one byte-identical expected.txt naming no stream operation —
the wave-2-demonstrated shift_l->shift_r emitter mutation passed
every directive. Each row now also pins its OPERATION by lowered
structure (identity read; addNat-on-index without bit0; streamScanl
under Bind.bind; index-arithmetic shift with bit0 and NO
atWithDefaultM/subNat; atWithDefaultM+subNat+genWithBoundsM),
deliberately NOT by probe name (a mutation keeps the name).
Cross-matrix verified at introduction: every set accepts only its
own emission — all 20 cross-pairs fail, and the demonstrated
mutation now fails on the shift_l row's absents. All five rows
green through the real harness.
F8b CLOSED AS UNCONSTRUCTIBLE (F-9 treatment): the owed pin's
triggering .saw script cannot be written — the emitter refuses the
shape upstream of the pinned surface.
Ledger + plan doc: the six clause-2 dispositions recorded as
user-accepted (F11, LIB-W2-3, F12-successor = explicit 0.03
carries with risk notes; W2-UNRUN-2 re-score commissioned);
clause-1 reading (i) recorded as a user decision; clause-4 noted
as the REMOTE Actions run per user confirmation.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ed and pinned RELEASE-BLOCKING defect found by the W2-UNRUN-2 threat-model re-score and verified independently end-to-end before fixing. Gate 3's TEST 1 exempted every NAMED telescope binder on the premise "a named domain is never a folded hypothesis". False: the Lean binder name is copied from the SAWCore VarName regardless of dependency (Convention.withSAWVar) and parse_core preserves a hand-written name. So prove_core (offline_lean ...) "(h : EqTrue (bvult 8 (at 2 ... [.., error ..] 0) ..)) -> EqTrue (bvEq 8 3 4)" EMITTED while the identical ANONYMOUS goal was refused. Measured at the pre-fix HEAD: SAW proves the hypothesis (lazy `at`, error slot unforced) and refutes the conclusion, so the obligation is FALSE; the emitted goal proves in Lean via congrArg Except.isOk with [propext, Quot.sound] — both on the replay allowlist — so offline_lean_replay would have issued LeanReplayEvidence for a false claim. In-model (no adversarial author; parse_core/prove_core are Current builtins), CRITICAL. FIX (mechanical, C7-safe direction proceed->reject): gate 3 now runs the printer's own anonymizeUnusedPiBinders before asking TEST 1's anonymity question, so it inspects the binders the artifact actually ships — the printer already anonymized named-but-unused binders, which is why the gate and the emitted text disagreed. Pretty.hs exports the function; ~2 lines in Signature.hs. Over-refusal checked: named-dependent and named-unused VALUE binders still emit (both probed). PIN: saw-boundary/.../except_carried_named_hypothesis (+ golden), with the stated mutation (restore the blanket named-binder exempt). The §5 failure clause fired, so its deliverable lands with the fix: doc/2026-07-31_why-gate3-escaped.md — the limit was KNOWN, then "narrowed" by a measurement that answered a different question than the claim it was cited for (one clause measured, one assumed, the conclusion drawn as if both were), which converted an open question into a settled one for three audit waves. New rule C8 governs limit-narrowing. Also: the OBL-1 fix-audit residues (canonical owed-pins row (i) struck with the full F-9 treatment; positional pins closing the audit's three shift-row mutants). Verified: smoketest 94/94, doc-claim-lint green, witness now refused, control unchanged. Full sweep in flight. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… in the close-out plan The §5 failure clause fired; its deliverable was written before the fix. Clause 3 re-established at d4d4c43 (full sweep PASS); clause 2's last item settled; clause 1 gets an honest asterisk — it quantified over wave 5, and wave 5 was clean of this because no wave read the surface. New pre-release wave-6 charge: the emission-side goal-shape gates, audited by witness-construction rather than comment-reading. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… name is not evidence The fix audit of d4d4c43 refuted that fix with two further in-model CRITICALs, both demonstrated end-to-end at the fixed HEAD. Verified independently before re-cutting: (A) Asking the anonymity question of the PRINTED text inherited the printer's opposite safety polarity. `mentionsIdent` over-reports mentions BY DESIGN (safe for a cosmetic rename, ADMITTING for a gate) and its Tactic arm is a SUBSTRING test — so a binder named `h` counted as mentioned by any goal whose conclusion carries an obligation script containing `h_bounds_obligation_`. Measured: binder `h` EMITTED, binder `zz` REFUSED, structurally identical goals. My "safe direction under C7" claim held only for binders the predicate called unused. (B) A hypothesis binder can be named AND genuinely used (`(g : EqTrue X -> Bool) -> (h : EqTrue X) -> … (g h)`), so no sharpening of "is it used" could ever have closed the class. THIRD CUT: delete the anonymity test outright. A binder NAME carries no soundness information about hypothesis-vs-value and never did — both the original gate and the first repair keyed on it. What remains is the value-image test applied to EVERY binder: carrier-headed after peeling Pis means the domain delivers a value, and every such image is inhabited (`Except.error ""`), so it cannot make the implication vacuous. The printer export is reverted, so the gate/printer coupling the audit warned about is gone. COST, measured not assumed: composite domains (a tuple with a function component) are not carrier-headed after peeling and now over-refuse — fail-closed, the C7-safe direction. Full suite PASS (1350.55s) with this cut, so no corpus shape pays for it. Legitimate value/function/named-used shapes still emit (probed). PINNED, one row per escape mechanism, each with its stated mutation: except_carried_named_hypothesis (named-unused), named_hypothesis_tactic_conclusion (the printer-coupling substring escape), named_hypothesis_used_binder (named-and-used). All three ran inside the green sweep with empty diffs. Also fixes the audit's wrong-pin-path comment; limit 2 now records all three cuts. The principled successor stays a wave-6 charge: decide hypothesis-vs-value SAWCore-side, where "is this domain a Prop" is unambiguous, instead of recovering it from the Lean image. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n the walk The audit of the third cut refuted it with the strongest witness yet: it ran the goal through offline_lean_replay and got LeanReplayEvidence ISSUED for a FALSE SAWCore obligation — not "would have", did. Mechanism: cut 3 exempted any binder whose type peels to the value carrier AND STOPPED WALKING. For a binder typed `(EqTrue P) -> Bool` the peeled codomain is a value, so the poisoned hypothesis `P` in the binder's own DOMAIN was never inspected. With P's Lean image uninhabited the Lean function space collapses to one element while the SAWCore one has many, so `Eq _ g h` is a Lean theorem and a SAW falsehood. My cut-3 argument — "delivers a value, therefore inhabited, therefore cannot make the implication vacuous" — was true and IRRELEVANT: vacuity is not the only way to be weaker. Note SAW cannot check this goal class itself (sequentToSATQuery refuses function-typed binders), so for it the gate is the only thing between the user and false evidence. FIX: classification is now recursive and uniform (classifyGoalDomain). At every Pi level what the type ultimately DELIVERS may be a value, but each domain it CONSUMES is itself a position to classify. Only a final codomain is exempt; nothing is skipped. Verified: all four witnesses refuse; value/named-used/value->value-function shapes still emit; full suite PASS (1349.72s), smoketest 94/94, doc-lint green; all four pins ran inside the sweep with empty diffs. Also corrects a rule-C8 violation I committed in the cut-3 pin header: named_hypothesis_used_binder.saw asserted "g's own image peels to the value carrier and is correctly exempt (a function from an uninhabited domain is inhabited)" — an unmeasured clause stated as settled, in the same commit that added C8. The correction is recorded in place. New pin: exempt_binder_poisoned_domain, with its stated mutation (restore cut 3's `= []` early exit). STANDING RECOMMENDATION, recorded with the fix: this is the FOURTH cut of this gate in one day and cuts 1-3 were each refuted by a witness, three of them after I stated a false premise confidently. Cut 4 closes every witness built so far, which is exactly what I believed of cut 3. The release should NOT rest on it. The durable fix is the SAWCore-side decision (is the domain a Prop? — a sort check, not a reconstruction from the Lean image), designed and adversarially reviewed BEFORE implementation, as the triviality-gate redesign was. Cut 4 is the fail-closed stopgap: it strictly refuses a superset of cut 3. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ged, redesign logged User decision 2026-07-31: "roll it into residual trust, and log it as something to revisit" — rather than hold the release for the SAWCore-side redesign. residual-trust.md §3.2g states the residual without softening: the gate was cut FOUR times in one day, cuts 1-3 each refuted by a constructed witness (the third after offline_lean_replay had ISSUED evidence for a false obligation), and cut 4's correctness is NOT legible — the same was believed of cut 3. Includes the four-cut table, the measured bounds the decision rests on (one production consumer and no cascade; enable_experimental opt-in, tested not inferred; the Cryptol/LLVM/goal_cut routes closed since they emit only anonymous binders; zero exposure across all 78 goal goldens / 114 binders; every cut refusing a strict superset of its predecessor, so wrong can only mean incomplete, never newly broken), and the point where this residual is WORSE than §3.2f's: goal inspection does not mitigate it, because an escaped goal reads as an ordinary conditional. decision-log D6 records the call and its grounds. TODO.md § 0.03 gains the redesign as the revisit item, with the process requirement the record earns: design doc, then an ADVERSARIAL review of the DESIGN before implementation, then implement/pin/sweep/audit — the order whose absence produced four cuts in a day. W2-UNRUN-2's FpOther blindness folds in as the same missing distinction seen from the other side. Plan doc: the emission-side gate work is no longer a release blocker, and §5 now carries its final state — clauses 1, 2 and 3 MET (clause 3 at 8d9bdba; this commit is doc-only and does not disturb it), clause 4 the only one outstanding: merge, then the REMOTE Actions run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
19 commits, branch saw-core-lean-0.02-closeout, executing doc/2026-07-30_convergence-closeout-plan.md whose §5 exit criterion was fixed IN ADVANCE so it could not be retrofitted. Steps 1-2 (observation + threat-model re-score): - SHIP-2 data-mode selftest (cold+warm legs) — the first execution of resolveLeanReplayAssets' installed/cache branch, previously dead under every harness; SHIP-3 ship-list closed check; demo/library Lean toolchain pins CONVERGED, retiring the destructive clobber class; FXC-6. - Every wave-3 "SHOULD FIX" item threat-model-scored with a written disposition. Two were real in-model defects and are fixed+pinned: the anti-trivialization gate's fail-OPEN branch (tool failure failing open in the trust kernel, against C3) and the strict-verb env var dropped on the cabal path. W3-REF-1's mechanism fixed by construction (checkedEvidenceSimpSet feeds contractEmittedNames); LIB-W2-2 reduced to a corrected comment; W3-HR-4 closed by pin. Wave 5 (the verdict wave) + remediation: - Verdict: gate NOT met at the time — one CONFIRMED MEDIUM, the S-2/LIB-2 documentation-propagation class (CONFORMANCE.md advertising coverage for contracts withdrawn as unsound). Fixed across five files plus shipped docstrings; ledger pass; four stale-open rows closed. Kernel design review (user charge: don't rebuild the cruft): - Re-accretion measured against the D2 cut (+16 kernel CODE lines in two places, +240 comment/test lines). The review's own first draft was adversarially REFUTED before reaching the user — it would have admitted a trivialized emission — and the published version presented an honest A/B. User chose Option B: the anti-trivialization gate DELETED whole (D5), residual cataloged at residual-trust §3.2f, rule C7 added to stop the accretion pattern. The gate-3 CRITICAL (the arc's most serious finding): - A W2-UNRUN-2 re-score, reading code rather than ledger text, constructed a demonstrated unsound-acceptance path: a NAMED hypothesis binder walked past goal-shape gate 3 while the identical anonymous goal was refused; the SAW obligation was false and the emitted goal proved in Lean with allowlisted axioms. Verified independently before fixing. §5's failure clause fired and its deliverable — doc/2026-07-31_why-gate3-escaped.md — was written BEFORE the fix: the limit was known, then "narrowed" by a measurement that answered a different question than the claim it was cited for, which converted an open question into a settled one for three audit waves. Rule C8 added. - The gate then took FOUR cuts in one day; cuts 1-3 were each refuted by a constructed witness (the third after offline_lean_replay had ISSUED evidence for a false obligation). Cut 4 makes classification recursive and uniform. Four witnesses pinned, one per mechanism. Disposition (D6, user decision): ship 0.02 on cut 4 with the residual CATALOGED (residual-trust §3.2g) rather than hold for a redesign — taken on measured bounds (one consumer, no cascade; enable_experimental opt-in; the Cryptol/LLVM/goal_cut routes closed; zero exposure across all 78 goal goldens; every cut refusing a strict superset of its predecessor). The SAWCore-side sort-check redesign is logged for 0.03 WITH its process requirement: design doc, then an adversarial review of the DESIGN, before implementation. §5 state: clauses 1, 2, 3 MET (clause 3 at 8d9bdba — full suite PASS 1349.72s, smoketest 94/94, both kernel selftests, doc-claim-lint, ship-list; the commits after it are doc-only). Clause 4 — the remote CI run — is the only one outstanding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
User direction: keep the backend's footprint outside its own subdir very limited — references are fine, discussion is not. Net -33 lines across five shared files, no behavior change (saw builds clean). CHANGES.md: the entry claimed offline_lean_replay "always fails with a diagnostic" — FALSE, it admits goals today. Corrected and kept to four lines, pointing at saw-core-lean/README.md for setup, limits and the trust model, rather than restating them here. doc/developer/developer.md: dropped the Phase-1a lockdown-item taxonomy (L-1..L-14 enumerated across two test-suite entries) from SAW's developer doc; the suites now get one line each plus a pointer to saw-core-lean/doc/contributing.md. .github/ci.sh, .github/workflows/ci.yml: my own comments from earlier today were 16 and 11 lines of backend audit narrative (wave-4 SHIP-1, W5-2, DEMO-7, a saw-core-lean/doc path). Cut to the operational facts a CI maintainer needs — why the assets must ship, that the unpacked root must be writable, why the export exists — plus a pointer. saw-central/src/SAWCentral/Builtins.hs: same treatment. The staging comments keep the invariants they document (per-call-unique names, the marker-existence reuse rule, the schema bump's purpose) and lose the finding IDs and dates. Root README and doc/saw-user-manual were already clean of backend content; verified, not assumed. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
A fresh-eyes agent followed the docs literally and could NOT complete the journey: three failed SAW runs, then it had to read a test row to discover the replay contract. Fixes, all verified by running them: BLOCKERS - getting-started gains Step 4 (offline_lean_replay), the step that was missing entirely. Documents the proof-directory contract as a table, and the two things that decide acceptance: the theorem must be `goal_closed` (not the emitted placeholder's `goal_holds`), and `proof.lean` must get `goal` via `import Emitted` rather than carrying a local `def goal` (the collision that surfaces as closer-wrong-type). Explains that the Lake file you iterate on and the proof.lean you hand replay are two different files, and why. VERIFIED: wrote the documented four-line proof.lean and ran the documented script — "Lean kernel check passed", goal accepted. - Prerequisites now cover what actually blocks a start: where `cabal build exe:saw` puts the binary, that elan installs no default toolchain (so `lake new` fails outright), the exact toolchain string to pin, and SAW_LEAN_ROOT. - Step 2 hoists the toolchain-pin instruction BEFORE the first build. It previously arrived in a note after `lake build`, i.e. after the damage; and the obvious fix (`elan default stable`) is the harmful one, since a mismatched pin rebuilds the shared library in place. MISLEADING - The walkthrough's regression row was described as holding the example "verbatim"; it is in replay form and differs in exactly the two ways that decide acceptance. Now says so. - "Where to read next" pointed trust questions at an archive doc whose own banner says its trust model has changed materially and which never mentions LIB-1. Replaced with a "before you rely on a replayed goal" section pointing at the README's limitations and threat model. - examples/saw-lean/README claimed getting-started "walks this flow"; it walks a different, smaller example. GAPS - proof-cookbook gains "When replay rejects your proof": the 12 user-causable CHECK-FAIL tokens with cause and remedy, extracted from the kernel rather than guessed. None of the ~31 tokens was documented anywhere a user reads. - Documented how to see what replay recorded (LEAN-REPLAY under Solvers Used) and pointed at `:help offline_lean_replay`, which is the best documentation in the project and was linked from nowhere. CORRECTIONS TO MY OWN CLAIMS FROM TODAY, caught by verifying rather than asserting: - residual-trust §3.2g and the README listed "`enable_experimental` opt-in" as a measured BOUND on the gate-3 residual. FALSE, and it widens the residual: parse_core, prove_core, offline_lean and offline_lean_replay all run with no flag, and a hypothesis-bearing goal reaches the gate with no flag. What I had actually tested was goal_num_when's flag requirement. Corrected in place, visibly, as the C8 failure mode committed in the entry that records C8. - The README's new "read your emitted goal" advice sat immediately after the gate-3 caveat, implying inspection covers that class; §3.2g says the opposite. Now states where reading helps (the conspicuous over-reduction class) and where it does not (an escaped hypothesis goal reads as an ordinary conditional). - summarize_verification needs enable_experimental; my first draft said otherwise. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
That branch is long-lived and merged into rather than PR'd per change, so with only master/release-** on the push trigger it got no CI coverage. One entry in the existing branch list. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The walkthrough called STATUS.md the best "what is this / what works" page in the project. It was two days and one heavy arc stale, and the README's Documentation list did not link it at all. - Smoketest count 73 -> 94 (verified by running it, not inferred). - Known-gap census RE-VERIFIED, still 72: recounted from disk as 68 `.known-gap` markers + 3 proof-gaps + 1 stretch. The table stands. - Conformance row figure (235) marked OWED A RE-MEASURE rather than restated. My disk count was of directories; STATUS counts rows, and saw-boundary directories hold several rows each, so the two units are not interchangeable. Substituting one for the other is exactly the error this file warns about (a stale count silently narrowing a release claim), so it is flagged instead of guessed. - New "Recent history" section: waves 4 and 5, the close-out arc, the design review that deleted the anti-trivialization gate, and the gate-3 CRITICAL with its four cuts — each with its doc pointer. - Header now states release posture accurately: three of four release-gate clauses met, the outstanding one a green CI run. - Names the three shipped-documented residuals (§3.2e/f/g) and says the user-facing summaries live in README.md. - README's Documentation list now leads with STATUS.md as the "is this usable for my case?" page. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Items 1-4 of the docs pass. (1) getting-started Step 4 gains "Goals with obligation placeholders (completed.lean)" — the path every partial operation (indexing, division, modulus) takes, previously one row in a table. Explains why the placeholder cannot be fixed from proof.lean (it is part of the goal's own text), the four mechanical steps, that the `def goal` line must stay verbatim, and that completed.lean must contain no `sorry` while proof.lean still carries the proof. Points at the two CI-exercised completed.lean rows under proofs/ as worked examples rather than shipping a toy: they cannot drift from what the checker accepts. The documented placeholder tactic is one I ran — the edited outline was accepted past the compile and sorry gates. (2) The backend README's Documentation list is now split "if you are trying to USE the backend" (getting-started -> STATUS -> cookbook -> the limitation sections -> :help) versus "if you are working ON it". It previously led with the trust authority and release plan. (3) The cookbook gains "Before you rely on a discharged goal", pointing at the README's limitations and threat model. A reader who lives in the cookbook could previously never learn LIB-1 exists — and the cookbook is exactly where someone is holding a closed goal and about to believe it. (4) Root README: five lines, one pointer, in the Documentation section — the backend exists, it is experimental, read its own docs including the soundness limitations. Nothing else about it at top level. Also filed, found while building the worked example: an unused goal binder emits a file Lean cannot elaborate (`Pure (?m.N x)` stuck instance on the `let x := (Pure.pure x)` shadow). Minimal pair verified: `\(i : [8]) -> i == i` compiles, `\(i : [8]) -> (3 : [8]) == 3` does not, and with no binder at all it compiles — so it is specifically the unused-and-shadowed case. Fail-closed (replay says emitted-does-not-compile), so completeness not soundness. In TODO.md with the likely fix, and in the README's punted list because a user hits it with no hint that an unused binder is the cause. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…filed Ran eight shapes a newcomer would actually reach for through emission and elaboration. Six compile: Bool properties, symbolic bv equality, x + y == y + x, x <= x, symbolic sequence indexing, and concrete arithmetic. Both failures are the same class, and it is more reachable than yesterday's filing implied: ANY unused binder triggers it, not just a lone one — `\(x : [8]) (y : [8]) -> x == x` fails on `y` alone. So an ordinary property carrying one parameter it happens not to reference hits this, which makes it a likely first encounter rather than an exotic case. Both the TODO entry and the README's punted list now say so; the README's example is the two-parameter one for that reason. Bounding good news, recorded with it: this is the SINGLE failure class among common shapes, not one of many. (The earlier filing also read as if the pair `i == i` / `(3:[8]) == 3` characterized the defect. It characterizes the mechanism; it understated the reach.) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The first CI run ever to execute on this branch (run 30651900851) failed `integration-tests` on both ubuntu-24.04 and macos-15, with 2 of 320 tests red: test1646 and test_search. Both are builtin-listing goldens that drifted when Lean primitives were added without resyncing them. Last sync was bd0bbe4 (2026-05-02). Landed since, unsynced: write_lean_sawcore_prelude b285470 2026-05-11 write_lean_cryptol_primitives_for_sawcore 0f08890 2026-06-26 offline_lean_replay c39d45e 2026-07-14 All three are registered `prim`s in Interpreter.hs with exactly the signatures the goldens now record, so the new outputs are correct (the harness asks that this be checked, not assumed). Verified locally against bin/saw, both directions: the pre-edit goldens reproduce the CI diff line for line, and the post-edit goldens diff clean. `bash ./test.sh` exits 0 in both test dirs. Note this class of drift is invisible to the backend's own sweeps: `integration-tests` (hs-source-dirs: intTests) and `saw-core-lean-tests` (otherTests/saw-core-lean) are disjoint cabal test-suites, so no backend-local green claim ever exercised these goldens. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
W5-2 asked which disjunct held for 07-18..07-30: the saw-core-lean-tests CI leg red, or not running. Answer, from `gh`: NOT RUNNING. Before 7f573bf the workflow's push trigger was `branches: [master, "release-**"]`, which never matched this branch, and no PR run fired either — the fork's entire run history is two runs, both today. This is exactly the disjunct the F4 fix audit narrowed to ("possible only if the workflow never ran on this branch at all"). The observation half stays open and got worse, not better: the first real run failed integration-tests before reaching the demo, so the SAW_LEAN_ROOT export and the bundle_files bindist change are still unexecuted. §5 clause 4 is NOT closed. W5-2b files what that run exposed. integration-tests and saw-core-lean-tests are disjoint cabal test-suites, so adding a SAWScript prim reddens intTests goldens that no backend sweep runs. The tree carried that regression for ~2.5 months while every sweep reported green. Recorded as a standing C8 consequence: a "suite green" claim from the backend path must name that path and must not be read as "CI would be green". doc-claim-lint: 285 identifiers across 8 docs, all resolve. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
An unused goal binder made the emitted file fail to elaborate.
quantifierShadow opens every goal body with a let-shadow chain that
Pure.pure-lifts each value-typed binder, emitted WITHOUT a type
annotation. For a binder the body never references there is nothing
to infer the monad from, so Lean stops with
typeclass instance problem is stuck
Pure (?m.31 x y)
Reach, surveyed 2026-07-31 rather than assumed: this is not the
exotic "property that ignores its only parameter". ANY unused binder
trips it, including one unused parameter among several used ones, so
an ordinary property carrying a parameter it happens not to reference
hits it. Severity is completeness and diagnostic quality, never
soundness: offline_lean_replay refuses with emitted-does-not-compile
and offline_lean writes a file the user cannot build.
Fix: gate each shadow on the new Lean.identOccursIn. That helper is
deliberately CONSERVATIVE — binder positions, Tactic source text, and
sort/universe names all count as occurrences — because a False answer
is what licenses deleting a binding. Over-reporting keeps a harmless
shadow; under-reporting would drop a live one. `let n := e; b` with n
absent from b is just b, so the rewrite is meaning-preserving by
construction rather than by argument.
Pinned by workflows/unused_binder_shadow: t1 unused-among-used (the
wide-reach shape), t2 sole-binder-unused, t3 unused in the MIDDLE of
three, t4 all-used control. The row lives in workflows/ because that
harness ELABORATES emitted files — a shape-only pin would not have
caught this, since the bad emission read as perfectly reasonable
text. t4 is load-bearing: without it, deleting the shadow chain
outright would also pass.
MUTATION VERIFIED, not asserted: removing the guard and rebuilding
turns t1/t2/t3 red with the stuck-instance error while t4 stays
green; restoring it returns the row to green.
Emission for all-binders-used goals is byte-identical to the pre-fix
binary, so this strictly widens what compiles.
lib1-census EXPECT_SCANNED 354 -> 358 for the row's four artifacts,
verified as the whole delta by moving exactly those files aside
(354) and restoring them (358).
Green: cabal test saw-core-lean-tests (373 rows, 72 known gaps, the
only failure was the census constant now updated); cabal test
saw-core-lean-smoketest (94); doc-claim-lint (285 identifiers).
Found by executing the getting-started documentation rather than
reading it — three release-gate audit waves read this code and
missed it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds
saw-core-lean, a SAW proof backend that discharges SAW proof obligations in Lean 4's kernel. It fills the same slot as a solver backend — SAW emits a verification condition, the backend presents it to another trusted engine, and success closes the obligation — except that the artifact is kernel-checked, inspectable, and replayable. Translation of SAWCore/Cryptol to Lean is the means, not the product.Generated
.leanfiles import a small handwritten support library (saw-core-lean/lean/CryptolToLean/) and elaborate underlake buildon pinned toolchainleanprover/lean4:v4.32.0.Status: NOT RELEASED. Three of the four 0.02 release-gate clauses are met; the outstanding one is a green CI run (gate:
saw-core-lean/doc/2026-07-30_convergence-closeout-plan.md§5). The current state of the world issaw-core-lean/STATUS.md; the open work list issaw-core-lean/TODO.md.Design
The translator normalises each input term with
scNormalizeto a fixed point, then walks the result: after normalisation only SAWCore primitives, inductives, and recursors remain, mapped 1:1 onto the handwritten Lean support library. User definitions are inlined rather than emitted as separate Leandefs. This sidesteps the Lean cumulativity gap that blocked a direct mirror of the Rocq backend (saw-core-lean/doc/archive/2026-04-23_specialization-approach.md).Since the original description, the implementation moved to the position/callee calculus (
saw-core-lean/doc/2026-07-02_position-callee-calculus.md), which is now the implementation rather than a design on paper:Except String T; type-level expressions translate raw. Every translation is directed by a declaredExpectedPosition, callees carry declaredArgModeconventions, and adaptation happens at a single chokepoint (adaptTo) where forbidden adaptations are unrepresentable.TranslatedTermproduction records; shape is never re-derived by inspecting emitted Lean (that inspection class is deleted, and a source lint keeps it deleted).@Foo.reccarries a Lean-checked constructor-order assertion (saw_ctor_order), so a reordered inductive on either side fails the emitted file loudly.Prelude.fixand partial operations route through proof-carrying obligations with Lean-checked evidence; recognized fix classes lower to proven realizations, everything else rejects with a named diagnostic.Commands
write_lean_term— one Term + type → anoncomputable def.write_lean_cryptol_module— a Cryptol.cryfile → a Leannamespaceofdefs.write_lean_cryptol_primitives_for_sawcore— regenerate the Cryptol primitives module.offline_lean— emission-only: writes the goal file and returnsSolveUnknown, so the SAW goal stays unsolved and scripts wrap it infails. SAW never claims a goal on the strength of an export.offline_lean_replay— the discharge path: re-emits the goal fresh (fresh emission is the authority), checks a user-completed proof against it under a factored trust kernel (saw-core-lean/replay/lean-check-core.sh: exact-match axiom allowlist,sorry/placeholder policy, drift and closer probes), and only on full success admits the goal with recordedLeanReplayEvidence. Design:saw-core-lean/doc/2026-07-16_replay-design.md.Coverage today
Works end-to-end: monomorphic instances; Cryptol modules with
{a}-polymorphism overType 0; theexamples/saw-lean/demo; compositional replay chains (the ChaCha20 core/qround family,llvm_eq_u128,llvm_popcount_eq,llvm_doubleround_comp); recognizedfixclasses (running sum, popcount32,rec_ones).Punted, all with named diagnostics or pinned gaps — 72 known-gap rows, re-verified 2026-07-31, tabulated in
STATUS.md: iterate-family and paired-stream fixes, direct recursors for Nat/Pos/Z/Bool/Accessible*, user datatypes, class-dictionary primitives (PCmp,PEq,PRing, …), SMT-array/enum/polynomial surfaces, raw-positionerrorand raw-positionfix, nativeLean.BitVecas thebitvectortype. Much of the growth in that census since 0.01 is deliberate withdrawal: the*WithProofprimitives (LIB-2) and raw-positionfix(S-2) were removed because their emitted statements were strictly weaker than the SAW obligations they claimed, and each withdrawal converts green rows into pinned gaps so the capability loss stays visible.One live usability defect worth flagging for review: any unused binder in a property makes the emitted file fail to elaborate (
\(x : [8]) (y : [8]) -> x == xfails onyalone). It fails closed — replay refuses withemitted-does-not-compile— but the user sees a raw Lean instance error rather than a named refusal, which is the actual defect. Surveyed 2026-07-31; tracked inTODO.md.Soundness — read before relying on a replayed goal
No skip lists, no
sorryin the translator, no close-but-not-equal mappings. Trust authority and axiom inventory:saw-core-lean/doc/2026-05-02_residual-trust.md(the axiom base is exactly two Vec↔BitVec round-trip axioms). Three residuals ship documented, and reviewers should weigh these specifically:Except String (Vec n T)collapses any erring element into failure of the whole vector. That collapse is non-injective and lands on both sides of an emitted equation, so a SAW-false equation whose falsity hides behind an unread erring slot can close byrflin a clean kernel — invisible to every replay gate by nature. Reachable from ordinary Cryptol; no landed proof is affected; pinned byotherTests/saw-core-lean/differential/lazy_vector_error_slot. Remedy (0.03): the faithful per-element carrierVec n (Except String T), which cannot represent the collapse by construction.rfl/trivialalone was deleted on 2026-07-31 rather than hardened a fourth time (doc/2026-07-31_kernel-design-review.md): three audit rounds in one day showed its implementation could not be kept honest, and it was not one of the checks that ask Lean's kernel a question. What defends against emitter over-reduction instead is the differential corpus and the fact that a trivialized goal is visible in the file you open.doc/2026-07-31_why-gate3-escaped.md). The gate took four cuts that day. It refuses every such goal anyone has built and the corpus is green, but its completeness is not established — the durable fix, deciding this on the SAWCore side where it is a sort check rather than a reconstruction of emitted Lean, is scheduled for 0.03.Threat model (
residual-trust.md, "Threat model"): the replay gates defend against mistakes, ours and yours — not against an adversarial proof author. Elaborating a Lean file executes code, so proof files from an untrusted source deserve the same review as any untrusted program, and third-partyLeanReplayEvidenceis a claim to be re-established by re-running replay, not a proof.Audit trail: seven release-gate audit waves plus whole-surface soundness and fidelity reviews, indexed in
saw-core-lean/doc/audit-history.mdanddoc/decision-log.md. One real soundness defect in the trusted support layer was found and fixed this way (bvToIntrealized as signed where SAW's is unsigned), which prompted a 200+-case labeled differential edge-case matrix.Verification layers
cabal test saw-core-lean-smoketest— 94 Tasty tests (translator internals, source lints, fix-recognizer classifier, goal-shape gate).otherTests/saw-core-lean/— data-driven suite run by one orchestrator:drivers/andworkflows/(SAW output diffed against goldens, emitted Lean elaborated),differential/(SAW-vs-Lean observations compared case by case),obligations/,proofs/,support-lemmas/,proof-gaps/(honest inventory of undischargeable obligations),negative/,saw-boundary/(rejection diagnostics),stretch/.saw-core-lean/lean/—lake buildgreen on the pinned toolchain, includingsaw_ctor_orderpositive/negative self-tests,#guard_msgsbehavior fences, andlinter.missingDocsover all public declarations..github/workflows/ci.yml).Reviewing this
The diff is large but concentrated: ~1580 files under
otherTests/saw-core-lean/are test rows and goldens, ~120 are dated design/audit docs, and the translator itself issaw-core-lean/src/plus small touches tosaw-script/src/SAWScript/Interpreter.hs,saw-central, andsaw.cabal. Outsidesaw-core-lean/the footprint is deliberately short pointers.Suggested entry points:
saw-core-lean/README.md(limitations first),saw-core-lean/STATUS.md(what works today),doc/getting-started.md(a 30-minute walkthrough from a Cryptol property to a goal SAW accepts on Lean's authority),doc/architecture.md, anddoc/2026-05-02_residual-trust.mdfor anything soundness-shaped.