From 6f5b8b53d766088c336c8872e7d90cbd3253edf9 Mon Sep 17 00:00:00 2001 From: Doll Date: Wed, 29 Jul 2026 01:18:00 -0500 Subject: [PATCH 1/3] Clarify documentation and complete BitVec reconstruction --- .design/forge/cli.md | 7 +- .design/stage3-bv-reconstruction.md | 73 ++- .design/verified/proof-backends.md | 11 +- .design/verified/thermite-semantics.md | 14 +- .design/verified/z3-demotion.md | 94 ++-- RATIONALE.md | 18 +- conformance/README.md | 93 ++-- conformance/address/README.md | 35 +- conformance/forge/mix64.th | 47 +- conformance/goal/README.md | 83 ++-- conformance/parse/README.md | 40 +- docs/overview.md | 8 +- docs/verification.md | 12 +- examples/README.md | 85 ++-- examples/calculator/README.md | 71 ++- examples/calculator/calc.th | 46 +- examples/editor/README.md | 206 ++++----- examples/editor/editor.th | 375 +++------------- examples/formatter/README.md | 70 +-- examples/formatter/format.th | 28 +- examples/parser/README.md | 75 ++-- examples/parser/parse_lines.th | 46 +- forge/src/accessibility.rs | 18 +- forge/src/audit.rs | 88 ++-- forge/src/battery.rs | 40 +- forge/src/bitvector.rs | 122 ++--- forge/src/body_tv.rs | 14 +- forge/src/build.rs | 8 +- forge/src/burn.rs | 26 +- forge/src/cache.rs | 16 +- forge/src/check.rs | 379 ++++++++-------- forge/src/cli.rs | 100 ++--- forge/src/closure.rs | 8 +- forge/src/contract_tv.rs | 34 +- forge/src/covenant.rs | 14 +- forge/src/covenant_engine.rs | 54 +-- forge/src/covenant_eval.rs | 54 +-- forge/src/degrade.rs | 8 +- forge/src/effect_wrappers.rs | 4 +- forge/src/engine.rs | 263 ++++++----- forge/src/exec_tv.rs | 16 +- forge/src/forks.rs | 18 +- forge/src/goal_repl.rs | 34 +- forge/src/kani.rs | 28 +- forge/src/lean_export.rs | 84 ++-- forge/src/lean_smt_export.rs | 425 ++++++++++-------- forge/src/lemma_library.rs | 68 +-- forge/src/manifest.rs | 52 +-- forge/src/meaning.rs | 16 +- forge/src/metrics.rs | 20 +- forge/src/mutation.rs | 30 +- forge/src/obligation.rs | 44 +- forge/src/profile.rs | 4 +- forge/src/relax.rs | 24 +- forge/src/repair.rs | 6 +- forge/src/review.rs | 18 +- forge/src/sandbox.rs | 8 +- forge/src/seven_verdicts.rs | 26 +- forge/src/strat_faithful.rs | 18 +- forge/src/strat_tv.rs | 24 +- forge/src/strengthen.rs | 14 +- forge/src/tv_signal.rs | 18 +- forge/src/vacuity.rs | 4 +- forge/src/vacuity_solver.rs | 62 +-- forge/src/verdict.rs | 38 +- forge/tests/acceptance_programs.rs | 16 +- forge/tests/battery_conformance.rs | 8 +- forge/tests/body_tv.rs | 2 +- forge/tests/build_conformance.rs | 12 +- forge/tests/bv_lowering.rs | 95 ++-- forge/tests/bv_vacuity_gate.rs | 8 +- forge/tests/cache_conformance.rs | 2 +- forge/tests/check_conformance.rs | 4 +- forge/tests/composition_basis_conformance.rs | 12 +- forge/tests/composition_conformance.rs | 12 +- forge/tests/concurrency.rs | 6 +- forge/tests/contract_tv_conformance.rs | 14 +- forge/tests/covenant_conformance.rs | 8 +- forge/tests/degrade_conformance.rs | 6 +- forge/tests/divergence_249_axiom_mask.rs | 8 +- forge/tests/divergence_251_macro_poison.rs | 2 +- .../divergence_252_indented_command_escape.rs | 2 +- .../divergence_audit_check2_exit_swallow.rs | 10 +- forge/tests/divergence_battery_vacuous.rs | 6 +- forge/tests/divergence_body_tv_frame.rs | 20 +- .../divergence_c12_map_goldens_missing.rs | 2 +- .../divergence_c7_corpus_goldens_missing.rs | 2 +- forge/tests/divergence_collections.rs | 4 +- ...divergence_covenant_assign_to_immutable.rs | 22 +- ...divergence_covenant_bitwise_not_integer.rs | 24 +- ...ivergence_covenant_out_of_range_witness.rs | 26 +- .../divergence_covenant_slag_ordering.rs | 20 +- ...gence_covenant_stmt_if_false_refutation.rs | 28 +- .../divergence_effect_link_string_wrappers.rs | 12 +- .../divergence_harness_names_checked_item.rs | 4 +- forge/tests/divergence_holed_lowering.rs | 4 +- .../tests/divergence_kernel_time_boundary.rs | 4 +- .../tests/divergence_multi_adt_subprogram.rs | 2 +- forge/tests/divergence_mutation.rs | 4 +- .../tests/divergence_numfmt_display_order.rs | 4 +- forge/tests/divergence_provenance.rs | 12 +- forge/tests/divergence_rlimit_phrase_drift.rs | 2 +- forge/tests/divergence_scheme_certs.rs | 4 +- forge/tests/divergence_solver_vacuity.rs | 16 +- .../tests/divergence_spec_call_param_cast.rs | 2 +- forge/tests/divergence_strings.rs | 2 +- forge/tests/divergence_vacuity.rs | 8 +- forge/tests/divergence_vec_push_frame.rs | 2 +- forge/tests/e2e_conformance.rs | 2 +- forge/tests/editor_runs.rs | 14 +- forge/tests/effect_link_conformance.rs | 6 +- forge/tests/effect_stdlib_conformance.rs | 4 +- forge/tests/ergonomics_conformance.rs | 2 +- forge/tests/exec_tv_conformance.rs | 6 +- forge/tests/g1_gate.rs | 8 +- forge/tests/goal_repl.rs | 4 +- forge/tests/goal_repl_fill.rs | 14 +- forge/tests/lean_while.rs | 10 +- forge/tests/literal_layer.rs | 2 +- forge/tests/map_conformance.rs | 20 +- forge/tests/merge_example.rs | 4 +- forge/tests/metrics_dashboard.rs | 24 +- forge/tests/mutual_recursion_conformance.rs | 4 +- forge/tests/nlsat_relax_conformance.rs | 10 +- forge/tests/operators_conformance.rs | 4 +- forge/tests/option_result_conformance.rs | 6 +- forge/tests/profile_conformance.rs | 2 +- forge/tests/proof_fill.rs | 4 +- forge/tests/proof_hole_gate.rs | 8 +- forge/tests/provenance_conformance.rs | 6 +- forge/tests/recursion_conformance.rs | 10 +- forge/tests/req_bounded_mul_conformance.rs | 4 +- forge/tests/restratify.rs | 14 +- forge/tests/sandbox_conformance.rs | 8 +- forge/tests/scratch_cleanup.rs | 2 +- forge/tests/solver_vacuity_conformance.rs | 2 +- forge/tests/spec_fn_string_param.rs | 2 +- forge/tests/strat_differential.rs | 8 +- forge/tests/strengthening_conformance.rs | 6 +- forge/tests/string_format_conformance.rs | 14 +- forge/tests/string_l3_completeness.rs | 22 +- forge/tests/string_search_conformance.rs | 38 +- forge/tests/tuples_conformance.rs | 4 +- forge/tests/vec_completeness_conformance.rs | 2 +- lean/Thermite.lean | 30 +- lean/Thermite/BvModel.lean | 55 +-- lean/Thermite/Exec.lean | 4 +- lean/Thermite/Exec/Loop.lean | 11 +- lean/Thermite/Exec/Stmt.lean | 10 +- lean/Thermite/Faithfulness.lean | 2 +- lean/Thermite/PinBodyRegistry.lean | 2 +- lean/Thermite/PinBrokenLift.lean | 2 +- lean/Thermite/PinCombDeriv.lean | 4 +- lean/Thermite/PinCombPredGap.lean | 20 +- lean/Thermite/PinExecStateMisMap.lean | 2 +- lean/Thermite/PinFiniteEscape.lean | 6 +- lean/Thermite/PinNNFPolarity.lean | 6 +- lean/Thermite/PinRegistryTerminating.lean | 12 +- lean/Thermite/PinRelaxRefute.lean | 6 +- lean/Thermite/PinRestratDropSide.lean | 12 +- lean/Thermite/PinStabilization.lean | 2 +- lean/Thermite/PinStratCapture.lean | 6 +- lean/Thermite/PinStratFlip.lean | 4 +- lean/Thermite/PinStratSelfLoop.lean | 4 +- lean/Thermite/PinWhileComposition.lean | 2 +- lean/Thermite/PinWhileDecShape.lean | 2 +- lean/Thermite/SmtExport.lean | 35 +- lean/Thermite/Soundness.lean | 106 ++--- lean/Thermite/Stabilize.lean | 2 +- lean/Thermite/Strat/Denote.lean | 4 +- lean/Thermite/Strat/Fragment.lean | 2 +- lean/Thermite/Strat/Graph.lean | 8 +- lean/Thermite/Strat/RefEncode.lean | 2 +- lean/Thermite/Strat/Restratify.lean | 16 +- lean/Thermite/Strat/SubstKit.lean | 2 +- lean/lake-manifest.json | 4 +- lean/lakefile.toml | 8 +- scripts/lean-axiom-probe.sh | 10 +- tests/golden/combinators/README.md | 44 +- thermite-lower/src/l1.rs | 8 +- thermite-lower/src/l2.rs | 4 +- thermite-lower/src/lower.rs | 79 ++-- thermite-lower/tests/adt_lower_conformance.rs | 8 +- .../tests/adt_schemes_conformance.rs | 2 +- .../tests/boundary_gate_verified.rs | 4 +- thermite-lower/tests/bytes_eq_conformance.rs | 10 +- .../tests/collections_conformance.rs | 2 +- .../divergence_bytes_eq_l1_empty_window.rs | 4 +- ..._combinator_private_vs_pub_open_spec_fn.rs | 2 +- thermite-lower/tests/divergence_effects.rs | 4 +- .../tests/divergence_golden_intlit.rs | 12 +- .../tests/divergence_intlit_lower.rs | 6 +- thermite-lower/tests/divergence_l1.rs | 4 +- thermite-lower/tests/divergence_lower.rs | 8 +- ...vergence_spec_call_cast_proof_aid_paths.rs | 2 +- .../tests/divergence_struct_inv_cast_lt.rs | 2 +- .../tests/equivalence_obligation.rs | 2 +- thermite-lower/tests/l2_conformance.rs | 4 +- thermite-lower/tests/req_bounded_mul_aid.rs | 4 +- thermite-lower/tests/string_conformance.rs | 2 +- thermite-skill/src/generate.rs | 134 +++--- thermite-skill/src/main.rs | 2 +- .../tests/divergence_unit_arm_example.rs | 24 +- thermite-skill/tests/skill.rs | 14 +- thermite-spec/src/classifier.rs | 30 +- thermite-spec/src/restratify.rs | 50 +-- thermite-spec/src/validator.rs | 30 +- .../tests/divergence_adt_validate.rs | 2 +- .../tests/divergence_c10_guarded_catchall.rs | 4 +- thermite-spec/tests/divergence_final2.rs | 2 +- thermite-spec/tests/divergence_nesting.rs | 2 +- thermite-syntax/src/address.rs | 2 +- thermite-syntax/src/ast.rs | 50 +-- thermite-syntax/src/desugar.rs | 12 +- thermite-syntax/src/lexer.rs | 16 +- thermite-syntax/src/parser.rs | 70 +-- thermite-syntax/tests/bv_tag_parse.rs | 14 +- thermite-syntax/tests/conformance.rs | 2 +- thermite-syntax/tests/dec_forms.rs | 2 +- thermite-syntax/tests/divergence_adt_parse.rs | 4 +- thermite-syntax/tests/divergence_final.rs | 2 +- thermite-syntax/tests/divergence_grammar.rs | 2 +- thermite-syntax/tests/divergence_intlit.rs | 2 +- thermite-syntax/tests/divergence_reaudit.rs | 2 +- thermite-syntax/tests/divergence_reaudit2.rs | 6 +- thermite-syntax/tests/forge_items.rs | 6 +- thermite-syntax/tests/operators_parse.rs | 8 +- thermite-syntax/tests/proof_hole_lex.rs | 2 +- thermite-syntax/tests/quantifiers_parse.rs | 20 +- thermite-syntax/tests/refinement_sugar.rs | 12 +- thermite-tv/examples/strat_probe.rs | 2 +- thermite-tv/src/exec_encode.rs | 16 +- thermite-tv/src/exec_stmt_encode.rs | 50 +-- thermite-tv/src/gen.rs | 36 +- thermite-tv/src/lib.rs | 4 +- thermite-tv/src/obligation.rs | 38 +- thermite-tv/src/ref_encode.rs | 22 +- thermite-tv/src/strat_ref_encode.rs | 6 +- thermite-tv/src/strat_two_phase.rs | 76 ++-- thermite-tv/tests/body_teeth.rs | 27 +- .../tests/divergence_ac4_if_stmt_mutation.rs | 2 +- .../tests/divergence_index_combinator.rs | 2 +- thermite-tv/tests/exec_teeth.rs | 22 +- thermite-tv/tests/loop_teeth.rs | 26 +- thermite-tv/tests/strat_probe.rs | 2 +- thermite-tv/tests/strat_two_phase_corpus.rs | 4 +- thermite-tv/tests/teeth.rs | 16 +- thermite-verified/src/lib.rs | 14 +- tooling/anti-pattern-gate.py | 55 +-- tooling/control-plane-check.py | 108 ++--- tooling/doc-drift.py | 140 ++---- tooling/req-status.py | 4 +- tooling/spec-discipline.py | 53 +-- 253 files changed, 2877 insertions(+), 3645 deletions(-) diff --git a/.design/forge/cli.md b/.design/forge/cli.md index 96a7fff32..02249704c 100644 --- a/.design/forge/cli.md +++ b/.design/forge/cli.md @@ -58,9 +58,10 @@ What the old doc never saw, grouped (each verb cites its issue in the code): - **SMT-tactic obligation export** — `forge smt-export [] [--out ]` (stage-3 REQ-7 / AC-8, #349): the automated Rust→Lean obligation exporter (`Command::SmtExport` → `run_smt_export`, driving `lean_smt_export.rs`). Emits a - `(P_prod) ⟺ (P_ref)` `by smt`-discharged Lean theorem + `#print axioms` probe per - renderable contract clause (QF_LIA, or QF_BV over the bounded-integer model for a - `@bvN` clause in a `bv` build); no file emits the canonical demo batch. + `(P_prod) ⟺ (P_ref)` Lean theorem + `#print axioms` probe per renderable contract + clause. QF_LIA uses `smt`; `@bvN` clauses use literal `BitVec N` terms and + kernel-checked normalization lemmas. With no file, the command emits the canonical + demo batch. ## Requirements diff --git a/.design/stage3-bv-reconstruction.md b/.design/stage3-bv-reconstruction.md index e00bdf523..fff4900b5 100644 --- a/.design/stage3-bv-reconstruction.md +++ b/.design/stage3-bv-reconstruction.md @@ -12,7 +12,7 @@ |---|---|---| | Gate G2 (stage 2 complete, trust flip done) | The per-clause `trust:` migration mechanics this stage reuses; stable schema-v2 certificates | RESOLVED: schema-v2 live (`forge/src/manifest.rs:226-316`); `with_clause_attribution(engine, trust, verdict)` is the migration seam | | Stage-1/2 review telemetry (`forge review`) | Q-BVSCOPE: full / `nowrap`-only / lemma-only | RESOLVED → **full tag + 3 locks**. The "bv-density telemetry" input is circular (no bv corpus can exist pre-ship), so REQ-6's density report becomes the *post-ship* F-F tripwire, not a precondition | -| Lean-SMT / cvc5 replay ecosystem assessment at G2 | Q-RECON: reconstruction engine + fragment support; default-on viability | RESOLVED → **build the Rust→Lean exporter; default-on for QF_LIA + QF_BV**. lean-smt pinned @ `7d1d8239` (vendored cvc5 FFI); `SmtDemo.lean` proves QF_LIA kernel-replay axiom-clean; cvc5 supports QF_BV. The gap was never ecosystem maturity — it is the automated obligation exporter (PoC Tier-3 hand-translation) | +| Lean-SMT / cvc5 replay ecosystem assessment at G2 | Q-RECON: reconstruction engine + fragment support; default-on viability | RESOLVED → **build the Rust→Lean exporter; default-on for QF_LIA + QF_BV**. lean-smt pinned @ `ee6d36b` (vendored cvc5 FFI); `SmtDemo.lean` proves QF_LIA kernel-replay axiom-clean; cvc5 supports QF_BV. The remaining work is the automated obligation exporter and reconstruction coverage. | | `KernelBudget`/`Timeout` telemetry on bv-shaped queries | Whether 64-bit multiplier instances need a dedicated budget profile | RESOLVED → folded into REQ-2: QF_BV 64-bit multiplication is the known cost cliff; it gets a dedicated budget profile and the `Timeout` verdict, never `unknown` | ## Summary @@ -117,15 +117,17 @@ locks shipping inside the same gate as the feature. Umbrella: (full → `nowrap`-only → lemma-only → drop). - REQ-7 (**the Rust→Lean obligation exporter**): build the automated exporter that turns a per-clause obligation into the - `smt`-dischargeable Lean goal `(P_production) ⟺ (P_reference)` — + Lean goal `(P_production) ⟺ (P_reference)` — the step `SmtDemo.lean`'s Tier-3 PoC performs by hand (`lean/Thermite/SmtDemo.lean`, "the hand-translation step is the gap an automated Rust→Lean exporter would close"). The exporter covers the QF_LIA scalar fragment (comparisons + connectives over `int`, the PoC's proven shape) and the QF_BV fragment (the bit-vector - clauses REQ-2 produces). Its output is fed to the lean-smt `smt` - tactic (pinned @ `7d1d8239`, `lean/lakefile.toml`) and the resulting - theorem's `#print axioms` must stay within `{propext, + clauses REQ-2 produces). QF_LIA is discharged by lean-smt's `smt` + tactic (pinned @ `ee6d36b`, `lean/lakefile.toml`). QF_BV is rendered + as literal Lean `BitVec N` terms and discharged with kernel-checked + normalization lemmas, avoiding lean-smt's incomplete bit-blast + reconstructor. Each resulting theorem's `#print axioms` must stay within `{propext, Classical.choice, Quot.sound}` for the fragment to count as reconstruction-supported. - REQ-8 (**reconstruction default-on**): where the obligation's @@ -190,9 +192,10 @@ locks shipping inside the same gate as the feature. Umbrella: known counts; a synthetic density spike trips the named F-F warning. (REQ-6) - [ ] AC-8: The exporter emits a `(P_prod) ⟺ (P_ref)` Lean goal for a - QF_LIA scalar clause AND a QF_BV `@bv` clause; each is discharged by - `smt` and `#print axioms` reports ⊆ `{propext, Classical.choice, - Quot.sound}` (no `Smt`-internal oracle, no `ofReduceBool`). (REQ-7) + QF_LIA scalar clause and a QF_BV `@bv` clause. QF_LIA is discharged + by `smt`; QF_BV uses literal `BitVec N` normalization lemmas. + `#print axioms` reports ⊆ `{propext, Classical.choice, Quot.sound}` + for both (no `Smt`-internal oracle, no `ofReduceBool`). (REQ-7) - [ ] AC-9: A reconstruction-supported clause (QF_LIA or QF_BV)'s certificate shows the kernel-checked `trust:` form while an unsupported clause on the same item retains `solver(z3)`; the audit's @@ -241,7 +244,7 @@ locks shipping inside the same gate as the feature. Umbrella: goldens stay byte-identical and the v1 oracle subset is unchanged for untagged clauses. - **Reconstruction** (`lean/` + `forge`): lean-smt is pinned @ - `7d1d8239` with vendored cvc5 over FFI, toolchain v4.29.0 + Mathlib + `ee6d36b` with vendored cvc5 over FFI, toolchain v4.29.0 + Mathlib (`lean/lakefile.toml`). `lean/Thermite/SmtDemo.lean` already proves the path works and stays axiom-clean for QF_LIA (Tier 2 toy + Tier 3 one TV obligation, both `smt`-discharged, `#print axioms` ⊆ @@ -289,39 +292,27 @@ solver-model-relative is migrated where the reconstruction path supports it and otherwise named honestly in the audit — F-J keeps that free, so G3 does not overclaim closing the entire rel/array gap. -**As-built note (REQ-7 #349, 2026-06-23) — the QF_BV reconstruction -wall and the bounded-int workaround.** D-RECON assumed cvc5's *literal* -QF_BV reconstruction was kernel-clean. It is NOT: at the pinned lean-smt -`7d1d8239`, any `BitVec N`-typed `by smt` goal — even a pure comparison -— bit-blasts through the upstream `Smt/Reconstruct/BitVec/Bitblast.lean` -`sorry` and pulls `sorryAx` (the `z3-demotion.md` wall). REQ-7's -exporter therefore renders QF_BV obligations over a **bounded-integer -machine model** (a `bvN` var → `Int` with `0 ≤ x < 2^N`; a wrap op → -`(a · b) % 2^N`; an unsigned compare → `Int` compare), which `smt` -discharges via clean linear-arith reconstruction — `#print axioms ⊆ -{propext, Classical.choice, Quot.sound}`, verified on all three shipped -theorems. Consequences REQ-8 inherits: +**As-built note (REQ-7 #349, updated 2026-07-28) — literal QF_BV.** +The exporter renders `@bvN` variables and terms directly as Lean +`BitVec N`. It covers wrapping `+`/`-`/`*`, unsigned division and +remainder, bitwise not/and/or/xor, bit-vector shifts, unsigned +comparisons, and boolean connectives. -- **Reconstruction-support = QF_LIA + the *arithmetic/comparison* - subset of QF_BV** (`+`,`-`,`*`, unsigned `=/≠//≥`, logical - connectives). The **bitwise/shift/rotate** subset (`^`,`&`,`|`,`<<`, - `>>`, rotate) is NOT reconstruction-supported — no clean `Int` - encoding exists, and the literal-`BitVec` path hits the `sorry`. The - exporter **refuses** it (`SmtExportError`, a named skip, never a - silent mis-encode). So `mix64`'s `a^b^b==a` and the rotate lemma stay - **solver-trusted** (F-J) — REQ-8's fragment-support check keys on - exactly this renderable/refused split. -- **The `render_bv_prop` faithfulness obligation is largely DISCHARGED, - not deferred.** REQ-7 ships `lean/Thermite/BvModel.lean`: a - kernel-checked, Mathlib-free, core-`BitVec`-only metatheorem - (`tmInt_eq_toNat`, `frmInt_iff_frmBV`, `tv_equiv_faithful`, all - axiom-clean, in `lean-axiom-probe.sh`) proving the bounded-int model - ⇔ the `BitVec` semantics. So the *semantic* gap REQ-8 was to own is - closed for the renderable fragment; the residual REQ-8 still owns is - narrower — the two **string-emission** legs (the Rust→Lean exporter's - pretty-printer and `bitvector.rs`'s SMT renderer both encode the same - `Frm`), a pretty-printer-trust class, not a semantic one. Tracked as - #356. +The exported goal compares the production predicate with +`reference_normalize`. That normalization only changes order duals, +`≠`, and the operand order of addition and multiplication. Lean proves +these equivalences with `simp` and ordinary `BitVec` lemmas, including +when the rewritten terms occur below bitwise, shift, division, or +remainder operations. This avoids both cvc5's partial literal-BitVec +reconstructor and `bv_decide`'s native-reflection axiom. + +The committed `Thermite/SmtExport.lean` includes a fixture using the +complete term surface. Every `#print axioms` result stays within +`{propext, Classical.choice, Quot.sound}`. The older +`Thermite/BvModel.lean` bounded-integer equivalence remains as an +independent semantic cross-check; it is no longer part of the active +export path. The remaining inspection-tier residual is agreement +between the Rust→Lean and SMT-LIB string emitters. Tracked as #356. ## Out of Scope diff --git a/.design/verified/proof-backends.md b/.design/verified/proof-backends.md index ea1fb7736..f5096f708 100644 --- a/.design/verified/proof-backends.md +++ b/.design/verified/proof-backends.md @@ -2110,11 +2110,12 @@ enumeration (R-CHAR-3), never regenerated from the exporter. ACTUAL SHAPE is FUEL-FREE shallow goals** (the #216 reconciliation, §6 tiers (a)/(b)): the exporter emits `denote 0 e env`-style statements for specCall-free obligations (via the fuel-irrelevance lemma) or statically-unfolded goals for non-recursive registries — exactly the QF shape the PoC's - `tv_obligation_*` theorems are, NOT raw `∃N∀fuel` goals. OUT of auto today: the - QF_BV bitwise fragment (blocked by an upstream `sorry` in Lean-SMT's `Bitblast.lean`), the bounded - quantifier combinators (~30% cvc5-rule reconstruction coverage — may FAIL, i.e. `Unknown`, never - unsound), and recursive spec-fns / `permutation_of` (need the `∃N∀fuel` stabilization form + an - induction on the per-env depth — INTERACTIVE only, §6 tier (c)). So the + `tv_obligation_*` theorems are, NOT raw `∃N∀fuel` goals. The stage-3 exporter also + handles the complete QF_BV term surface with literal `BitVec N` normalization proofs. + OUT of auto today: bounded quantifier combinators (~30% cvc5-rule reconstruction + coverage — may fail as `Unknown`) and recursive spec-fns / `permutation_of` (need the + `∃N∀fuel` stabilization form + induction on the per-env depth — INTERACTIVE only, + §6 tier (c)). So the Lean-auto FRAGMENT (REQ-2(a)) is precisely the scalar/linear contract clause exported FUEL-FREE — the "cheapest real win" (increment (ii)). - **(ii) INTERACTIVE** — an agent authors a proof file checked in NEXT TO the source, replayed in CI. diff --git a/.design/verified/thermite-semantics.md b/.design/verified/thermite-semantics.md index 8917209cb..e4c8cbf73 100644 --- a/.design/verified/thermite-semantics.md +++ b/.design/verified/thermite-semantics.md @@ -126,7 +126,7 @@ linker); (3) extraction + the OCaml runtime; (4) Coq itself. Thermite's analogou | 1 | formal semantics of source + target | the Thermite operational semantics `S` + the Verus/Rust target semantics — **THE MOST DELICATE ITEM** (see below) | | 2 | unverified passes (parser/assembler/linker) | the unverified production lowerer `thermite-lower` (replaced per-run by the *verified* validator), plus the Thermite lexer/parser | | 3 | extraction + runtime | the Rust↔Lean encoder-correspondence (the Rust `thermite-tv` code matching the Lean-proved algorithm — see "The Rust↔Lean correspondence gap"), rustc/LLVM/the build chain | -| 4 | Coq itself | **the Lean 4 kernel** + (today) Z3/Verus, with the Lean-SMT cvc5-reconstruction path the route to demote Z3 (finding #8). **Demonstrated (4a, #184):** the QF-linear-integer-arith core of the TV obligation IS now kernel-checkable via Lean-SMT/cvc5 (`#print axioms` = standard only) — a PARTIAL-SCOPE demotion proven; the bitwise fragment (upstream `sorry`), quantified/recursive fragments (coverage), and the end-to-end production wiring (Verus/Z3 emit no certificates; no Rust→Lean exporter yet) remain walls (`.design/verified/z3-demotion.md`). | +| 4 | Coq itself | **the Lean 4 kernel** + (today) Z3/Verus. QF-linear integer obligations are reconstructed through Lean-SMT/cvc5; QF_BV normalization obligations use literal `BitVec N` lemmas. Both report only the standard allowed axioms. Quantified/recursive coverage remains incomplete, and Verus/Z3 do not emit replayable certificates (`.design/verified/z3-demotion.md`). | **The source semantics is the irreducible residue (finding #3, Leroy's item (1)).** `S`'s agreement with the *intended* meaning of Thermite is the single most delicate item — an @@ -665,11 +665,11 @@ open-question #2): - (T2) is RELATIVE, not unconditional: `{Z3 soundness, S = intended meaning}`, lowering-link only, loops kernel-gated, Verus-VC-gen/borrow-checker/LLVM inherited (the reduced-trusted-base framing, Leroy finding #3). -- The Lean-SMT Z3-demotion path is the GOAL, not a present fact: Lean-SMT's cvc5 reconstruction - covers ~30% of cvc5's proof rules today (finding #8), and Verus/Z3 do not emit reconstructable - proofs by default — so TODAY an L3 certificate still enumerates Z3 + Verus. The DEMOTION is the - reason Lean was chosen; whether Lean-SMT is mature enough to actually demote Z3 for OUR VCs is - flagged least-confident (below). +- Z3 demotion is partial. QF_LIA translation obligations are re-discharged through + Lean-SMT/cvc5, and Thermite's QF_BV exporter uses literal `BitVec N` + normalization proofs. Lean-SMT still covers only part of cvc5's proof rules, + especially for quantified obligations, and Verus/Z3 do not emit reconstructable + proofs by default. - This is the soundness ARC's keystone, not its completion. The keystone makes the arch STATEABLE + load-bearing; the increments (#170/#176-#182, #171, #172, #163, #174/#183) BUILT it, in Lean (all SHIPPED-CLOSED, epic #169 COMPLETE). @@ -681,6 +681,6 @@ open-question #2): | REQ-2 (T1 — verified-validator / reference-encoder soundness w.r.t. S) | **SHIPPED (the frozen subset — T1 `⟦R(P)⟧ = ⟦P⟧_S` is kernel-checked for ALL three encoders over the frozen subset: `ref_sound` covers `S_C` 8/8 (#170/#176/#177/#178/#179/#180/#181/#182), `exec_ref_sound` covers `S_E` (#171), `body_ref_sound` covers `S_B` straight-line (#172 + the #186 fix), and `while_rule`/`tv_meta_loop` cover the v1 `while` loop (#163); the residuals OUT of the frozen subset are NAMED — general USER-ADT match/is, and the post-v1 loop shapes)** | T1 is SHIPPED over the frozen subset (the contract sublanguage `S_C` 8/8 COMPLETE under #182; the EXEC-EXPRESSION `S_E` COMPLETE under #171; the exec-BODY `S_B` straight-line COMPLETE under #172 + the #186 fix; the v1 `while` loop COMPLETE under #163). The honest residuals OUTSIDE the frozen subset stay NAMED: general USER-ADT match/is, and the post-v1 loop shapes (loop-kind / break / mid-body return). (T1) `∀ P, ⟦R(P)⟧ = ⟦P⟧_S` is STATED precisely above + written out CONCRETELY for two cases (AC-2). **Proved-so-far:** `theorem ref_sound (fuel : Nat) (e : Expr) (env : Env) : refDenote fuel e env ↔ denote fuel e env` in `lean/Thermite/Soundness.lean` — kernel-checked, the non-spec-fn fragment by structural recursion + the #181 spec-fn calls by WELL-FOUNDED recursion on `(fuel, sizeOf e)` (`termination_by`/`decreasing_by`, core Lean; mutual `ref_sound`/`ref_sound_arms`, fuel-MATCHED in the header for `specCall` so the measure sees the `n+1`→`n` decrease), NON-VACUOUS (`refDenote`/`refIntVal`/`refSeqVal` via the encoder's maps/dispatch vs `denote`/`intVal`/`seqVal`'s source meaning, defined in separate modules), over: (i) **#170** the comparison/logical fragment (`Eq/Ne/Lt/Le/Gt/Ge/And/Or/Not`; the negative `eq_le_infidelity_breaks_soundness` shows an `Eq→<=` map BREAKS soundness — the boss's `==`-vs-`<=` teeth); (ii) **#176** the ARITHMETIC operators (`Add/Sub/Mul/Div/Rem/Shl/Shr/BitAnd/BitOr/BitXor`) over the unbounded-`int` spec domain (NO wraparound — overflow is the exec-side #171 obligation), via the shared `arithDenote` + the `encArith`/`tokArith` operator-map round-trip (`tokArith_encArith`); the PARTIALITY of `Div/Rem/Shl/Shr` (zero divisor/shift) is a SOURCE precondition (L0), modelled with Lean's TOTAL `Int` ops under the divisor-≠0 convention, held CONSISTENT between `denote` and `refDenote` (so T1 is insensitive to the partial point); (iii) **#177** the CASTS (`as u64/u32/usize/nat/int`) via the shared `castDenote` + the `encCast`/`tokCast` cast-target round-trip (`tokCast_encCast`), `as nat` = `Int.toNat` under a `≥0` source frame; (iv) **#178** the SPEC-CONTEXT REWRITES — slice→`@` (`seqVar`/`strVar` denote the SAME sequence — the `@`-view is the identity), `xs[i]`→`xs@[i]` (`Expr.idx` → the shared `seqIdx`), `&xs[..i]`/`&xs[a..b]`/`&xs[a..]`→`xs@.subrange(..)` (`Expr.subrange` + `RangeArg` → the shared `seqSub`), and the #127 byte-view DISPATCH `s.byte_at(i)`→`s.spec_byte_at(i)` / `s.len()`→`s.spec_len()` (`Expr.byteAt`/`Expr.seqLen` → the dispatch token `VerusByteView`/`byteView`, round-trips `byteView_encByteAt`/`byteView_encLen`); the sequence env is `structure Env { ints, seqs }`, the access partiality (`xs[i]` in-range) a SOURCE precondition modelled with total `List.getD`/`take`/`drop`, held consistent across both denotations — all PROVEN denotation-preserving via the combined `refVal_eq`. **#122/#146 RETIRED:** the negative `cast_paren_drop_breaks_soundness` proves a paren-DROPPED encoder (`(n-1) as nat` → `n - 1 as nat` re-parsing as `n - (1 as nat)`) DISAGREES at `n=-1` (`0 ≠ -2`). **#127 RETIRED on the contract side:** the negatives `byteview_wrong_index_breaks_soundness` (a wrong byte-view INDEX `s.spec_byte_at(0+1)` reads byte `20`≠ faithful `10`) and `byteview_misdispatch_breaks_soundness` (a wrong RECEIVER-METHOD — `byte_at` mis-dispatched to `spec_len` — reads length `3`≠ byte `10`) PROVE a faulty byte-view dispatch breaks T1 at a concrete sequence env (`s := [10,20,30]`); the faithful `encByteAt`/`encLen` dispatch is exactly what makes `ref_sound` hold. (v) **#179** the 6 BOUNDED-QUANTIFIER COMBINATORS (`forall_in`/`exists_in`/`sorted`/`forall_below`/`forall_from`/`disjoint`) — each denotes its FROZEN `verus_l3` quantifier form (`thermite-spec/src/combinators.rs`, matched EXACTLY: `forall_in` = `∀i, 0≤i15`; wrong `∃` TRUE — `10≤15`). **#145 arg-kind RETIRED on the contract side:** the negative `index_argkind_slice_view_breaks_soundness` proves slice-`@`-viewing `forall_below`'s `ArgKind::Index` bound `n` (reading `n@.len()`=3 instead of the scalar `n`=1) DISAGREES at `s := [10,20,30]` (faithful scalar-bound TRUE; #145-buggy view-length-bound FALSE — `20>15` at `i=1`); the faithful `encode_index_value` SCALAR threading is exactly what `ref_sound`'s `comb`/`forallBelow` arm pins (via `refIntVal_eq_intVal`). (vi) **#180** the MATCH-IN-ENS / `is` PAYLOAD-IN-CONTRACT forms (the C7 class, `.design/basis/09-option-result.md`) — `match scrut { Some(v) => P(v), None => Q }` (and the `Ok`/`Err` Result form) + `scrut is Some/None/Ok/Err`, FAITHFUL to `thermite-tv/src/ref_encode.rs`'s `encode_match`/`encode_pattern` (the #150 work) + the `Expr::Is` arm: a built-in `Option`/`Result` scrutinee denotes an `OptResVal` (`none`/`some v`/`ok v`/`err e`, the payload an `Int` — the C7 corpus shape; `Denote.lean` `inductive OptResVal` + `Env.optres`), the `match` denotes the arm SELECTED by the scrutinee's variant with the payload BOUND via `Env.bindInt` (`Denote.lean`/`RefEncode.lean` `denoteArms`/`refDenoteArms` — STRUCTURALLY identical, the Verus `match` selection reused verbatim; the soundness content is the scrutinee/body encoding via the SAME recursion + the pattern's variant/binder choice from `encode_pattern`), the `is`-test denotes the variant discriminant (`OptResVal.isVariant`). Proved in the MUTUAL `ref_sound`/`ref_sound_arms` (`Ast.lean` `inductive Variant` + `Expr.{optResVar,match_,is_}` + the mutual `MatchArm`; the `match_` case threads `ref_sound_arms`, the arm-walk soundness via the recursive `ref_sound` IH on each arm body). **#180 match-arm-swap RETIRED:** the negative `match_arm_swap_breaks_soundness` proves a `Some`/`None` arm-body SWAP DISAGREES at `result := Some 7` (source `Some(v) => v==7` TRUE; swapped `Some(v) => false` FALSE). **#180 wrong-`is`-variant RETIRED:** the negative `is_wrong_variant_breaks_soundness` proves `is Some` tested as `is None` DISAGREES at `result := Some 7` (TRUE vs FALSE). Positives `match_faithful_is_sound`/`match_result_faithful_is_sound` (Option AND Result)/`is_faithful_is_sound` confirm the faithful encoder is sound. GENERAL USER ADTs are SCOPED OUT (honest): `encode_pattern`'s `is_builtin_variant` gate `Err`s on a user variant (no enum-qualification map), so user ADTs are OUT of what the encoder produces → not in `S_C` here, DELIBERATELY not embedded (no embed-then-`sorry`). (vii) **#181** the NAMED SPEC-FN CALLS — incl. WELL-FOUNDED RECURSION (the design `⟦Call(f,args)⟧ = ⟦body_of(f)⟧[params ↦ ⟦args⟧]`, "well-defined because §4.2 mandates a `dec` measure"). A `specCall name args` (`ast.rs` `Expr::Call` for a non-combinator/non-`old` callee — `ref_encode.rs::encode_call`'s case (3), which emits `name()` and does NOT inline the body) resolves `name` in a SHARED `Registry` (`Ast.lean` `structure SpecFn { params, body }` + `abbrev Registry := String → Option SpecFn`, carried in `Env.specs`), binds the params to the denoted args (`Env.bindParams`), and denotes the BODY (an `Expr` of the SAME fragment, MAY recurse via further `specCall`s). The well-founded denotation is FUEL-INDEXED (`denote`/`intVal`/`refDenote`/`refIntVal` all take `fuel : Nat`; a `specCall` consumes one unit `fuel+1 → fuel`, a structural subterm keeps the same fuel — well-founded on `(fuel, sizeOf e)`, core Lean, NO Mathlib). This is the FULLY-GENERAL recursive-registry soundness (path 1): `ref_sound` is proved for ALL fuel and ALL registries (arbitrary recursion), with the SOURCE and ENCODER SHARING the fuel + registry — so it is NOT a fuel-cap vacuity dodge (T1 holds at EVERY fuel, including the fuel-`0` shared bottom where both sides denote the IDENTICAL default; the `dec`-bounded source spec fn terminates so a real call always reaches its fixpoint at some fuel). The call-site soundness is the GENERIC theorem "the args agree (the `refIntValArgs_eq`/`refVal_eq` IH, args in order) + the SAME registry resolves the SAME body, denoted at the SAME fuel" — `Ast.lean` `Expr.specCall` + `SpecFn`/`Registry`; `Denote.lean`/`RefEncode.lean` the fuel-indexed `intVal`/`refIntVal`/`denote`/`refDenote` `specCall` arms + `intValArgs`/`refIntValArgs` + `Env.bindParams`; `Soundness.lean` the mutual `refVal_eq`/`refIntValArgs_eq` + the `specCall` cases of `ref_sound`/`refVal_eq`. **#181 wrong-arg-order RETIRED:** the negative `specfn_arg_order_breaks_soundness` proves `sub_fn(b, a)` for source `sub_fn(a, b)` (`sub_fn(p,q) = p-q`, NON-commutative) DISAGREES at `a:=1,b:=2` (faithful `-1` vs swapped `1`). **#181 wrong-resolution RETIRED:** the negative `specfn_wrong_resolution_breaks_soundness` proves resolving the call to `add_fn` where the source resolves to `sub_fn` DISAGREES (`-1` vs `3`). The recursive-denotation NON-VACUITY is witnessed by `specfn_nested_resolution_value` (`g(p) = sub_fn(p,1)`; `g(5)` unfolds through TWO registry entries at fuel `2` to the genuine `4`, NOT the fuel-`0` default `0`) + the positive `specfn_call_faithful_is_sound (fuel : Nat)` (the faithful call sound at EVERY fuel). (viii) **#182** the 2 RECURSIVE/AGGREGATE COMBINATORS — the LAST contract brick, COMPLETING the closed 8-combinator set 8/8: (1) `count_where(s, p)` — a VALUE-combinator (`ResultKind::Usize`, threads `intVal`/`refIntVal` NOT `denote`), the recursive `nat` COUNT, modelled FAITHFULLY to the frozen `verus_l3` (`combinators.rs`, matched EXACTLY: `if s.len()==0 {0} else {(if p(s[0]) {1} else {0}) + count_where(s.drop_first(), p)}`) by the SHARED `countWhereVal` — STRUCTURAL recursion over the source `List` (core Lean, NO Mathlib, NO fuel: the list shrinks by `List.tail` mirroring `drop_first`/`decreases s.len()`), the per-element predicate the closure body via `denote`/`refDenote` at the element (`Env.bindInt`), using `Classical` decidability for the `if p(s[0])` test; (2) `permutation_of(a, b)` — a `Prop`-combinator (two slices, no predicate, like `disjoint`), MULTISET equality `a.to_multiset() == b.to_multiset()` (matched EXACTLY) modelled via the COUNT-CHARACTERIZATION `permEq a b := ∀ x, a.count x = b.count x` (core `List.count` — NOT Mathlib's `Multiset`; this IS multiset equality). Both reuse `Expr.comb` with the new `CombName.{countWhere,permutationOf}`; proved in the MERGED mutual block `refVal_eq`/`refIntValArgs_eq`/`ref_sound`/`ref_sound_arms` (the `count_where` case threads `countWhereVal_congr` + the recursive `ref_sound` IH on the flat closure body; `permutation_of` reduces to the two slices agreeing via `refVal_eq`). **#182 count_where wrong-predicate + off-by-one RETIRED:** the negatives `count_where_wrong_pred_breaks_soundness` (count of `|x| x≤15` over `[10,20,30]` is `1`, of `|x| x≤25` is `2` — DISAGREE) and `count_where_off_by_one_breaks_soundness` (`1 ≠ 1+1`); the genuine recursive count is `count_where_value_is_one` (`1`, NOT a vacuous bottom). **#182 permutation_of MULTISET-vs-SET RETIRED (the KEY fidelity check):** the negative `permutation_set_model_breaks_soundness` proves the CANONICAL witness `a := [1,1,2]`, `b := [1,2,2]` — SAME set `{1,2}` (the SET model `permSetModel` is TRUE) but DIFFERENT multisets (`count 1` is `2` vs `1`, so the faithful `permEq` is FALSE) — DISAGREE, PROVING `permutation_of` is `to_multiset()` equality NOT set equality. Positives `permutation_faithful_is_sound` + `permutation_true_on_real_permutation` (`[1,2,3]`~`[3,1,2]` via `List.Perm.count_eq`) + `count_where_faithful_intval_matches_source` confirm the faithful encoder is sound and the models are non-vacuous. `#print axioms ref_sound → [propext, Classical.choice, Quot.sound]` (no `sorryAx`/custom axiom; `Classical.choice` enters via `count_where`'s decidable predicate test — standard). CORE Lean only (no Mathlib — the expected Mathlib wall did NOT materialize; `List.count`/`List.Perm`/structural recursion sufficed). The contract sublanguage `S_C` is now 8/8 construct classes proven; the remaining `S_C` construct (general user-ADT match/is) is NOT yet embedded (no `sorry`). **(ix) #171 — LAYER 2 OPENED: the EXEC-EXPRESSION sublanguage `S_E` + `exec_ref_value` proven SOUND.** This is a DIFFERENT semantics from `S_C`, kept in a SEPARATE module/namespace `Thermite.Exec` (`lean/Thermite/Exec.lean`): `S_E` is the BOUNDED EXECUTABLE value, NOT unbounded `int`. `theorem exec_ref_sound (e : ExecExpr) (env : ExecEnv) : execRefValue e env = execDenote e env` — kernel-checked by STRUCTURAL recursion over the pure-exec subset (`#print axioms → [propext, Quot.sound]`, no `sorryAx`/custom axiom, CORE Lean only), NON-VACUOUS (`execRefValue` threads each construct through the encoder's `binop_str`/`cast_target` maps — `tokArith ∘ encArith`, `tokCast ∘ encCast` — vs `execDenote`'s source bounded ops `evalArith`/`castVal`, defined independently; the round-trips `tokArith_encArith`/`tokCast_encCast` are the content). FAITHFUL to `thermite-tv/src/exec_encode.rs::exec_ref_value`: the value domain is `ExecVal = .int BVal | .bool Bool` where `BVal { ty : IntTy, value : Int }` carries its type's BOUND `2^width` (the issue's "the Int value together with its type's bound"); int/bool lit, var, arith (`Add/Sub/Mul/Div/Rem/Shl/Shr/BitAnd/BitOr/BitXor`), cmp (`Eq/Ne/Lt/Le/Gt/Ge`), logical (`And/Or`), `!`, cast (`as u8/u16/u32/u64/usize`), slice-index — exactly the nodes `exec_encode.rs::encode` admits. **THE THREE `S_E ≠ S_C` FIDELITY PROPERTIES (the issue title):** (1) **BOUNDED** — a value is well-formed only in `[0, 2^width)` (`BVal.inRange`); (2) **OVERFLOW AS A PROOF OBLIGATION carried alongside the value** — `evalArith op a b : Option BVal` returns `some r` only when the mathematical result is in range (the no-overflow obligation DISCHARGED) and `none` on overflow (the obligation FAILS — the value is not defined, exactly a Verus exec `+` rejected because overflow is possible; the obligation is named `arithObligation`); div/shift-by-zero is a SOURCE precondition (`rawArith → none`); an out-of-range index is the bounds obligation; (3) **NEVER nat-coerced** — a cast WRAPS at the target width (`castVal t v = v.value % 2^t.width`, stays a BOUNDED value), it does NOT inject into an unbounded nat; there is NO `nat`/`int` cast token (`CastTok` = `u8/u16/u32/u64/usize` only). **The OVERFLOW-OBLIGATION treatment is GENUINE (not silently unbounded):** `add_overflow_has_no_value` proves `a + b` with `a = 2^64-1, b = 1` (both `u64`) has `execDenote = none` (a silently-unbounded model would return `some (2^64)`); `add_in_range_has_value` proves the non-overflowing `1+1=2` HAS its value (the partiality is the obligation, not blanket); `encoder_agrees_on_overflow` proves the encoder carries the SAME `none` (neither masks nor invents). **NEGATIVE LEMMA — the "never nat-coerced" discipline PROVEN (mirrors `cast_paren_drop_breaks_soundness`):** `nat_coercion_underflow_breaks_soundness` proves a NAT-COERCED `a - b` (the forbidden encoding `(a-b) as nat`, modelled `subNatCoerced` via `Int.toNat` which CLAMPS the underflow to `0`) produces `some (.int ⟨u64, 0⟩)` at `envUnderflow` (`a:=0, b:=1`, so `0-1=-1` underflows `u64`) while the faithful bounded `S_E` produces `none` (`sub_underflow_has_no_value` — the underflow obligation fails); `some 0 ≠ none`, so a nat-coercing encoder does NOT satisfy `exec_ref_sound`. The positives `sub_faithful_is_sound`/`slice_index_faithful_is_sound`/`slice_index_value_is_twenty` confirm the faithful encoder is sound + the index value is the genuine element `20` (non-vacuous). Honest deferral (no embed-then-`sorry`): method calls / Vec-String accessors (`exec_ref_value` `Err`s — #154/#156), the exec-BODY statement forms (`let`/`if`/mutation) are increment 2b #172 / the v1 loop 2c #163, and a non-path callee / non-slice index / slice-RANGE index (`exec_ref_value` `Err`s) — all OUT of the pure-exec EXPRESSION subset `S_E`, NOT modelled. **(x) #172 — `S_B` SHIPPED:** `theorem body_ref_sound (b : Block) (st : State) : bodyRefState b st = bodyDenote b st` in `lean/Thermite/Exec/Stmt.lean` (axioms `[propext, Quot.sound]`, NO `sorry`), the straight-line state transformer with the obligation-`none` propagating + 3 negative lemmas (`wrong_var_assign_breaks_soundness`/`sequencing_order_breaks_soundness`/`mutation_not_applied_breaks_soundness`) biting; the #186 `ifElse` branch-local-scope divergence was found+fixed+re-verified by the ACToR loop. **(xi) #163 — the v1 `while` loop SHIPPED:** `theorem while_rule` (PARTIAL CORRECTNESS by fuel induction) + `theorem tv_meta_loop` over `def loopDenote` (the fuel-indexed iteration of the SHIPPED `blockThread`) in `lean/Thermite/Exec/Loop.lean` (axioms `[propext, Quot.sound]`), with the L2/L3 negative lemmas biting. **The frozen subset (`S_C ⊔ S_E ⊔ S_B ⊔ S_Loop-v1`) is fully mechanized; the residuals OUT of it stay NAMED:** general USER-ADT match/is, and the post-v1 loop shapes (`loop`-kind / `break`/`continue` / mid-body early `return` / nested / non-scalar mutation — `Unsupported`, honestly Skipped). | | REQ-3 (T2 — semantic preservation, as forward simulation) | **SHIPPED (the T2 CAPSTONE for the straight-line frozen subset; increments (d) #174 + 3b #183, the existential→universal conversion)** | The (T2) META-THEOREM is now MECHANIZED in Lean 4 (`lean/Thermite/Faithfulness.lean`, namespace `Thermite`), composing the three proven (T1) theorems with the per-run TV result. **The TV hypothesis abstraction** `structure FnTvWitness` bundles the per-layer Z3-discharged premises (`h_tv_contract : loweredContract = refDenote fuel contract contractEnv`, `h_tv_body : loweredBody = bodyRefState body bodyState`); `loweredContract`/`loweredBody` are ARBITRARY denotation values standing for the Z3-attested meaning of the UNVERIFIED production lowering — known to Lean ONLY through the Z3 attestation, so `h_tv` is a GENUINE premise, not `True`. **The per-layer meta-theorems** `theorem tv_meta_contract (fuel) (e : Expr) (env) (lowered : Prop) (h_tv : lowered = refDenote fuel e env) : lowered = denote fuel e env := h_tv.trans (ref_sound_eq fuel e env)` (`S_C`), `theorem tv_meta_exec (e : ExecExpr) (env) (lowered : Option ExecVal) (h_tv : lowered = execRefValue e env) : lowered = execDenote e env := h_tv.trans (exec_ref_sound e env)` (`S_E`), `theorem tv_meta_body (b : Block) (st) (lowered : Option ExecVal) (h_tv : lowered = bodyRefState b st) : lowered = bodyDenote b st := h_tv.trans (body_ref_sound b st)` (`S_B`, straight-line) — each the one-line modus-ponens `h_tv.trans (T1)` (AC-3). **The COMPOSED whole-program capstone** `theorem lowering_faithful (w : FnTvWitness) : w.loweredContract = denote w.fuel w.contract w.contractEnv ∧ w.loweredBody = bodyDenote w.body w.bodyState` — a function = a contract (`S_C`) + a straight-line body (`S_B`/`S_E`); the whole lowering is faithful given the per-encoder TV witnesses + the composed (T1). The `∀ w` is REAL (holds for ANY function; the only per-fn input is the Z3-supplied witness) — THE existential→universal conversion: not "there exists a faithful P" but "EVERY P passing TV is faithful", RELATIVE to {Z3 soundness, S = intended meaning, the Lean kernel}. **FORWARD-SIMULATION framing (finding #2):** `lowering_faithful` IS the forward simulation — the denotational equality it establishes is the relation `∼` between the Thermite state and the emitted Verus-Rust target state, preserving the observable effects (the caged contract fragment + the `fx` rows). **NON-VACUITY PROVEN:** `h_tv_is_genuine_premise` shows a `lowered` (a TRUE `Prop` `2=2`) whose `h_tv` against the FALSE encoder meaning of `a==b` at `envAB` is FALSE — so the theorem genuinely CONSUMES the Z3 attestation (it does not certify a `lowered` that disagrees with the reference); `tv_meta_contract_fires_on_faithful_lowering` shows it FIRES on a genuine TV pass. `#print axioms lowering_faithful → [propext, Classical.choice, Quot.sound]` (standard only — NO `sorryAx`, NO custom axiom; `Classical.choice` inherited from `S_C`'s `count_where`). **THE TRUST BOUNDARY is EXPLICIT:** `h_tv` is Z3-DISCHARGED, NOT Lean-proven — increment 4a (#184) demotes Z3 to a kernel-checked Lean-SMT proof (finding #8); until then (T2) is RELATIVE to Z3 soundness. **RESIDUALS NAMED:** loops (#163, kernel-gated — `body_ref_sound` and hence `tv_meta_body`/`lowering_faithful` range over the STRAIGHT-LINE `Block` only); the Z3-demotion (#184, the GOAL not a present fact); the Rust↔Lean encoder-correspondence (#185, audit-by-inspection trusted link). The full trust base + the reduced-trusted-base coverage boundary are enumerated in `Faithfulness.lean`'s doc block (AC-4, Leroy finding #3). | | REQ-4 (the increment roadmap — Lean-targeted, honest, multi-cycle) | SHIPPED | the spec-first ordering table above ((a) #170 → (b) #171 → (c) #172 → (d) #174), all four now Lean 4 proofs, with per-increment rationale + the honest-magnitude statement (≈ one-pass effort, Necula finding #4). The plan is authored AND fully executed — the blockers (#170/#176-#182, #171, #172, #163, #174/#183) are all SHIPPED-CLOSED (epic #169 COMPLETE). Non-doc consumer: the five filed blockers reference back to this doc; the whole frozen-subset spine is now mechanized in Lean 4. | -| REQ-5 (the tooling decision — Lean 4 / Mathlib / Lean-SMT, COMMITTED) | SHIPPED | the decision is RECORDED above with its TCB-shrink rationale (Lean-SMT cvc5 reconstruction = the Z3-demotion path, finding #8) and Coq / Isabelle / Verus-native recorded as considered-and-deferred with one-line reasons. Blocker #173 RESOLVED (the human's call, per the #173 result comment). The DECISION is taken; this is no longer an open fork. **Z3-DEMOTION PoC (increment 4a, #184 — `.design/verified/z3-demotion.md`):** the Lean-SMT dependency now BUILDS with the project (Lake `require smt @ main` → mathlib v4.29.0 + lean-cvc5 vendoring cvc5 1.3.2 + lean-auto; `lean/lean-toolchain` pinned DOWN `v4.30.0`→`v4.29.0` to match Lean-SMT, and the ENTIRE existing proof spine STILL builds green on v4.29.0 — non-negotiable, verified). **TIER 3 reached:** two REAL per-run TV equivalence obligations (the `(P_production) ⟺ (P_reference)` shape of `thermite-tv/src/obligation.rs`) HAND-translated into Lean and discharged by the `smt` tactic (cvc5 reconstruction), KERNEL-CHECKED — `lean/Thermite/SmtDemo.lean` `tv_obligation_arith_cmp`/`tv_obligation_or_le` (+ Tier-2 toys). **HONESTY CRUX:** `#print axioms` on all four = `[propext, Classical.choice, Quot.sound]` (STANDARD ONLY — NO `sorryAx`/cvc5-oracle/`ofReduceBool`; the cvc5 proof is genuinely REPLAYED in the kernel, a real PARTIAL-SCOPE demotion for the QF-linear-integer-arith core, NOT laundering). **WALLS (z3-demotion.md):** Lean-SMT's QF_BV reconstruction carries a `sorry` (`Smt/Reconstruct/BitVec/Bitblast.lean:36` — so a bitwise/shift obligation is NOT kernel-clean, though our int obligations are); ~30% cvc5-rule coverage (quantified combinators weak); Verus/Z3 do NOT emit reconstructable certificates (the demotion must RE-solve via cvc5); the hand-translation residual (a Rust→Lean predicate exporter, #185-adjacent, NOT built here). `lowering_faithful`'s `h_tv` REMAINS Z3-trusted in production — the PoC proves the path is real for the scalar core + pins the walls; it does NOT yet replace `h_tv`'s production source (no overclaim). | +| REQ-5 (the tooling decision — Lean 4 / Mathlib / Lean-SMT, COMMITTED) | SHIPPED | The Lean-SMT dependency builds with the project on Lean v4.29.0 and uses its vendored cvc5. `Thermite/SmtDemo.lean` kernel-checks the QF_LIA proof-of-concept, while `forge/src/lean_smt_export.rs` automates QF_LIA export and covers Thermite's full QF_BV term surface with literal `BitVec N` normalization proofs. The dependency is pinned to `ee6d36b`, which removes the compiled BitVec `sorry`; `lake build` and the axiom probe report only `{propext, Classical.choice, Quot.sound}`. Quantified cvc5 reconstruction remains partial, and Verus/Z3 still do not emit replayable certificates. See `.design/verified/z3-demotion.md`. | | REQ-6 (the Lean project setup — pinned for the builder) | SHIPPED (for the comparison/logical fragment; increment (a) opening move, #170) | The `lean/` project EXISTS and `lake build` kernel-checks clean (NO `sorry`/`axiom`/`admit`/`native_decide`). Files: `lean/lean-toolchain` (`leanprover/lean4:v4.29.0` — pinned DOWN from v4.30.0 in increment 4a #184 to match Lean-SMT; the spine builds green on both, verified), `lean/lakefile.toml` (library `Thermite` + the `require smt` Lean-SMT dependency added in 4a), `lean/Thermite.lean` (root) + the four modules `Thermite/Ast.lean` (the `inductive Expr` for the comparison/logical fragment — `intLit`/`boolLit`/`var`/`cmp`/`logic`/`neg`, mirroring the `BinOp::{Eq,Ne,Lt,Le,Gt,Ge,And,Or}`/`UnaryOp::Not` arms of `thermite-syntax/src/ast.rs`), `Thermite/Denote.lean` (`def denote`/`def intVal` — the source `S_C` meaning), `Thermite/RefEncode.lean` (`def refDenote` via `encOp`/`encLog` mirroring `ref_encode.rs::binop_str`), `Thermite/Soundness.lean` (`theorem ref_sound`/`ref_sound_eq` + the negative `eq_le_infidelity_breaks_soundness` + — #176/#177 — `tokArith_encArith`/`tokCast_encCast`/`refIntVal_eq_intVal` and the cast-paren negative `cast_paren_drop_breaks_soundness`). **#176/#177 EXTENSION:** `Ast.lean` += `inductive ArithOp` (the 10 arithmetic ops) + `inductive CastTy` (`u64/u32/usize/nat/int`) + the `Expr.arith`/`Expr.cast` constructors (mirroring `BinOp::{Add..BitXor}` + `Expr::Cast`/`Type`/`PrimType`); `Denote.lean` += `def arithDenote`/`def castDenote` (the SHARED int-meaning/coercion, routed through by both denotations) and `intVal`'s `arith`/`cast` arms; `RefEncode.lean` += `encArith`/`encCast` (mirroring `ref_encode.rs::binop_str` arithmetic arms + `cast_target`) + `tokArith`/`tokCast` + `refIntVal`'s `arith`/`cast` arms (FAITHFUL to `encode_binary`'s whole-binary paren + `encode_cast`'s `({inner}) as {target}` paren — the #122 discipline). The Rust↔Lean correspondence gap is stated (above). **#178 EXTENSION (the spec-context rewrites):** `Ast.lean` += a MUTUAL `Expr`/`RangeArg` block with the sequence/index/byte-view constructors `seqVar`/`strVar` (a free `&[u32]`-slice / `String`-bytes SEQUENCE name), `idx` (`xs[i]`), `subrange` (`&xs[..i]`/`&xs[a..b]`/`&xs[a..]` via `RangeArg.{rangeTo,range,rangeFrom}`), `seqLen` (`.len()`), `byteAt` (`.byte_at(i)`) — mirroring `Expr::{Index,Ref,MethodCall}`/`IndexArg`; `Denote.lean` += `structure Env { ints, seqs }` (the SEQUENCE env), `seqIdx`/`seqSub` (the shared total access/subrange under the in-range source frame) + a mutual `seqVal`/`intVal` (the source `@`/element/prefix/byte/length meanings); `RefEncode.lean` += `VerusByteView` + `encByteAt`/`encLen`/`byteView` (the #127 DISPATCH as an explicit step) + a mutual `refSeqVal`/`refIntVal` (FAITHFUL to `ref_encode.rs::{encode_slice_arg,encode_index,encode_ref,encode_string_byteview}` — the `@`-view identity, `recv[idx]`, `recv.subrange(..)`, the `spec_byte_at`/`spec_len` dispatch); `Soundness.lean` += the combined `refVal_eq` (mutual structural recursion) + `byteView_encByteAt`/`byteView_encLen` round-trips + the #127 negatives `byteview_wrong_index_breaks_soundness`/`byteview_misdispatch_breaks_soundness` + positive witnesses `byteat_faithful_intval_matches_source`/`subrange_index_faithful_matches_source`. **HONEST DEVIATION from this REQ's dependency list:** still Lean 4 CORE ONLY (no Mathlib, no Lean-SMT) — the arithmetic+cast+rewrite fragment is provable with core `Int`/`Nat`/`Bool`/`Prop`/`List` + `simp`/`rfl`/`cases` (the byte-view uses core `List.getD`/`take`/`drop`/`length`, no Mathlib needed); Mathlib/Lean-SMT are added when a later increment's proof (the recursive combinators `count_where`/`permutation_of`, #182) genuinely needs them. **#179 EXTENSION (the 6 bounded-quantifier combinators):** `Ast.lean` += `inductive CombName` (the 6 frozen bounded combinator names — the 2 recursive `count_where`/`permutation_of` DELIBERATELY ABSENT, #182) + a MUTUAL `Pred` (the flat `|x| ` closure) + the `Expr.comb` constructor (carrying the slice / optional second slice / optional SCALAR index / optional predicate per `CombinatorSig.arg_kinds`); `Denote.lean` += `Env.bindInt` (the SHARED predicate-at-element env update) + `denote`'s `comb` arm (the frozen `verus_l3` quantifier forms); `RefEncode.lean` += `refDenote`'s `comb` arm (the SAME quantifier form, args threaded per ARG-KIND — `refSeqVal` Slice / `refIntVal` SCALAR Index #145 / `refDenote`-of-body Pred — FAITHFUL to `encode_combinator_call`/`encode_combinator_arg`); `Soundness.lean` += the `comb` case of `ref_sound`/`refVal_eq` + the negatives `wrong_combinator_breaks_soundness` (the wrong `∀`-vs-`∃` combinator) and `index_argkind_slice_view_breaks_soundness` (the #145 slice-viewed Index arg) + the positive witness `forall_below_faithful_is_sound`. **#180 EXTENSION (the C7 match-in-ens / `is` payload-in-contract forms):** `Ast.lean` += `inductive Variant` (the 4 built-in `Some/None/Ok/Err` — `ref_encode.rs::is_builtin_variant`; user variants DELIBERATELY ABSENT) + the `Expr.{optResVar,match_,is_}` constructors + a MUTUAL `MatchArm` (the variant pattern + optional payload binder + body — `Pattern::Enum` RESTRICTED to the built-in payload patterns); `Denote.lean` += `inductive OptResVal` (the `none`/`some v`/`ok v`/`err e` scrutinee value, the payload an `Int`) + `Env.optres` + `OptResVal.{variant,payload,isVariant}` (the shared Verus match/is discriminant) + `scrutVal` + a MUTUAL `denote`/`denoteArms` (the arm SELECTION + payload BINDING); `RefEncode.lean` += a MUTUAL `refDenote`/`refDenoteArms` (the SAME arm-selection structure — the Verus `match` reused verbatim — with each body via the encoder's `refDenote`; the `is`-test via `isVariant`, FAITHFUL to `encode_match`/`encode_pattern` + the `Expr::Is` arm); `Soundness.lean` += a MUTUAL `ref_sound`/`ref_sound_arms` (the `match_` case threads `ref_sound_arms`, the arm-walk soundness via the recursive `ref_sound` IH on each arm body; the `is_` case is the shared discriminant) + the negatives `match_arm_swap_breaks_soundness` (a `Some`/`None` body SWAP) and `is_wrong_variant_breaks_soundness` (`is Some` tested as `is None`) + the positives `match_faithful_is_sound`/`match_result_faithful_is_sound` (Option AND Result)/`is_faithful_is_sound`. Still Lean 4 CORE ONLY (the match/is fragment is provable with core inductives + `cases`/`by_cases`/`simp` — no Mathlib). **#181 EXTENSION (the named spec-fn calls — incl. well-founded recursion, increment 1e):** `Ast.lean` += `Expr.specCall (name : String) (args : List Expr)` (mirroring `Expr::Call` for a non-combinator/non-`old` callee) + `structure SpecFn { params : List String, body : Expr }` + `abbrev Registry := String → Option SpecFn` (the SHARED spec-fn registry, the external ground truth like the combinator `lookup`); `Denote.lean` += `Env.specs : Registry` + `Env.bindParams` (bind params to denoted args) + the FUEL-INDEXED mutual `seqVal`/`intVal`/`intValArgs`/`denote`/`denoteArms` (a `specCall` resolves `name` in `Env.specs`, binds params, denotes the body at the CONSUMED fuel; the well-founded `(fuel, sizeOf e)` recursion is auto-derived); `RefEncode.lean` += the fuel-indexed mutual `refIntVal`/`refSeqVal`/`refIntValArgs`/`refDenote`/`refDenoteArms` (the `specCall` arm FAITHFUL to `encode_call`'s case (3) — `name()`, NOT inlined; the body is the shared registry entry); `Soundness.lean` += the fuel-indexed mutual `refVal_eq`/`refIntValArgs_eq` (`termination_by (fuel, sizeOf …)`/`decreasing_by Prod.Lex`) + the `specCall` cases of `ref_sound`/`refVal_eq` (fuel-MATCHED in the header so the measure sees the `n+1`→`n` decrease) + the negatives `specfn_arg_order_breaks_soundness` (wrong arg order, non-commutative `sub_fn`) and `specfn_wrong_resolution_breaks_soundness` (call resolves to the wrong spec fn) + the recursive-denotation witness `specfn_nested_resolution_value` (`g(5)` unfolds to `4` at fuel `2`, NOT the fuel-`0` bottom) + the positive `specfn_call_faithful_is_sound (fuel : Nat)`. Scoping PATH 1 (fully-general recursive registry): `ref_sound` is proved for ALL fuel + ALL registries (arbitrary recursion) — the fuel-indexing is NOT a vacuity dodge (source + encoder share the fuel; T1 is fuel-uniform). Still Lean 4 CORE ONLY (the well-founded recursion is core `termination_by`/`decreasing_by`; no Mathlib). **#182 EXTENSION (the 2 recursive/aggregate combinators — the LAST contract brick, 8/8 complete, increment 1d-ii):** `Ast.lean` += `CombName.{countWhere,permutationOf}` (the 8-combinator set now COMPLETE; `count_where` documented as VALUE-sorted, `permutation_of` as `Prop`/multiset, both reusing `Expr.comb`); `Denote.lean` += `noncomputable def countWhereVal` (structural recursion over `List Int`, faithful to the recursive `verus_l3`, `Classical` decidability for the `if p(s[0])` test) + `countWhereVal_cons` + `def permEq` (the count-characterization `∀ x, a.count x = b.count x` of multiset equality) + `intVal`'s `comb CombName.countWhere` arm (the value side) + `denote`'s `comb` `permutationOf`/`countWhere` arms (and EXPLICIT `termination_by (fuel, sizeOf …)` added to the now-`noncomputable` block); `RefEncode.lean` += the SAME `comb` arms (the two mutual blocks MERGED into one so `refIntVal`'s `count_where` arm can reference `refDenote` for the predicate; all `noncomputable`, explicit `termination_by`); `Soundness.lean` += `theorem countWhereVal_congr` (the count depends on the predicate only through its truth at each element) + the `count_where`/`permutationOf` arms of the now-MERGED `refVal_eq`/`ref_sound` block (the `count_where` case threads `countWhereVal_congr` + the recursive `ref_sound` IH on the flat closure body) + the negatives `count_where_wrong_pred_breaks_soundness`/`count_where_off_by_one_breaks_soundness` (wrong predicate / off-by-one count) and `permutation_set_model_breaks_soundness` (the MULTISET-vs-SET teeth, canonical witness `[1,1,2]`/`[1,2,2]`) + the positives `count_where_value_is_one`/`count_where_faithful_intval_matches_source`/`permutation_faithful_is_sound`/`permutation_true_on_real_permutation`. CORE Lean only — the expected Mathlib wall did NOT materialize (`List.count`/`List.Perm`/structural recursion sufficed); `Classical.choice` enters the axiom set via `count_where`'s decidable predicate test (standard, sanctioned). The remaining deferred AST construct (general USER-ADT match/is beyond the built-in Option/Result) is listed in `lean/Thermite/Ast.lean` as a future sub-increment — NOT embedded-then-`sorry` (forbidden), simply left out of the inductive. | | REQ-7 (the architecture decision — a verified validator) | SHIPPED | the decision is RECORDED above (REQ-7 section): a VERIFIED VALIDATOR (prove `R` sound = T1), NOT a verified lowerer, NOT a full universal simulation proof — Leroy finding #1 + Necula finding #4. This resolves the doc's own open architectural question (research open-question #2). Recorded in #173's result comment. Non-doc consumer: the #173 resolution + the increment blockers #170–#174 build to this architecture. | diff --git a/.design/verified/z3-demotion.md b/.design/verified/z3-demotion.md index 2f287c710..c4e08f2c4 100644 --- a/.design/verified/z3-demotion.md +++ b/.design/verified/z3-demotion.md @@ -65,8 +65,8 @@ not in the trust base for these obligations; only the Lean kernel + the standard ## The dependency / toolchain story (Tier 2 — the dep BUILDS, spine stays green) -- **Lean-SMT** has NO release tags; it is pinned by toolchain. `main` - (`7d1d823`) requires **`leanprover/lean4:v4.29.0`** + full **Mathlib v4.29.0** + +- **Lean-SMT** has no release tags. Thermite pins upstream PR #227 + (`ee6d36b`), which requires **`leanprover/lean4:v4.29.0`** + full **Mathlib v4.29.0** + **lean-auto** (`5c4433f`) + **lean-cvc5** (`abdoo8080/lean-cvc5` @ `4ecae27`). - **lean-cvc5** downloads a **VENDORED cvc5 1.3.2 static library** (from `abdoo8080/cvc5/releases`, Linux-x86_64-static) and builds an FFI binding. It does NOT @@ -92,51 +92,29 @@ not in the trust base for these obligations; only the Lean kernel + the standard The per-run TV obligation is `lower(P) ⟺ ref(P)` as a Verus/SMT query. For the CAGED contract sublanguage the relevant SMT fragments are: -| Clause family (`gen.rs`/`obligation.rs`) | SMT fragment | Lean-SMT reconstructable? | +| Clause family (`gen.rs`/`obligation.rs`) | SMT fragment | Kernel-checked export? | |---|---|---| | scalar comparisons + logical connectives (`==`/`<=`/`&&`/`||`/`!`) | QF_LIA / QF_UF | **YES** — kernel-clean (the two Tier-3 obligations) | | integer arithmetic (`+`/`-`/`*`-by-literal, linear) | QF_LIA | **YES** (linear); nonlinear `*` is solver-incomplete (not a reconstruction wall, a solver one) | | bounded quantifier combinators (`forall_in`/`sorted`/…) | quantified (UF + LIA + arrays) | PARTIAL — quantifier-instantiation rules have weaker reconstruction coverage (~30% of cvc5 rules overall, finding #8); not exercised here | -| bitwise / shift ops (`&`/`|`/`^`/`<<`) | QF_BV | **NO (kernel-clean)** — see the BitVec wall below | +| bitwise / shift ops (`&`/`|`/`^`/`<<`) | QF_BV | **YES** — literal `BitVec N` normalization proof | | `spec_sum`/recursive spec fns, `permutation_of` | UF + recursion / multiset | OUT — needs the recursive definition exported as an SMT axiomatization; not a single reconstructable query | -So **the scalar/linear-arithmetic core of the contract obligation is within Lean-SMT's -reconstructable subset TODAY** (demonstrated, kernel-clean). The richer fragments are not. +The scalar/linear core uses Lean-SMT. QF_BV takes a separate literal-`BitVec` path in +Thermite's exporter. Quantified and recursive fragments remain outside the automated path. ## THE EXACT WALLS -1. **The BitVec-reconstruction `sorry`.** Building Lean-SMT emits: - `warning: Smt/Reconstruct/BitVec/Bitblast.lean:36:4: declaration uses 'sorry'`. - The bit-vector (QF_BV) proof-reconstruction path in Lean-SMT itself contains a `sorry`. - It is NOT pulled into our integer obligations (their axiom sets are clean — verified by - `#print axioms`), but it means a **bitwise/shift TV obligation (`&`/`|`/`^`/`<<`, the - `gen.rs` exec-side surface) would NOT be kernel-clean** — its reconstruction would route - through the `sorry`. This is a hard wall for the bitwise fragment until upstream closes - it. - **UPDATE (stage-3 REQ-7, #349):** empirically reconfirmed at the pinned rev and found - to be WIDER than "bitwise/shift only" — *every* `BitVec`-typed `by smt` goal, including - a pure unsigned comparison `a ≤ b ↔ ¬ (b < a)` over `BitVec 8`, bit-blasts and pulls - `sorryAx`. Root cause (read in the vendored source): `reconstructRewrite` is a total - stub (`| _ => return none` for every cvc5 DSL rewrite) and `BV_BITBLAST_STEP` covers - only `EQUAL`/`BITVECTOR_ADD`; closing the `eq_eq_beq` sorry alone (verified, = upstream - PR ufmg-smite/lean-smt#227) is necessary but NOT sufficient. The stage-3 exporter - therefore renders a `@bvN` clause over the **range-bounded integer machine-model** - (`0 ≤ x < 2^N`, wrap as `% 2^N`, unsigned cmp as `Int` cmp), which IS kernel-clean. - - **UPDATE (stage-3 "Path B", #356) — the `render_bv_prop` gap is now CLOSED in our own - spine, no upstream dependency.** Rather than complete lean-smt's literal bv - reconstruction (weeks–months, fork-owning), `lean/Thermite/BvModel.lean` proves the - integer machine-model FAITHFUL to the `BitVec N` semantics: `frmInt_iff_frmBV` - (the two denotations agree) + `tv_equiv_faithful` (so the exporter's int-model `↔` - certifies the genuine `@bv` clause), KERNEL-CHECKED, `#print axioms` ⊆ {propext, - Classical.choice, Quot.sound}, and **Mathlib/Smt-free** (so it runs in CI via - `scripts/lean-axiom-probe.sh`, unlike the `Smt`-importing `SmtExport`). Combined with - the exporter's `by smt`-reconstructed int-model `↔`, a `@bv` clause's truth is - kernel-grounded end to end with no solver in the trust base for the renderable - fragment. This is the route that retires the bit-blasting wall for our purposes — - completing lean-smt's literal QF_BV reconstruction (the `reconstructRewrite` stub + - the missing `BV_BITBLAST_STEP` arms) remains the open UPSTREAM task, not load-bearing - for us. +1. **Lean-SMT's cvc5 BitVec reconstructor remains partial, but Thermite no longer uses + it for QF_BV.** The pinned revision includes the proof of `BitVec.eq_eq_beq`, so the + dependency build has no `sorry` warning. Thermite's exporter renders the complete + fixed-width term surface directly as Lean `BitVec N`. Its production/reference + normalization needs only order duality, `≠` expansion, and commutativity, all proved + with ordinary kernel-checked lemmas. The full-term fixture's `#print axioms` stays + within `{propext, Classical.choice, Quot.sound}`. + + `lean/Thermite/BvModel.lean` keeps the former bounded-integer equivalence as an + independent cross-check. It is no longer part of the active export path. 2. **Coverage ~30% of cvc5's proof rules (finding #8).** Quantified obligations (the bounded combinators) and theory-lemma-heavy proofs may hit an unreconstructable cvc5 rule and FAIL (the `smt` tactic errors rather than producing an unsound proof — it does @@ -157,37 +135,35 @@ reconstructable subset TODAY** (demonstrated, kernel-clean). The richer fragment exporter** that parses both emitted predicate strings into Lean `Prop`s over the typed env the obligation frame declares. The LOGICAL CONTENT discharged is exactly the per-run obligation; the residual is the parse/translate step. - **UPDATE (stage-3 REQ-7, #349 — this gap is now CLOSED for the scalar/bv-model - fragment).** `forge/src/lean_smt_export.rs` is the automated Rust→Lean obligation + **UPDATE (stage-3 REQ-7, #349).** `forge/src/lean_smt_export.rs` is the automated + Rust→Lean obligation exporter (`forge smt-export`): it renders a Thermite predicate `Expr` into a Lean - `Prop` and emits the `(P_prod) ⟺ (P_ref)` theorem `by smt` + `#print axioms`. The - committed `lean/Thermite/SmtExport.lean` is its verbatim output for one QF_LIA scalar - clause and two QF_BV `@bv` clauses, all three `#print axioms` ⊆ `{propext, - Classical.choice, Quot.sound}` (verified by `lake build`). The hand-translation step - `SmtDemo.lean` performed is now mechanical. + `Prop` and emits the `(P_prod) ⟺ (P_ref)` theorem plus `#print axioms`. QF_LIA uses + `smt`; QF_BV uses literal `BitVec N` lemmas. The committed module contains one QF_LIA + and three QF_BV fixtures, including the complete term surface. All four axiom reports + stay within `{propext, Classical.choice, Quot.sound}`. ## The upstream asks (what would have to change) -- **Lean-SMT:** close the `Smt/Reconstruct/BitVec/Bitblast.lean` `sorry` (kernel-clean - QF_BV reconstruction) and raise proof-rule coverage above ~30% (esp. quantifier - instantiation, for the bounded combinators). A release-tagged, toolchain-current - (`v4.30.0`+) line would also remove our forced downgrade. +- **Lean-SMT:** raise proof-rule coverage above ~30%, especially quantifier + instantiation for the bounded combinators. Complete cvc5 BitVec reconstruction + would still help other users, but Thermite's QF_BV exporter no longer depends on it. - **Verus / Z3:** emit a **reconstructable proof certificate** for a discharged VC (proof logging Lean-SMT/SMTCoq can replay). Until then the demotion must RE-SOLVE the obligation through cvc5 rather than reuse the Verus/Z3 attestation. -- **Thermite (us):** a Rust→Lean predicate exporter (parse the two emitted Verus predicate - strings → Lean `Prop`s under the obligation frame's typed env) to remove the - hand-translation step — future work, NOT this increment. +- **Thermite:** widen the exporter to quantified and recursive obligation shapes while + preserving the typed environment and stabilization evidence. ## Honest assessment — when does FULL demotion become practical? -- **Today (this increment):** the SCALAR / QF-linear-integer core of the contract TV - obligation can be RE-discharged by cvc5 and kernel-checked with the standard axioms — a - REAL but PARTIAL-SCOPE demotion (proven, not asserted). The bitwise fragment is blocked - by an upstream `sorry`; quantified/recursive fragments by coverage; the end-to-end path - by Verus/Z3 not emitting certificates + the missing exporter. -- **Practical full demotion** needs THREE things to land: (1) Lean-SMT QF_BV `sorry` closed - + quantifier-rule coverage raised; (2) Verus/Z3 proof-logging OR an accepted policy of +- **Today (this increment):** the scalar QF_LIA core can be re-discharged by cvc5 + and kernel-checked with the standard axioms. Thermite's literal `BitVec` path covers + its complete QF_BV term surface with ordinary Lean lemmas. Quantified and recursive + fragments remain limited by reconstruction coverage, and Verus/Z3 still do not emit + replayable certificates. +- **Practical full demotion** needs three things: (1) broader Lean-SMT + quantifier-rule coverage; (2) Verus/Z3 proof-logging or + an accepted policy of re-solving every TV obligation through cvc5 (a latency cost — every L3 program's TV runs twice); (3) the Rust→Lean exporter. None is fundamental; all are engineering + upstream maturation. Realistically this is a multi-cycle, partly-upstream-gated effort — exactly diff --git a/RATIONALE.md b/RATIONALE.md index 1e1ae2889..27a7823be 100644 --- a/RATIONALE.md +++ b/RATIONALE.md @@ -569,11 +569,12 @@ the scalar/QF-linear-integer contract fragment were re-discharged by cvc5 and **kernel-checked** with the standard axioms only (`#print axioms` → `{propext, Classical.choice, Quot.sound}`, no `sorryAx`, no cvc5 oracle axiom; a partial-scope demotion rather than laundering). The remaining obstacles to *full* demotion: an -upstream Lean-SMT `sorry` in the QF_BV (bitwise) reconstruction; ~30% cvc5 -proof-rule coverage (quantified/recursive fragments); Verus/Z3 not emitting -reconstructable certificates (requiring the obligation to be re-solved through -cvc5); and a missing Rust→Lean predicate exporter. These are engineering and -upstream-maturation items rather than feasibility ones, documented in +upstream Lean-SMT reconstructor that only partially covers QF_BV; ~30% cvc5 +proof-rule coverage (especially for quantified/recursive fragments); and Verus/Z3 +not emitting reconstructable certificates. Thermite now has a Rust→Lean exporter +for QF_LIA and its complete QF_BV term surface. The latter uses literal `BitVec N` +normalization lemmas instead of the partial upstream bit-blast route. The remaining +work is documented in [`.design/verified/z3-demotion.md`](.design/verified/z3-demotion.md). **Why Lean 4 (and not Coq / Isabelle / Verus-native).** The live route to demote @@ -837,9 +838,10 @@ and check [1]'s coverage grows. the exporter stops at straight-line bodies today (`while` is next — the spine's rule is already proven). [`.design/verified/proof-backends.md`](.design/verified/proof-backends.md). -- **Full Z3 demotion**: close the upstream Lean-SMT QF_BV `sorry`, raise cvc5 - proof-rule coverage, and re-solve TV obligations through cvc5 so `h_tv` becomes - kernel-checked rather than Z3-trusted. +- **Full Z3 demotion**: literal QF_BV normalization is now kernel-checked in + Thermite. The remaining work is wider cvc5 proof-rule coverage and replayable + evidence for the quantified/recursive TV obligations, so `h_tv` no longer + depends on Z3. [`.design/verified/z3-demotion.md`](.design/verified/z3-demotion.md) (upstream-gated; the scalar core is already a proven PoC). - **The extraction bridge**: a mechanized Lean→Rust extraction (or a Rust-side diff --git a/conformance/README.md b/conformance/README.md index 453c4e17f..bc7c4b911 100644 --- a/conformance/README.md +++ b/conformance/README.md @@ -1,73 +1,44 @@ -# Thermite conformance corpus +# Conformance corpus -This directory is the **cert oracle**: the external truth the `acto-critic` -anchors divergence claims to, in place of the upstream a translation fork -would have (see `goal.md` → "Why the critic still has teeth without an -upstream"). +The conformance corpus provides hand-authored inputs and expected results for +the Thermite toolchain. Tests compare implementation output with these fixtures; +they do not regenerate the expected values from the code under test. ## Layout -``` -conformance/ - .th a Thermite program (the input to `forge check`) - .cert.json the GOLDEN certificate `forge check .th` must emit - README.md this file -``` +The root contains representative `.th` programs. Depending on the component, +their expected results live in one of several forms: -A future `tests/golden/lower/.verus.rs` holds the golden **lowering** -(the exact Verus source `thermite-lower` must emit). Those are NOT authored -yet — see "Forward-declared" below. +- `.cert.json` records the stable subset of a `forge check` certificate. +- `parse/*.facts.json` records parser-level structure. +- `address/*.addresses.json` records semantic addresses and error cases. +- Subdirectories such as `build/`, `mutation/`, and `sandbox/` contain focused + JSON case sets. +- `tests/golden/lower/` contains expected Verus or L1 lowering output. -## The cert-oracle contract +## Fixture rules -For each `.th` with a golden `.cert.json`, `forge check` must -emit a certificate whose fields **match the golden cert**, with two rules: +Expected values come from the language and component designs, or are derived by +hand from the source program. They must not be copied from the implementation's +current output (goal.md R-CHAR-3). -1. **Deterministic subset only.** Non-deterministic fields (e.g. - `solver_time_ms`) are EXCLUDED from comparison. The design's Appendix A - shows `solver_time_ms: 612` as illustrative; it is not asserted. Builds - are bit-reproducible given a pinned seed (design §5.3), but wall-clock - solver timing is not, so it never appears in a golden cert. +Golden certificates contain deterministic fields only. Measurements such as +`solver_time_ms` are excluded. A fixture may describe a field before its +producer ships; tests begin comparing that field when the component is +implemented. -2. **Forward-declared fields.** A golden cert may assert a field that no - shipped component produces yet (e.g. `mutants_killed`, produced by the - mutation scorer, issue #12 / v0.3). Until that component ships, the - cert-oracle compares only the fields the toolchain actually emits. Each - field becomes a LIVE assertion when its producing component lands. The - golden cert is the target; the toolchain grows into it. +Not every source program has every kind of fixture. For example, +`binary_search.th` is useful to the parser and lowerer without a committed +certificate for every toolchain stage. -Expected values trace to `thermite-design.md` or are hand-derived from it — -**never** copied from `forge`'s own output (`goal.md` R-CHAR-3). A test that -asserts the toolchain's output equals itself is itself a divergence. +## Consumers -## Current entries +- `thermite-syntax` uses source programs, parse facts, and address fixtures. +- `thermite-spec` uses focused accept/reject case sets. +- `thermite-lower` compares generated code with the golden lowerings. +- `forge` compares certificates and component behavior with the relevant JSON + fixtures. -| Program | Source | Golden cert | Provenance | -|---|---|---|---| -| `sum.th` | verbatim | `sum.cert.json` | `thermite-design.md` Appendix A (program + certificate excerpt) | -| `binary_search.th` | verbatim | — (not yet) | `thermite-design.md` §4.1 (program) | - -`binary_search` has no golden cert yet: the design gives its program but not -a certificate, and its `mutants_killed` value is not specified. Authoring a -fabricated cert would create a false anchor (`goal.md` R-CHAR-3), so its -golden cert is deferred until the certificate-manifest contract -(`.design/forge/certificate-manifest.md`) and the mutation scorer (#12) pin -the missing fields. Until then `binary_search.th` is used as a parser / -lowering fixture only. - -## Forward-declared (do not fabricate) - -- `tests/golden/lower/.verus.rs` — exact Verus lowering. Authored - alongside `.design/lower/verus-lowering.md` (issue #4), not guessed now. -- Full `*.cert.json` schema — pinned by `.design/forge/certificate-manifest.md`. -- `binary_search.cert.json` — pending the above + mutation scorer (#12). - -## How the corpus is consumed - -- **thermite-syntax / parser (#3)** — `*.th` are parse fixtures (round-trip, - per-item recovery, semantic addressing). -- **thermite-lower (#4)** — `*.th` lower to the golden Verus files (once - authored). -- **forge check (#5)** — `forge check .th` emits a cert compared to - `.cert.json` under the contract above. This is the gate referenced - by `goal.md` R-DEFER-6. +When behavior changes intentionally, update the governing design first and then +update the fixture from that design. A failing test should not be fixed by +copying newly emitted output into the oracle. diff --git a/conformance/address/README.md b/conformance/address/README.md index 097a73655..ee3ef05e8 100644 --- a/conformance/address/README.md +++ b/conformance/address/README.md @@ -1,26 +1,19 @@ -# Address-resolution oracle (`conformance/address/`) +# Semantic-address fixtures -The hand-derived expected semantic addresses for the corpus programs — the -external anchor for `thermite-syntax/src/address.rs` (issue #3), per +Each `.addresses.json` file describes the addresses expected for +`conformance/.th`. These fixtures test +`thermite-syntax/src/address.rs` against the scheme defined in `.design/syntax/semantic-addressing.md`. -## Contract +The `addresses` array lists valid addresses in document order. Entries for +`inv` and `dec` nodes also include the source text that the address must resolve +to. The `must_error` array contains invalid or out-of-range addresses that must +produce a structured error. -Each `.addresses.json` lists, for `conformance/.th`: +Addresses are one-based and follow source order within their enclosing item. +For `binary_search`, `inv#2` resolves to `forall_below` and `inv#3` resolves to +`forall_from`. This corrects the reversed labels in the illustrative +`thermite-design.md` §4.3 example. -- `addresses[]` — every valid address in the program, in document order. For - `inv`/`dec` nodes, `text` is the source text the address must resolve to. -- `must_error[]` — address strings that MUST resolve to a structured error - (never a panic — R-CODE-2), e.g. out-of-range ordinals or unknown names. - -The address scheme (1-based, source-order, structural/positional within the -enclosing item) is defined in `.design/syntax/semantic-addressing.md`. The -`inv#2`/`inv#3` resolutions encode the resolution of blocker #26: **`inv#2` is -`forall_below`, `inv#3` is `forall_from`** (source order; the thesis §4.3 -`forge edit` example mislabels this and is an erratum). - -## R-CHAR-3 - -Expected values here are derived from `thermite-design.md` §4.3 + the verbatim -`.th` source. They are NEVER copied from `address.rs`'s output. A test that -asserts `address.rs` agrees with itself is itself a divergence. +Expected values are derived from the source programs and the design, not from +the address resolver's output (goal.md R-CHAR-3). diff --git a/conformance/forge/mix64.th b/conformance/forge/mix64.th index e4ebfaa7f..baf53a894 100644 --- a/conformance/forge/mix64.th +++ b/conformance/forge/mix64.th @@ -1,36 +1,12 @@ -// The `mix64` example (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-2 — the -// stage-3 bit-vector route's centerpiece). A SINGLE `fn` whose certificate exhibits the -// RFC's MIX64 shape: wraparound (`@bv64`) clauses and an unbounded clause side by side, -// each lowered through the engine that grounds it and labeled per clause. +// One function with two fixed-width clauses and one unbounded clause. // -// * `ens@bv64 a + b == b + a` — wraparound-add COMMUTATIVITY. Interpreted -// over fixed-width (2's-complement) machine -// semantics, discharged by the bit-vector route -// (`EngineName::BitVector`, QF_BV via Z3 -// directly — the procedure `by(bit_vector)` -// invokes) at the caged rung **L4** (decidable, -// complete bit-pattern countermodels; -// solver-trusted Z3 QF_BV). -// No `var * var` multiplication, so the default -// budget profile (a cheap bit-blast). +// - `a + b == b + a` checks wrapping addition at `bv64`. +// - `a ^ b ^ b == a` checks bitwise xor at `bv64`. +// - `a * 0 == 0` stays in the unbounded nlsat route. // -// * `ens@bv64 a ^ b ^ b == a` — xor SELF-INVERSE (the one-time-pad identity): -// xoring twice by the same mask is the identity. -// A genuine machine-semantics fact, decided by -// the bit-vector route at **L4**. -// -// * `ens a * 0 == 0` — the UNBOUNDED zero-fixpoint (multiplicative -// zero). NOT `@bv`-tagged, so it lowers as before -// — a relaxable polynomial discharged by the -// stage-1 nlsat relax route at the kernel-grounded -// **L4** (`engine: nlsat`). -// -// The item certifies at the MIN over the clauses — **L4** (L4, L4, L4); all three are the -// caged rung (two @bv64 decidable QF_BV + one nlsat real-relaxation). One function, two -// mechanisms (`bitvector` ×2, `nlsat` ×1), each clause's certificate naming its engine and -// its semantics (fixed-width wraparound vs unbounded). `forge check --engine bv` (with the -// `bv` shadow-flag plumbing compiled in) drives the per-clause route; z3 (bundled with -// verus) decides both the QF_BV and the QF_NRA queries. +// All three certify at L4. The certificate records the engine and semantics for each +// clause. The two QF_BV translation obligations are also exported as literal `BitVec 64` +// goals and checked by Lean's kernel. fn mix64(a: u64, b: u64) -> u64 req true ens@bv64 a + b == b + a @@ -39,13 +15,8 @@ fn mix64(a: u64, b: u64) -> u64 fx pure { a + b } -// The INJECTIVITY lemma (AC-2: "the injectivity lemma discharges at `@bv64` with no proof -// block"). Rotate-left-by-1 — `(x << 1) | (x >> 63)` — is a bijection on 64-bit words, so -// distinct inputs rotate to distinct outputs. A `lemma` carries no `result` and no body; -// the single `@bv64` clause is a closed QF_BV query over the parameters under `req x != y`, -// decided directly by the bit-vector route at **L4** with NO author proof effort — the -// `proof { }` block is empty (the lemma grammar requires the keyword; the QF_BV solver, -// not the author, discharges it). +// Rotate-left-by-one is injective on 64-bit words. The bit-vector route discharges this +// at L4, and the proof block stays empty because the lemma has no author-written proof. // (`<<`/`>>`/`|` all bind tighter than `!=`, so the rotate inequality needs no outer // parens — and a `@bvN` clause cannot open with `(`, which the REQ-1 tag grammar reads as // the `(nowrap)` modifier.) diff --git a/conformance/goal/README.md b/conformance/goal/README.md index c121d51a2..82e86bb66 100644 --- a/conformance/goal/README.md +++ b/conformance/goal/README.md @@ -1,51 +1,32 @@ -# `conformance/goal/` — the §5.1 goal-state dialogue oracle - -This directory holds the acceptance oracle for the Lean-style goal-state REPL -(`forge goal` / `forge fill` / `forge edit` / `forge battery` — design -`.design/forge/goal-repl.md`, thesis `thermite-design.md` §5/§5.1, Appendix B). - -## The dialogue golden - -`binary_search.dialogue.json` is the **AC-6** acceptance oracle: the verbatim -§5.1 `binary_search` dialogue (declare with `body = ?0` → fill the loop skeleton -introducing `?1 ?2` → one discharged / one open-with-counterexample → guard the -branch → `ALL GOALS DISCHARGED ✓ binary_search certified L3`). - -It is **hand-derived from `thermite-design.md` §5.1's hand-written dialogue text + -the design doc's AC-6** — NEVER regenerated from running the verbs (`goal.md` -R-CHAR-3). A fabricated golden copied from the tool's own output would be a false -anchor (the same rule the cert-oracle README states). - -## Structural-oracle vs illustrative (R-CHAR-3 honesty) - -`thermite-design.md` §5.1 is an ILLUSTRATIVE narrative — its concrete numbers are -not all assertable against the real prover. The golden's `oracle_kind` field and -its `expect_structure` / `illustrative_not_asserted` blocks pin the split: - -**STRUCTURAL ORACLE (asserted — the acceptance criteria):** -- the **given/want** lines are present and carry the contract's `req`/`ens` text - (turn 1: `given` contains `sorted(haystack)`); -- a holed item shows its **open holes** as the `holes:` section and is - **NOT CERTIFIED** (`Level::L0`, `reject_cause: OpenHole`) — it never reaches - verus (REQ-5); -- `forge fill` at a hole **closes that hole**, and a fill whose code introduces - new holes **re-presents the new open holes** (the §5.1 fill loop); -- an open obligation carries a **concrete counterexample** (a non-empty - diagnostic), never a bare adjective (§5.1 property 2); -- once **every hole is closed** and the bodies are correct, the item renders - `ALL GOALS DISCHARGED` and certifies **L3** with a **non-vacuous** battery line. - -**ILLUSTRATIVE (NOT asserted — the design's narrative numbers):** -- `solver_time_ms` (§5.1 `0.4s`) — wall-clock, non-deterministic, EXCLUDED from - the oracle (the cert-oracle contract, `conformance/README.md`); -- the **mutant kill ratio** (§5.1 `23/24`) — the live tool computes its own ratio - for the actual mutant set; the asserted fact is `non-vacuous`, not the ratio; -- the **exact `?N` numbers across turns** — v1 re-numbers holes on each re-parse - (no incremental hole-id stability — §5.1 property 1, "the oracle re-presents"), - so the asserted fact is the open/discharged **transition + count**, not literal - `?N` identity across turns; -- the **exact counterexample witness** (§5.1 `lo=3, hi=3, mid=3`) — the design's - illustrative witness; the asserted fact is that the witness is CONCRETE. - -The runner `forge/tests/goal_repl_fill.rs` drives the dialogue against the real -toolchain (real verus where available) and asserts only the structural oracle. +# Goal-state dialogue fixture + +`binary_search.dialogue.json` is the acceptance fixture for `forge goal`, +`forge fill`, `forge edit`, and `forge battery`. It is based on the workflow in +`thermite-design.md` §5.1 and `.design/forge/goal-repl.md`. + +## What the fixture checks + +The runner asserts that: + +- goal output includes the relevant `given` and `want` clauses; +- an item with open holes is not certified and reports those holes; +- filling a hole removes it and reports any new holes introduced by the fill; +- an open failed obligation includes a concrete diagnostic; +- closing all holes in a correct body produces `ALL GOALS DISCHARGED`, an L3 + certificate, and a non-vacuous contract result. + +## Illustrative values + +Several values in the design dialogue are examples rather than stable oracle +data: + +- solver time varies by machine and is not compared; +- the mutation ratio depends on the live mutant set; +- holes may be renumbered after each parse; +- the solver may return a different concrete counterexample. + +The fixture therefore checks structure and state transitions, not those literal +values. It was derived from the design rather than recorded from the goal-state +implementation (goal.md R-CHAR-3). + +`forge/tests/goal_repl_fill.rs` drives the dialogue and applies these assertions. diff --git a/conformance/parse/README.md b/conformance/parse/README.md index 5c8323e5e..4b1259b9d 100644 --- a/conformance/parse/README.md +++ b/conformance/parse/README.md @@ -1,30 +1,20 @@ -# Parse oracle (`conformance/parse/`) +# Parser fixtures -Hand-derived expected structural facts for parsing the corpus programs — the -external anchor for `thermite-syntax/src/parser.rs` (issue #3), per -`.design/syntax/parser.md`. +Each `.facts.json` file records representation-independent facts about a +corresponding Thermite program. The fixtures test +`thermite-syntax/src/parser.rs` without fixing the parser's internal AST layout. -## Contract +Recorded facts include: -Each `.facts.json` describes parsing `.th` at a -representation-agnostic level (counts and kinds the parser must produce, NOT a -specific AST encoding — so the oracle does not over-constrain the AST shape): +- whether the program parses and how many diagnostics it produces; +- top-level item names, kinds, parameters, and return types; +- `req`, `ens`, and `fx` clause counts for functions; +- loop addresses, surface forms, invariant counts, and `dec` presence; +- `dec` presence for specification functions. -- `parses_ok` / `error_count` — whether the program parses cleanly. -- `items[]` — per top-level item: `name`, `kind` (`fn` / `spec fn`), `params` - (name + type as written), `ret`, and for `fn`s the mandatory-clause counts - (`req_count`, `ens_count`, `fx`) and `loops[]` (each with its `loop#N` addr, - surface keyword, `inv_count`, `has_dec`). `spec fn`s carry `has_dec` and no - contract clauses (§4.2). +`recover_per_item.th` checks parser recovery. Its first item is malformed, while +the second is valid. The parser must report the first error and still recover +the second item. -`recover_per_item.th` + `.facts.json` pin **per-item recovery** (§4.3): the -first item is malformed (omits the mandatory `ens` clause — a parse error per -§4.1), and the parser must report that error yet still recover and parse the -well-formed second item (`recovered_items` / `recovered_item_facts`). - -## R-CHAR-3 - -Expected values are derived from the verbatim `.th` source + `thermite-design.md` -§4. They are NEVER produced by `parser.rs`. The parser is the artifact under -test; this is the truth it is tested against. A builder implementing the parser -must MATCH these fixtures, never edit them to match its output. +Expected values are derived from the source text and `thermite-design.md` §4. +They are not generated from the parser under test (goal.md R-CHAR-3). diff --git a/docs/overview.md b/docs/overview.md index 171646e53..01f66913d 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -19,11 +19,12 @@ the resulting certificates; the agent writes the proofs. ## The assurance ladder -Each contract clause is graded on a four-rung ladder. `forge`, the toolchain, +Each contract clause is graded on a five-rung ladder. `forge`, the toolchain, aims for the top rung and records where each clause landed. | Rung | Meaning | |---|---| +| **L4** | A kernel-grounded proof. The nonlinear-arithmetic route combines a Z3 nlsat result with Lean-checked soundness lemmas that connect the real relaxation back to integer semantics. | | **L3** | A machine-checked proof that the clause holds for every input. (SMT-backed deductive verification via the Verus prover and the Z3 solver.) | | **L2** | Proven for all inputs up to a stated size. (Bounded model checking, via Kani/CBMC.) | | **L1** | Checked while the program runs; a violation stops it. (Runtime contract monitoring.) | @@ -33,6 +34,5 @@ A function's level is the minimum over its clauses. A counterexample — a concrete input where a clause fails — is a hard failure; it is never recorded as a lower grade. -The rungs correspond to four established verification techniques. [Verification](verification.md) -covers how L3 is discharged and re-checked; [Trust](trust.md) covers what -remains trusted after a clean run. +[Verification](verification.md) explains how the upper rungs are discharged and +re-checked. [Trust](trust.md) lists what remains trusted after a clean run. diff --git a/docs/verification.md b/docs/verification.md index 746434cef..9f04cd204 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -2,11 +2,13 @@ ## What the ladder rungs mean -L3, the top rung, is a machine-checked proof that a clause holds for every -input, discharged by the Verus prover emitting verification conditions to the -Z3 solver. L2 is bounded model checking (Kani/CBMC) up to a stated size. L1 is -runtime contract monitoring. L0 is a trusted-by-fiat `#[slag]` annotation. -[Overview](overview.md) has the full table. +L4 is a kernel-grounded proof. The shipped nonlinear-arithmetic route asks Z3's +nlsat procedure to discharge a relaxation over the reals, then uses +Lean-checked soundness lemmas to connect that result back to integer semantics. +L3 is a machine-checked proof for every input, normally discharged by Verus and +Z3. L2 is bounded model checking (Kani/CBMC) up to a stated size. L1 is runtime +contract monitoring. L0 is the trusted-by-fiat `#[slag]` annotation. +[Overview](overview.md) summarizes the ladder. ## Grading the contract, not only the proof diff --git a/examples/README.md b/examples/README.md index 95cbdf3dc..354d3d168 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,64 +1,57 @@ -# Thermite example programs +# Thermite examples -Four runnable Thermite programs that prove the verified-primitive basis (C1–C9) -**composes into real programs that run** — each `forge check`s at **L3** (its -bug-prone logic proven for all inputs by real Verus) *and* `forge build`s into a -native binary you run directly. Only the raw syscalls at the I/O seam are trusted. +This directory contains four programs built from Thermite's verified primitive +library. -The pattern is always the same: **`forge build … --entry --out `** drops -a standalone binary at ``; then run `./`. (`forge build` lowers the -program to executable Rust + the always-active runtime contract checks and shells -out to `rustc`; `--out` names the artifact so no wrapper script is needed.) - -| program | what it proves | `forge check` | +| Program | Verified behavior | Runnable entry | |---|---|---| -| [`editor/`](editor/) | a **multi-line interactive text editor** — editing, line nav, cursor layout, keystroke decode all L3; only `read`/`write`/`ioctl`/`open` trusted | edit+nav+layout core L3, shell L1 | -| [`formatter/`](formatter/) | `u64` → decimal `String`, the digit round-trip `parse_be(to_string(n)) == n` proven | `format_*` L3 | -| [`calculator/`](calculator/) | parse two digit-strings + add; the sum is pinned, parse refuses non-digits (loud `None`) | `add_*` L3 | -| [`parser/`](parser/) | split a string on a separator into a `Vec`; the field-count bound proven | `has_key`/`fields` L3 | +| [Editor](editor/) | Buffer edits, line navigation, cursor layout, frame rendering, and key decoding at L3 | `run` | +| [Formatter](formatter/) | `u64` decimal formatting with `parse_be(to_string(n)) == n` at L3 | `format_42` | +| [Calculator](calculator/) | Parsing two decimal strings and returning their sum at L3 | `add_2_3` | +| [Parser](parser/) | String splitting and separator detection at L3 | `split_abc` | -## The editor (the keystone — interactive) +The calculator and parser have a current build limitation: their complete +contracts refer to specification helpers that do not yet have L1 runtime forms. +Their zero-argument demonstration entries still build and run. The individual +READMEs explain the boundary. -```sh -# build the standalone editor binary (one time): -cargo run -q -p forge -- build examples/editor/editor.th --entry run --out ./nano +## Check a program -# run it directly — it self-sets raw mode (extern-C termios; no stty, no script): -THERMITE_EDITOR_FILE=mydoc.txt ./nano +```sh +cargo run -q -p forge -- check examples/calculator/calc.th ``` -Keys: type to insert · **Enter** newline · **↑↓** move between lines · **←→** within -a line · **Backspace** delete · **Ctrl-S** save · **Ctrl-Q** quit. See -[`editor/README.md`](editor/README.md) for the verified-vs-trusted breakdown. -It runs **under the default seccomp sandbox** — its `raw_mode_on`/`raw_mode_off` -boundaries declare `fx term`, whose `fx`-derived seccomp widening grants exactly the -terminal `ioctl` the termios raw mode needs (crosslink #106), so the binary is -effect-confined, not unsandboxed. +`forge check` runs the verification ladder and the contract-quality battery. An +L3 result means Verus discharged every obligation and the contract met the +mutation-score requirement. -## The formatter / calculator / parser (runnable demos) - -These have zero-argument demo entries `forge build` can run directly: +## Build the editor ```sh -# formatter: 42 -> "42" -cargo run -q -p forge -- build examples/formatter/format.th --entry format_42 --out ./fmt && ./fmt - -# calculator: 2 + 3 -> Some(5) -cargo run -q -p forge -- build examples/calculator/calc.th --entry add_2_3 --out ./calc && ./calc - -# parser: "a,b,c" split on ',' -> 3 fields -cargo run -q -p forge -- build examples/parser/parse_lines.th --entry split_abc --out ./parse && ./parse +cargo run -q -p forge -- build examples/editor/editor.th --entry run --out ./nano +THERMITE_EDITOR_FILE=mydoc.txt ./nano ``` -Other demo entries: `format_0` / `format_1000000`; `add_100_200`. +The editor sets terminal raw mode itself and runs under the default seccomp +filter. Its declared `fx` effects permit the required file and terminal calls. + +Keys: type to insert, Enter for a newline, arrows to move, Backspace to delete, +Ctrl-S to save, and Ctrl-Q to quit. -## Check the proof (any program) +## Build the smaller demos ```sh -# certify: per-item L3/L2/L1 certificate (real Verus), no binary produced -cargo run -q -p forge -- check examples/calculator/calc.th +# 42 -> "42" +cargo run -q -p forge -- build examples/formatter/format.th --entry format_42 --out ./fmt +./fmt + +# 2 + 3 -> Some(5) +cargo run -q -p forge -- build examples/calculator/calc.th --entry add_2_3 --out ./calc +./calc + +# "a,b,c" -> three fields +cargo run -q -p forge -- build examples/parser/parse_lines.th --entry split_abc --out ./parse +./parse ``` -`forge check` runs the verification ladder + the anti-Goodhart battery; an item is -**L3** only when real Verus discharges every obligation and the contract kills the -generated mutants. The `examples/*/README.md` files give the per-item proof tables. +Other demo entries include `format_0`, `format_1000000`, and `add_100_200`. diff --git a/examples/calculator/README.md b/examples/calculator/README.md index 48e60def3..147bf3ad3 100644 --- a/examples/calculator/README.md +++ b/examples/calculator/README.md @@ -1,12 +1,9 @@ -# Verified calculator core — acceptance program 2 of 3 (#103) +# Verified calculator -`calc.th` composes shipped verified primitives — cluster **C7**'s `parse_u64` -(`String` → `Option`, `.design/basis/09-option-result.md` / `07-strings.md` -REQ-9) with its big-endian round-trip, the built-in `Option` + the -spec-`match`-in-`ens` payload projection, and the C2 partial-`+` (overflow is a -proof obligation). No new toolchain feature is needed to CERTIFY. +[`calc.th`](calc.th) combines the verified decimal parser with +`Option`, contract-position pattern matching, and checked addition. -## What is proven (forge check → L3) +## Verified behavior ```thermite fn add(a: String, b: String) -> Option @@ -23,53 +20,43 @@ fn add(a: String, b: String) -> Option } ``` -`forge check calc.th` certifies `add` at **L3**: a valid, in-range pair of digit -strings parses to `Some(parse_be(a) + parse_be(b))` — the sum is PINNED (a `None` -or a wrong-sum mutant violates the `ens`). The nested-`match` composition over the -two `parse_u64` calls certifies cleanly. The arithmetic core `add_vals`/`add_2_3`/ -`add_100_200` also certify L3. +`forge check` certifies `add` at L3. For valid in-range decimal inputs, the +result is `Some(parse_be(a) + parse_be(b))`. Returning `None` or the wrong sum +violates the postcondition. -## Run it (the arithmetic core) - -```bash -cargo run -p forge -- build examples/calculator/calc.th --entry add_2_3 # see the gap below first +```sh +cargo run -q -p forge -- check examples/calculator/calc.th ``` -The arithmetic core RUNS: +## Run the arithmetic demo +```sh +cargo run -q -p forge -- build examples/calculator/calc.th --entry add_2_3 --out ./calc +./calc ``` -add_2_3() = Some(5) # 2 + 3 -add_100_200() = Some(300) # 100 + 200 + +Available zero-argument entries: + +```text +add_2_3() = Some(5) +add_100_200() = Some(300) ``` -## FORCING-FUNCTION FINDING — the string-parse front-end cannot `forge build` +## Current build limitation -`forge build calc.th` (the full program, including `add`) **fails to compile**: +Building the full string-parsing entry currently fails because the L1 runtime +lowerer does not emit all of the C7 parsing helpers: -``` +```text error[E0425]: cannot find function `all_digits` in this scope error[E0425]: cannot find function `parse_be` in this scope error[E0425]: cannot find function `parse_u64` in this scope ``` -This is a real gap, NOT a defect in the program (it certifies L3 correctly). The -cause: `forge build` lowers EVERY function to its always-active runtime -`thermite_check!`, and `add`'s contract names the **C7 spec fns** `all_digits` / -`parse_be` and its body calls the free `parse_u64` — but `thermite-lower`'s -`emit_string_runtime_l1` emits an **L1 (runtime / build) runnable form ONLY for -cluster C4**'s `parse_be` / `parse_le` / `pow10` / `u64_to_string` (the formatter). -The C7 parse spec fns have **no L1 emission**, so the runtime check cannot resolve -them. This belongs to the **C7 / #95 build-side cluster** (the L1 mirror of the C7 -spec fns), not the calculator. When that L1 lowering lands, `calc.th` builds + runs -the full string-parse path end-to-end ("2"+"3" → `Some(5)`). - -Until then, the calculator's add-and-return-`Some(sum)` core composes and runs (the -arithmetic core above); the digit-STRING parse at RUNTIME awaits the C7 L1 form. - -## Verification +This affects `forge build`, which turns contracts into runtime checks. It does +not affect the L3 proof. The missing work is the L1 implementation of the C7 +specification functions described in +`.design/basis/09-option-result.md` and `.design/basis/07-strings.md`. -`forge/tests/acceptance_programs.rs`: `calculator_sum_contract_certifies_l3` -(forge check → L3 for `add` + the core), `calculator_arithmetic_core_builds_and_runs` -(the core RUNS → `Some(5)`, `Some(300)`), and -`calculator_string_parse_build_is_blocked_by_missing_l1_parse_u64` (PINS the gap; -flips to assert end-to-end build when the C7 L1 lowering lands). +The behavior is covered in `forge/tests/acceptance_programs.rs` by the +calculator certification, runnable-core, and missing-L1-helper tests. diff --git a/examples/calculator/calc.th b/examples/calculator/calc.th index 0f96e9085..c7ddbded7 100644 --- a/examples/calculator/calc.th +++ b/examples/calculator/calc.th @@ -1,33 +1,9 @@ -// ACCEPTANCE PROGRAM 2 of 3 (crosslink #103) — a verified CALCULATOR core. +// Verified calculator example (crosslink #103). // -// THE COMPOSE-ANY-PROGRAM PROOF: this program composes shipped verified -// primitives — cluster C7's `parse_u64` (String -> Option, -// .design/basis/09-option-result.md / 07-strings.md REQ-9) with its big-endian -// round-trip contract, the built-in `Option` + the spec-`match`-in-`ens` -// payload projection (C7), and the C2 partial-`+` (overflow is a proof -// obligation). No new toolchain feature is needed to CERTIFY. -// -// DELIVERABLE (a) — `forge check calc.th` certifies `add` at L3 with the -// MEANINGFUL sum contract: a valid, in-range pair of digit strings parses to -// `Some(parse_be(a) + parse_be(b))` (the sum is PINNED, not vacuous — a None or -// a wrong-sum mutant violates the `ens`). The nested-`match` composition over -// the two `parse_u64` calls certifies cleanly. -// -// DELIVERABLE (b) — RUN: the arithmetic core `add_vals` + the `add_2_3` entry -// build + run, printing `Some(5)` for 2+3. The STRING-PARSE front-end `add` -// canNOT `forge build` — its contract names the C7 spec fns `all_digits` / -// `parse_be` / the free `parse_u64`, which have NO L1 (runtime / build) exec -// emission (only C4's `parse_be`/`parse_le`/`u64_to_string` were given an L1 -// form, for the formatter). This is a real FORCING-FUNCTION finding (the C7/#95 -// build-side gap), reported precisely in README.md — NOT faked. The calculator's -// add-and-return-Some(sum) core composes and runs; the digit-string parse at -// RUNTIME awaits the L1 lowering of the C7 parse spec fns. - -// THE FULL CALCULATOR (forge check -> L3): parse two valid in-range digit -// strings and add. The req pins all_digits + non-empty + in-range (so each -// parse_u64 takes the Some arm and the sum does not overflow i64::MAX*2 < u64); -// `ens result is Some` discharges from those reqs; the sum `Some(v) => v == -// parse_be(a) + parse_be(b)` pins the value (the round-trip on both operands). +// `add` verifies at L3. Its preconditions require two non-empty decimal strings +// whose values can be added without overflowing `u64`; its postconditions fix +// the result to that sum. The parser-related specification functions do not yet +// have L1 lowering, so the runnable entries use `add_vals` instead. fn add(a: String, b: String) -> Option req all_digits(a) && a.len() >= 1 && parse_be(a) <= 9223372036854775807 && all_digits(b) && b.len() >= 1 && parse_be(b) <= 9223372036854775807 ens result is Some @@ -40,11 +16,8 @@ fn add(a: String, b: String) -> Option } } -// THE ARITHMETIC CORE (forge check -> L3, builds + runs): add two parsed values, -// returning `Some(sum)`. The `ens match result { Some(v) => v == x + y, None => -// false }` pins the sum AND forbids None (a None or wrong-sum mutant is killed). -// The req bounds each operand to i64::MAX so `x + y` cannot overflow u64. This is -// the half of the calculator that has an L1 runnable form (ordinary Option + `+`). +// Add two parsed values. The operand bounds rule out overflow, and the +// postcondition requires `Some(x + y)`. fn add_vals(x: u64, y: u64) -> Option req x <= 9223372036854775807 && y <= 9223372036854775807 ens match result { Some(v) => v == x + y, None => false } @@ -53,8 +26,7 @@ fn add_vals(x: u64, y: u64) -> Option Some(x + y) } -// Runnable entry (forge build --entry add_2_3): the 2 + 3 example, fixed -// internally (the v0.1 runner synthesizes no Option/String arg). RUNS -> Some(5). +// Runnable entry: `forge build calc.th --entry add_2_3`. fn add_2_3() -> Option req true ens match result { Some(v) => v == 5, None => false } @@ -63,7 +35,7 @@ fn add_2_3() -> Option add_vals(2, 3) } -// Runnable entry: 100 + 200 -> Some(300). +// A second fixed-input entry for checking a larger sum. fn add_100_200() -> Option req true ens match result { Some(v) => v == 300, None => false } diff --git a/examples/editor/README.md b/examples/editor/README.md index f3128d2aa..a70177281 100644 --- a/examples/editor/README.md +++ b/examples/editor/README.md @@ -1,158 +1,104 @@ -# A MAX-VERIFIED, runnable MULTI-LINE text editor (Thermite) +# Verified terminal editor -`editor.th` is the keystone proof-of-the-pudding for Thermite (crosslink #125, -builds on #90, ref #83): a nano-like **multi-line** text editor whose **bug-prone -logic — the editing heart, the line NAVIGATION + cursor LAYOUT math, the -display-frame construction, AND the keystroke decode — is mechanically PROVEN**, -with only the raw read/write/ioctl/open **syscalls** honestly trusted at the seam -where proof meets the world. It both `forge check`s (the proof) and `forge build`s + -RUNS (the artifact). +[`editor.th`](editor.th) is a small multi-line terminal editor. Thermite proves +the buffer operations, navigation, cursor layout, frame construction, and key +decoding. File and terminal operations remain at the L1 boundary. -It is genuinely nano-like: **newlines (Enter), up/down line navigation, a -full-screen render that positions the cursor by ROW/COLUMN, and file load/save**. -The buffer is ONE `String` with `\n` bytes; the cursor is a byte offset, so the -shipped edit core (`insert_str`/`backspace`/`move_left`/`move_right`) works -unchanged over the multi-line text (a `\n` is just a byte). The ROW/COLUMN cursor -math and the UP/DOWN navigation are VERIFIED Thermite (L3) — the navigation + -layout logic is proven, not trusted glue. +## Assurance boundary -## The thesis: verified vs trusted, pushed as far as the language allows +The project-level assurance is L1 because a project's level is the minimum of +its functions. Most editor logic reaches L3; syscall wrappers and the +non-terminating event loop remain L1. -Thermite does not pretend the kernel can be proven — but it pushes the proof -boundary all the way down to the syscalls. The display logic and the input -interpretation, the parts that are actually bug-prone, are VERIFIED. +### L3 logic -### Verified logic — `forge check` certifies **L3** (total + mutation-proven) - -| item | what is proven | +| Item | Guarantee | +|---|---| +| `Buffer` | `cursor <= text.len()` and `text.len() <= 1_000_000` | +| `insert_str` | Inserts at the cursor and advances it by `ins.len()` | +| `backspace` | Deletes the preceding byte and moves the cursor back one | +| `move_left` / `move_right` | Moves one byte while preserving the text and cursor bound | +| `count_nl` | Counts newlines in a bounded range and terminates by `end - i` | +| `line_start` / `line_end` | Finds the bounds of the line containing a position | +| `cursor_row` / `cursor_col` | Computes zero-based display coordinates | +| `move_up` / `move_down` | Preserves the text and clamps the cursor to the target line | +| `to_1based` | Converts terminal coordinates with `result == x + 1` | +| `render_frame` | Includes the buffer text and positions the terminal cursor | +| `decode` | Maps printable bytes, control keys, and arrow sequences to editor commands | + +These functions are total and pass the mutation battery. The decimal formatter's +20-digit upper bound is used to prove that cursor-coordinate concatenation stays +within the bounded string capacity. + +### L1 boundary + +| Item | Responsibility | |---|---| -| `Buffer` | the type invariant `cursor <= text.len() && text.len() <= 1_000_000` — the cursor NEVER points past the text, the text stays within the bounded-`String` cage (§4.2) | -| `insert_str` | the text grows by exactly `ins.len()` and the cursor advances by exactly `ins.len()` (so it still points within the new text) | -| `backspace` | the text shrinks by exactly one and the cursor steps back exactly one (`req cursor > 0` guarantees a byte to delete) | -| `move_left` | the cursor steps back exactly one; the text is unchanged | -| `move_right` | the cursor advances exactly one (`req cursor < text.len()` keeps it in bounds); the text is unchanged | -| `count_nl` | **NAV (#125)** — a verified recursive FORWARD scan counting the newline (byte 10) occurrences in `text[i..end]`; `ens result <= end - i` plus a boundary teeth clause (a newline at `i` forces `result >= 1`) make it non-vacuous, `dec end - i` proves termination. The row math. | -| `line_start` / `line_end` | **NAV (#125)** — verified forward scans for the start/end of the line a position sits on (`line_start` carries the running "index after the last newline" accumulator). The line-boundary math up/down navigation stands on. | -| `cursor_row` / `cursor_col` | **LAYOUT (#125)** — the cursor's 0-based ROW (newlines in `text[0..cursor]`, via `count_nl`) and COLUMN (`cursor − line_start`). `ens result <= b.cursor`; they feed the ANSI cursor positioning. Proven cursor layout, not trusted. | -| `move_up` / `move_down` | **NAV (#125)** — verified up/down line navigation: find the prev/next line boundaries, clamp the target column to that line's length (`min2`), set the new cursor. PROVEN: the text is unchanged AND the new cursor stays in bounds (`cursor <= text.len()` — the Buffer type invariant). | -| `to_1based` | **LAYOUT (#125)** — the 0→1-based ANSI coordinate conversion, `ens result == x + 1`. PROVEN exactly, so an off-by-one mutant is killed — the `+1` is verified, not an unverified literal in `render_frame`. | -| `render_frame` | **THE THESIS (multi-line, #125)** — the full ANSI display frame is built as a `String` (C1 escape literals + the buffer text [`\n` bytes render as terminal line breaks] by `concat` + the cursor coordinate `\x1b[;H` from the VERIFIED `cursor_row`/`cursor_col` via the C4 `.to_string()` and the proven `to_1based`), and `ens result.len() >= b.text.len()` PROVES the whole buffer text is carried into the frame (a dropped-text mutant shortens the frame and fails the `ens`). The display logic is PROVEN, not trusted glue. | -| `decode` | a PURE TOTAL function mapping the raw read bytes to a key code (printable → itself, DEL → backspace, **Enter (CR/LF) → insert-newline 1004**, **Ctrl-S → save 19**, Ctrl-Q → quit, the arrow escape sequences `ESC [ A..D` → synthetic codes 1000..1003). The keystroke INTERPRETATION is proven, not trusted. | - -Each is a TOTAL function: its math holds for ALL inputs, an SMT proof discharges -every obligation, and the §7 mutation battery confirms the contract is strong (not -a tautology a weak body could satisfy). **L3 = total correctness.** - -`render_frame` L3 rests on a real C4 strengthening: `u64_to_string`'s `ens` now -bounds the formatted decimal length `result.data.len() <= 20` (a u64 is < 10^20, so -at most 20 digits — PROVED, not assumed), so the bounded `concat`'s §4.2 cage -precondition discharges for any formatted-number concat. - -### The minimal trusted syscall boundary — **L1** (the honest seam to the world) - -| item | level | why it is trusted, not proven | -|---|---|---| -| `raw_mode_on` | L1 boundary | `#[boundary("os::raw_mode_on")]` — put the terminal in RAW mode (clear `ICANON`/`ECHO` so each keystroke reaches the editor live) via extern-C `tcgetattr`/`tcsetattr` (libc linked through std — self-contained, no `stty`). `ens result <= 1` (0 = ok, 1 = not-a-TTY/error). The binary self-sets raw mode; a non-TTY (piped) stdin is handled gracefully — `tcgetattr` returns ENOTTY, the wrapper returns 1, **no crash**. | -| `raw_mode_off` | L1 boundary | `#[boundary("os::raw_mode_off")]` — restore the saved original termios; runs on the quit path so the terminal is never left in raw mode. A clean no-op when raw mode was never entered. | -| `read_key_raw` | L1 boundary | `#[boundary("os::read_key_raw")]` — read one keystroke, returning the raw bytes PACKED into a u64 for `decode` (`b0` bits 0..9, `b1` 9..18, `b2` 18..27; an ESC reads the 2-byte arrow tail). `ens result <= 134_217_727` (the 27-bit packing width — an honest boundary bound). | -| `write_frame` | L1 boundary | `#[boundary("os::write_frame")]` — write the rendered frame `String` to stdout and flush. `ens result <= 1`. | -| `read_file` | L1 boundary | `#[boundary("os::read_file")]` — LOAD the initial buffer from the fixed demo file (`THERMITE_EDITOR_FILE` if set, else `/tmp/thermite_editor.txt`) via extern-C `std::fs::read`; the multi-line `\n` bytes are preserved. A missing file yields the EMPTY string (a fresh buffer) — the honest arm, no crash. `ens result.len() <= 1_000_000`. (#125) | -| `write_file` | L1 boundary | `#[boundary("os::write_file")]` — SAVE the buffer `String`'s bytes (incl. the `\n` line breaks) to the same fixed file on Ctrl-S, via `std::fs::write`. `ens result <= 1` (0 = ok, 1 = I/O error). (#125) | -| `run` | L1 (partial correctness) | the `fx diverge` event loop. An event loop is **non-terminating by design**, so it cannot honestly claim L3 = TOTAL correctness. It caps at **L1 = partial correctness**: the loop runs under its always-active runtime contract checks, and the logic it drives (`decode`, `render_frame`, `insert_str`/`backspace`/`move_left`/`move_right`) is the L3-proven core its correctness rests on. The §7 mutation gate is exempt for a diverge fn — `run`'s shape is honestly weak, NOT a gamed one (R-DEFER-9). | - -The whole-project assurance is the **min over functions = L1** — capped by the -trusted syscall seam, exactly as it should be. The verified guarantee is -*to-the-boundary*: the display + input logic is PROVEN; only the raw syscalls are -trusted. - -## How to check it +| `raw_mode_on` / `raw_mode_off` | Enter and restore terminal raw mode through `tcgetattr` and `tcsetattr` | +| `read_key_raw` | Read and pack a key sequence for `decode` | +| `write_frame` | Write and flush a rendered frame | +| `read_file` / `write_file` | Load and save the configured file | +| `run` | Drive the `fx diverge` event loop under runtime contract checks | + +The terminal wrappers return status values instead of panicking when stdin is +not a TTY. A missing input file produces an empty buffer. `run` is partial +correctness at L1 because the event loop is intentionally non-terminating. + +## Check it ```sh -forge check examples/editor/editor.th +cargo run -q -p forge -- check examples/editor/editor.th ``` -Expect the verified logic (`Buffer`/`insert_str`/`backspace`/`move_left`/ -`move_right`/`render_frame`/`decode`) at **L3**, the syscall boundary -(`raw_mode_on`/`raw_mode_off`/`read_key_raw`/`write_frame`) at **L1 boundary**, and -`run` at **L1** (diverge / partial correctness — NOT an L0 `WeakContract` reject). -Project assurance: **L1**. - -## How to run it +Expect the pure editor functions at L3, boundary functions at L1, and `run` at +L1. The resulting project level is L1. -Build it once to a named path with `--out`, then run that **standalone binary -directly** — it self-sets raw mode via its own extern-C `termios` boundary (no -`stty`, no wrapper script): +## Build and run ```sh -# build the standalone editor binary (one time) — FULLY SANDBOXED by default: cargo run -q -p forge -- build examples/editor/editor.th --entry run --out ./nano - -# run it INTERACTIVELY in a real terminal — type, arrows move, Ctrl-S saves, Ctrl-Q quits: THERMITE_EDITOR_FILE=mydoc.txt ./nano ``` -`./nano` is a self-contained executable: it puts the terminal in raw mode itself, -loads `THERMITE_EDITOR_FILE` (empty/missing → a fresh buffer), and restores the -terminal on Ctrl-Q. It runs UNDER the default seccomp sandbox (no `--no-sandbox`): -its `raw_mode_on`/`raw_mode_off` boundaries declare `fx term` (crosslink #106/#132), -whose seccomp widening grants the `ioctl` the termios raw mode needs — so every -syscall the editor issues is granted by its transitive `fx` (`ioctl` by `term`, -`read`/`openat` by `read(input)`, `write`/`openat` by `write(output)`, the heap by -the baseline). A program WITHOUT `term` attempting `ioctl` is still SIGSYS-killed. +The editor sets and restores raw mode itself. No `stty` wrapper is required. -You can also drive it non-interactively by piping keystrokes (deterministic): +| Key | Action | +|---|---| +| Printable byte | Insert at the cursor | +| Enter | Insert `"\n"` | +| Up / Down | Move to the previous or next line, preserving the column when possible | +| Left / Right | Move one byte | +| Backspace | Delete the byte before the cursor | +| Ctrl-S | Save | +| Ctrl-Q | Quit and restore the terminal | + +You can also run a deterministic piped session: ```sh -printf 'ab\x1b[DX\x7f\x11' | ./nano # a, b, LEFT, X (splice -> aXb), Backspace, Ctrl-Q +printf 'ab\x1b[DX\x7f\x11' | ./nano ``` -The keystrokes are `a`, `b`, then a **LEFT arrow** (`ESC [ D` = `\x1b[D`, decode → -1003: cursor steps left between `a` and `b`), then `X` (the L3 `insert_str` SPLICES -mid-text → `aXb`), then **Backspace** (`0x7f`, deletes `X` → `ab`), then **Ctrl-Q** -(`0x11` = 17 = quit). The editor self-sets raw mode, decodes each keystroke with the -L3-proven `decode`, dispatches to the L3-proven edit ops, builds each frame with the -L3-proven `render_frame`, writes it, and exits clean on Ctrl-Q — restoring the -terminal on the way out. +That sequence inserts `ab`, moves left, inserts and deletes `X`, then quits. -### The MULTI-LINE keymap (#125) +For a multi-line save: ```sh SAVE=/tmp/thermite_editor.txt -# type "ab", ENTER (newline), "cd", UP arrow, Ctrl-S (save), Ctrl-Q (quit): -printf 'ab\rcd\x1b[A\x13\x11' | THERMITE_EDITOR_FILE="$SAVE" -cat "$SAVE" # -> ab\ncd (the multi-line buffer, saved with its newline) +printf 'ab\rcd\x1b[A\x13\x11' | + THERMITE_EDITOR_FILE="$SAVE" ./nano +cat "$SAVE" ``` -| key | byte(s) | decode | action | -|---|---|---|---| -| printable (space..~) | 32..126 | itself | insert the char at the cursor | -| **Enter** | CR `\r` (13) or LF (10) | 1004 | insert `"\n"` (a new line — the cursor drops to row+1, col 1) | -| **UP** / **DOWN** arrow | `ESC [ A` / `ESC [ B` | 1000 / 1001 | move the cursor to the same column on the previous / next line (`move_up`/`move_down`) | -| **LEFT** / **RIGHT** arrow | `ESC [ D` / `ESC [ C` | 1003 / 1002 | move the cursor one byte (`move_left`/`move_right`) | -| Backspace / DEL | `0x7f` | 127 | delete the char before the cursor | -| **Ctrl-S** | `0x13` | 19 | **save** the buffer to the file (`write_file`) | -| Ctrl-Q | `0x11` | 17 | quit (restores the terminal) | - -The buffer is LOADED from the file on start (`read_file`; an empty/missing file is a -fresh buffer). Each frame is the full-screen render: clear+home, the multi-line -buffer text (`\n` → line breaks), then the cursor positioned at -`\x1b[;H` from the VERIFIED `cursor_row`/`cursor_col`. A piped session -shows TWO lines and the cursor moving between them — e.g. after Enter the cursor is -at `\x1b[2;1H` (row 2), and after the UP arrow it is back at `\x1b[1;3H` (row 1). - -**The sandbox grants `ioctl` via `fx term` (#106/#132):** the termios boundary -(`raw_mode_on`/`raw_mode_off`) issues the `ioctl` syscall (16) via -`tcgetattr`/`tcsetattr`. Those boundaries declare `fx term` — the dedicated -terminal-control effect atom — whose `forge/src/sandbox.rs` `TERM_SYSCALLS={ioctl:16}` -widening grants `ioctl`. So the editor builds + runs FULLY sandboxed (the default, -NO `--no-sandbox`): raw mode enters under the filter, edits/saves run, exit 0. The -grant is SCOPED to the effect — a plain `write` program (`print`, `write_file`) does -NOT acquire `ioctl`, so its `ioctl` is still SIGSYS-killed. The grant is `ioctl`-broad -(any cmd) because classic seccomp-bpf cannot filter the `ioctl` cmd register -(runtime-sandbox.md OQ-5). - -The runnable session is grounded as a test: `forge/tests/editor_runs.rs` builds the -editor with `rustc`, runs it with the piped keystrokes above, and asserts the frames -show the mid-text splice (`aXb`) then the backspace undo (`ab`) and a clean exit — -alongside the cert-level checks (logic L3, boundary/`run` L1) and the diverge-only -honesty regressions. +The saved content is `ab\ncd`. + +## Sandbox + +The binary runs under the default seccomp filter. The terminal boundaries +declare `fx term`, which adds `ioctl`; file effects add the required `read`, +`write`, and `openat` calls. Programs without `fx term` do not receive `ioctl`. +Classic seccomp-BPF cannot filter the `ioctl` command argument, so the grant is +syscall-wide. + +`forge/tests/editor_runs.rs` checks certification, builds the editor, drives the +piped session, verifies the rendered splice and backspace behavior, and checks a +clean exit. diff --git a/examples/editor/editor.th b/examples/editor/editor.th index 599feebf3..0bec7fd71 100644 --- a/examples/editor/editor.th +++ b/examples/editor/editor.th @@ -1,80 +1,19 @@ -// A verified, RUNNABLE nano-like MULTI-LINE text editor (crosslink #125, ref #83, -// builds on #90) — the keystone acceptance program, MAX-VERIFIED. +// Verified multi-line editor example (crosslink #125). // -// THE THESIS (verified vs trusted, pushed as far as the language allows): -// * VERIFIED (certifies L3 — proven Thermite, NOT trusted glue): -// - the EDIT CORE: Buffer + insert_str + backspace + move_left + move_right -// (the cursor math + buffer edits — proven for all inputs). The buffer is -// ONE String with `\n` bytes; the cursor is a byte offset, so insert_str/ -// backspace/move_left/move_right work UNCHANGED over the multi-line text -// (a `\n` is just a byte). -// - the NAV / LAYOUT CORE (the multi-line heart, #125): cursor_row / cursor_col -// (the verified ROW/COLUMN of the cursor — a recursive `\n`-counting scan -// over the buffer prefix, proven) + move_up / move_down (verified up/down -// line navigation — find the prev/next line boundaries, clamp the column, -// set the new cursor, proven IN BOUNDS). The recursive scans count_nl / -// line_start / line_end carry real `ens` + `dec` and certify L3 individually. -// - the RENDER-FRAME: `render_frame(&b)` builds the full ANSI terminal frame -// as a String (C1 escape literals + `b.text` by concat + the cursor -// coordinate `\x1b[;H` via the verified row/col + C4 -// `.to_string()`); its `ens` PROVES the frame is at least as long as the -// buffer text. The 1-based ANSI conversion is the PROVEN `to_1based` -// (`ens result == x + 1` — the off-by-one is killed, not laundered). -// - the DECODE: `decode(b0,b1,b2)` — a PURE TOTAL function mapping the raw read -// bytes to a key code (printable -> itself, DEL -> backspace, Enter (CR/LF) -// -> insert-newline, Ctrl-S -> save, Ctrl-Q -> quit, the arrow escapes -> -// synthetic up/down/left/right codes). The keystroke interpretation is proven. -// * TRUSTED (the MINIMAL syscall boundary — certifies L1, the honest seam): -// - os::raw_mode_on / os::raw_mode_off (termios raw mode via extern-C -// tcgetattr/tcsetattr — libc through std, self-contained), -// - os::read_key_raw (read 1 byte; on ESC read the 2-byte arrow tail; packed), -// - os::write_frame (write the rendered String to stdout), -// - os::read_file (LOAD the initial buffer from the fixed demo file) and -// os::write_file (SAVE the buffer on Ctrl-S) — extern-C `std::fs` (#125). -// * THE EVENT LOOP `run` (L1, the `fx diverge` cap): the non-terminating loop is -// the honest L1 seam; the LOGIC it calls (decode, render_frame, the edit + nav -// ops) is the L3-verified core. -// -// Only the raw read/write/ioctl/open SYSCALLS are trusted. The editing, the -// navigation, AND the cursor-layout LOGIC — the editor's bug-prone parts — are -// VERIFIED. See README.md for the split + the keymap + how to run. - -// --------------------------------------------------------------------------- -// THE VERIFIED EDIT CORE (L3) — proven cursor math + buffer edits. -// --------------------------------------------------------------------------- - -// The editor buffer: the text (one String, `\n` bytes mark line breaks) and the -// cursor offset (a byte index). The type invariant is the editor's core safety -// claim — the cursor never points past the end of the text, and the text stays -// within the bounded-String capacity (the §4.2 cage). Every edit + nav op below -// preserves this invariant (proven L3). +// Buffer edits, navigation, layout, frame construction, and key decoding verify +// at L3. Terminal mode, file access, input, and output cross the L1 boundary. +// The event loop is also L1 because it diverges. See README.md for the keymap and +// commands. + +// The cursor is a byte offset into a String whose newline bytes separate lines. +// The invariant keeps the cursor in bounds and limits the buffer size. struct Buffer { text: String, cursor: u64, } inv cursor <= text.len() && text.len() <= 1_000_000 -// Insert `ins` at the cursor: split the text at the cursor, splice `ins` between -// the halves, advance the cursor past the inserted run. PROVEN: the text grows by -// exactly `ins.len()` and the cursor advances by exactly `ins.len()`. (Enter -// inserts "\n" through this SAME op — a newline is just a byte.) -// -// CONTENT PINS (#276, the built-in `bytes_eq` — `.design/basis/07-strings.md` -// REQ-18, the field-access operand view #279): the new text is EXACTLY the splice -// `head ++ ins ++ tail`, certified byte-for-byte by three `bytes_eq` windows over -// the result's `text` field (the byte-view `result.text` → `result.text.data@` — -// the BARE-FIELD operand form, one of #279's four sanctioned operand shapes; the -// editor is the keystone that must BOTH `forge check` AND `forge build --entry run`, -// and the bare-field spelling lowers cleanly through BOTH the L3 spec path and the -// L1 exec runtime-check twin): -// * the UNCHANGED PREFIX text[0..cursor) survives verbatim; -// * the INSERTED RUN ins[0..ins.len()) lands at the cursor; -// * the SHIFTED SUFFIX text[cursor..end) reappears at cursor+ins.len(). -// These are CONTENT teeth a length pin cannot fake — a head/tail-swap mutant -// (`tail ++ ins ++ head`) has the SAME length but fails every window (R-DEFER-9). -// The byte windows discharge via the prove-once `lemma_bytes_eq_bridge` (one -// auto-emitted citation, zero per-conjunct glue). The cage `req` is the SAME -// `<= 1_000_000` the §4.2 bounded concat needs; the windows add no new precondition -// (the Buffer inv `cursor <= text.len()` supplies the bounds the bridge consumes). +// Insert text at the cursor. The three `bytes_eq` conditions preserve the +// prefix, insert `ins`, and preserve the shifted suffix byte for byte (REQ-18). fn insert_str(b: Buffer, ins: String) -> Buffer req b.text.len() + ins.len() <= 1_000_000 ens result.text.len() == b.text.len() + ins.len() @@ -90,20 +29,8 @@ fn insert_str(b: Buffer, ins: String) -> Buffer Buffer { text: head.concat(ins).concat(tail), cursor: b.cursor + n } } -// Delete the byte before the cursor: splice the text around the deleted byte and -// step the cursor back one. PROVEN: the text shrinks by exactly one and the cursor -// steps back exactly one (`req cursor > 0` guarantees a byte to delete). -// -// CONTENT PINS (#276, `bytes_eq` — REQ-18): the result text is EXACTLY -// `head ++ tail` with the byte at `cursor-1` excised, certified by two `bytes_eq` -// windows over `&result.text`: -// * the UNCHANGED PREFIX text[0..cursor-1) survives verbatim; -// * the SHIFTED SUFFIX text[cursor..end) reappears at cursor-1 (the deletion -// pulls the tail back exactly one byte). -// (bare-field `result.text` operand — #279's sanctioned shape, builds + checks.) -// CONTENT teeth: a mutant deleting the WRONG byte (or duplicating the prefix) keeps -// the same length but fails a window. Discharged via `lemma_bytes_eq_bridge`. No new -// `req` (the existing `cursor > 0` + the Buffer inv feed the bridge bounds). +// Delete the byte before the cursor. The `bytes_eq` conditions preserve the +// surrounding content rather than checking only the new length. fn backspace(b: Buffer) -> Buffer req b.cursor > 0 ens result.text.len() == b.text.len() - 1 @@ -118,8 +45,7 @@ fn backspace(b: Buffer) -> Buffer Buffer { text: head.concat(tail), cursor: lo } } -// Move the cursor one byte left. PROVEN: the cursor steps back exactly one and the -// text is unchanged. +// Move the cursor one byte left without changing the text. fn move_left(b: Buffer) -> Buffer req b.cursor > 0 ens result.cursor == b.cursor - 1 @@ -129,8 +55,7 @@ fn move_left(b: Buffer) -> Buffer Buffer { text: b.text, cursor: b.cursor - 1 } } -// Move the cursor one byte right. PROVEN: the cursor advances exactly one (kept in -// bounds by `req cursor < text.len()`) and the text is unchanged. +// Move the cursor one byte right without changing the text. fn move_right(b: Buffer) -> Buffer req b.cursor < b.text.len() ens result.cursor == b.cursor + 1 @@ -140,23 +65,9 @@ fn move_right(b: Buffer) -> Buffer Buffer { text: b.text, cursor: b.cursor + 1 } } -// --------------------------------------------------------------------------- -// THE VERIFIED NAV / LAYOUT CORE (L3, #125) — the multi-line heart, PROVEN. -// -// cursor_row / cursor_col compute the cursor's ROW and COLUMN by VERIFIED scans -// over the buffer prefix (each scan a recursive `fn` with a real `ens` + `dec`); -// move_up / move_down move the cursor to (approximately) the same column on the -// previous / next line, clamping to that line's length and proving the new cursor -// stays IN BOUNDS (the Buffer type invariant). This is the editor's navigation + -// layout LOGIC — proven, not trusted. -// --------------------------------------------------------------------------- - -// Count the newline (byte 10) occurrences in text[i..end] — a verified recursive -// FORWARD scan (it examines `byte_at(i)` at the plain loop index `i`, so the L1 -// runtime-check lowering of the contract has no compound-index cast to mangle). -// PROVEN: result <= end - i (a bound), AND when the byte at i is a newline the -// count is at least one (a boundary teeth clause that kills the trivial `return 0` -// mutant — the contract is non-vacuous). `dec end - i` proves it terminates. +// Navigation and layout use recursive scans over newline-delimited text. + +// Count newline bytes in `text[i..end]`. fn count_nl(text: &String, i: u64, end: u64) -> u64 req i <= end && end <= text.len() ens result <= end - i @@ -177,12 +88,7 @@ fn count_nl(text: &String, i: u64, end: u64) -> u64 } } -// The SPEC twin of the exec `count_nl` (#270, mirroring the #126 `spec_line_start` -// precedent): the same `\n`-counting forward scan as a `spec fn` so `count_nl`'s -// `ens` can NAME its exact value — this kills the F-IDENT identity mutants -// (`return i`/`return end`) that the bounds-only `ens` could not. The base case is -// `i >= end` (a spec fn carries no `req`, so `dec end - i` must be well-founded for -// every `i`); the recursion mirrors `count_nl` byte-for-byte. PROVEN to terminate. +// Specification counterpart to `count_nl`, used to state its exact result (#270). spec fn spec_count_nl(text: &String, i: u64, end: u64) -> u64 dec end - i { @@ -197,11 +103,8 @@ spec fn spec_count_nl(text: &String, i: u64, end: u64) -> u64 } } -// The start index of the line that `target` sits on: a verified FORWARD scan from -// `i` to `target`, carrying `acc` = the index just after the most recent newline -// seen (0 if none). Each step examines `byte_at(i)` at the plain index `i` (no -// compound-index cast). PROVEN: acc <= result <= target — the line start is -// at-or-before the target and never before the running accumulator. `dec target - i`. +// Find the start of the line containing `target`. `acc` is the position after +// the most recent newline seen by the scan. fn line_start(text: &String, i: u64, target: u64, acc: u64) -> u64 req i <= target && target <= text.len() && acc <= i ens result <= target @@ -222,12 +125,8 @@ fn line_start(text: &String, i: u64, target: u64, acc: u64) -> u64 } } -// The index of the next newline at-or-after position i, or n (= text.len()) if -// none — the end of the line position i sits on. A verified FORWARD scan examining -// `byte_at(i)` at the plain index. PROVEN: i <= result <= n. The length `n` is -// passed as a u64 so the `dec n - i` measure is a plain arithmetic term (a `.len()` -// call is exec-mode, not admitted in a `dec`). `req n == text.len()` ties it to the -// real length. +// Find the next newline at or after `i`, or return `n` if there is none. Passing +// `n` explicitly keeps the termination measure in specification mode. fn line_end(text: &String, i: u64, n: u64) -> u64 req i <= n && n == text.len() ens result >= i @@ -248,12 +147,7 @@ fn line_end(text: &String, i: u64, n: u64) -> u64 } } -// The SPEC twin of the exec `line_end` (#270): the same next-newline forward scan -// as a `spec fn` so `line_end`'s `ens` can NAME its exact value — pinning the -// result KILLS the F-IDENT identity mutants (`return i`/`return n` both satisfied -// the bounds-only `ens result >= i && result <= n`, the move-down-class hole). The -// base case is `i >= n` (a spec fn carries no `req`, `dec n - i` well-founded for -// every `i`); the recursion mirrors `line_end` byte-for-byte. PROVEN to terminate. +// Specification counterpart to `line_end` (#270). spec fn spec_line_end(text: &String, i: u64, n: u64) -> u64 dec n - i { @@ -268,9 +162,7 @@ spec fn spec_line_end(text: &String, i: u64, n: u64) -> u64 } } -// min of two u64s — PROVEN result is <= both AND equals one of them (so it is a -// real minimum, not a constant). Used to CLAMP the target column to the -// destination line's length in move_up/move_down. +// Minimum used to clamp a requested column to the destination line. fn min2(a: u64, b: u64) -> u64 req true ens result <= a @@ -282,23 +174,14 @@ fn min2(a: u64, b: u64) -> u64 if a <= b { a } else { b } } -// The SPEC twin of the exec `min2` (#270): a pure `spec fn` mirroring the -// two-way min, so `min2`'s `ens` NAMES its exact value AND the navigation spec -// twins (`spec_move_up_target`/`spec_move_down_target`) can express the clamped -// target column compositionally. (`min2`'s identity mutants are already killed by -// `result <= a && result <= b`; the twin hardens it to an exact pin and supplies -// the combinator the nav pins compose with.) +// Specification counterpart to `min2` (#270). spec fn spec_min2(a: u64, b: u64) -> u64 dec a { if a <= b { a } else { b } } -// The cursor's ROW (0-based): the count of newlines in text[0..cursor]. Delegates -// to the verified `count_nl` forward scan. PROVEN: result <= cursor, AND the -// boundary teeth (if the text begins with a newline and the cursor is past it, the -// row is >= 1) — a plain `byte_at(0)` index that kills the trivial `return 0` -// mutant (non-vacuous), buildable at L1. +// Return the zero-based cursor row by counting preceding newlines. fn cursor_row(b: &Buffer) -> u64 req true ens result <= b.cursor @@ -309,27 +192,15 @@ fn cursor_row(b: &Buffer) -> u64 count_nl(&b.text, 0, b.cursor) } -// The SPEC twin of the exec `cursor_row` (#270): the cursor's row is EXACTLY the -// `spec_count_nl` of the buffer prefix — the same value the exec scan computes -// (tied by `count_nl`'s own `ens result == spec_count_nl(...)`). Pinning the row -// exactly KILLS cursor_row's F-IDENT/return-0 mutants (the prior bounds-only `ens` -// could not). Takes `(&String, u64)` (NOT `&Buffer`) so the spec-fn pool stays -// free of the `Buffer` ADT dep — mirroring `spec_line_start`/`spec_count_nl` over -// `&String` (the #126 `cursor_col` precedent). `dec cursor` is the trivial measure. +// Specification counterpart to `cursor_row`; it takes text and cursor separately +// to avoid a `Buffer` dependency in the specification-function pool. spec fn spec_cursor_row(text: &String, cursor: u64) -> u64 dec cursor { spec_count_nl(text, 0, cursor) } -// The SPEC twin of the exec `line_start` (#126): the same `\n`-scan over the -// `&String` prefix, as a `spec fn` so `cursor_col`'s `ens` can NAME the exact line -// start. The base case is `i >= target` (a spec fn carries no `req`, so the -// `decreases target - i` must be well-founded for every `i`); the recursion mirrors -// `line_start` byte-for-byte (`acc` jumps to `i + 1` just past a newline). PROVEN to -// terminate (`dec target - i`). The exec `line_start`'s `ens result == spec_line_- -// start(text, i, target, acc)` (below) ties the two together, so a contract that -// names `spec_line_start` is pinned to the value the exec scan computes. +// Specification counterpart to `line_start` (#126). spec fn spec_line_start(text: &String, i: u64, target: u64, acc: u64) -> u64 dec target - i { @@ -344,15 +215,7 @@ spec fn spec_line_start(text: &String, i: u64, target: u64, acc: u64) -> u64 } } -// The cursor's COLUMN (0-based): the distance from the line start to the cursor. -// Delegates to the verified `line_start` forward scan. PINNED (#126): the column is -// EXACTLY `cursor - spec_line_start(text, 0, cursor, 0)` — the distance from the -// PROVEN line start to the cursor — not merely bounded by `<= cursor`. This kills -// the return-0 (and return-cursor) mutant: a body `0` or `b.cursor` no longer -// satisfies the equality (it must equal the real `cursor - line_start`), so the §7 -// gate scores cursor_col 4/4 (the survivor is gone). The body is UNCHANGED -// (`b.cursor - line_start(...)`); only the contract tightens. Mirrors cursor_row's -// tight-contract precedent. +// Return the zero-based cursor column: the distance from the current line start. fn cursor_col(b: &Buffer) -> u64 req true ens result == b.cursor - spec_line_start(&b.text, 0, b.cursor, 0) @@ -363,23 +226,9 @@ fn cursor_col(b: &Buffer) -> u64 b.cursor - ls } -// The SPEC twin of the exec `move_up`'s TARGET CURSOR (#270): the exact cursor the -// up-navigation computes for a `(text, cursor)`, as a pure `spec fn` composed over -// the verified `spec_line_start` + `spec_min2`. It mirrors `move_up`'s body -// branch-for-branch: the `ls == 0` first-line arm leaves the cursor put (the `b` -// no-op), else it clamps the column to the previous line's length. PINNING -// `move_up`'s `ens result.cursor == spec_move_up_target(&b.text, b.cursor)` KILLS -// the F-IDENT `return b` mutant — the review's `move_up` weak-contract escape — and -// every other body mutant that would shift the cursor (the §7 gate now scores it -// against the EXACT target, not the loose `<= b.cursor` bound). Mirrors the -// `cursor_col`/#126 spec-twin precedent. -// The previous-line-relative branch of the up target, given the CURRENT line -// start `ls` already computed (a helper keeps `spec_move_up_target` a single -// expression — the lowerer cannot cast-wrap a body whose head is a `let`, so the -// nav twins are written `let`-free; the `0 +` keeps both `if` arms the same `int` -// type before the trailing `as u64`). The `ls == 0` first-line arm leaves the -// cursor put (`move_up`'s `b` no-op); otherwise the column clamps to the previous -// line's length. `dec cursor` is the trivial well-founded measure (non-recursive). +// Compute the upward-navigation target from a known line start. This helper is +// expression-only because the lowerer cannot cast-wrap a body headed by `let`. +// `0 + cursor` keeps the two branches at the same integer type. spec fn spec_move_up_from(text: &String, cursor: u64, ls: u64) -> u64 dec cursor { @@ -391,25 +240,15 @@ spec fn spec_move_up_from(text: &String, cursor: u64, ls: u64) -> u64 } } -// The SPEC twin of the exec `move_up`'s TARGET CURSOR (#270): the exact cursor the -// up-navigation computes for a `(text, cursor)`, composed over the verified -// `spec_line_start` + `spec_min2` (through `spec_move_up_from` with the current -// line start). It mirrors `move_up`'s body branch-for-branch. PINNING `move_up`'s -// `ens result.cursor == spec_move_up_target(&b.text, b.cursor)` KILLS the F-IDENT -// `return b` mutant — the review's `move_up` weak-contract escape — and every -// other body mutant that would shift the cursor (the §7 gate now scores it against -// the EXACT target, not the loose `<= b.cursor` bound). Mirrors `cursor_col`/#126. +// Exact target for upward navigation (#270). spec fn spec_move_up_target(text: &String, cursor: u64) -> u64 dec cursor { spec_move_up_from(text, cursor, spec_line_start(text, 0, cursor, 0)) } -// The next-line-relative branch of the down target, given the CURRENT line end -// `cur_end` (= `spec_line_end(text, cursor, text.len())`) already computed. The -// `cur_end == n` last-line arm leaves the cursor put (`move_down`'s `b` no-op); -// otherwise the column clamps to the next line's length. `let`-free + symmetric -// `int` arms for the same lowerer reasons as `spec_move_up_from`. +// Compute the downward-navigation target from a known line end. It uses the same +// expression-only form and integer-branch convention as `spec_move_up_from`. spec fn spec_move_down_from(text: &String, cursor: u64, n: u64, cur_end: u64) -> u64 dec cursor { @@ -422,11 +261,7 @@ spec fn spec_move_down_from(text: &String, cursor: u64, n: u64, cur_end: u64) -> } } -// The SPEC twin of the exec `move_down`'s TARGET CURSOR (#270): the exact cursor -// the down-navigation computes, composed over `spec_line_start` + `spec_line_end` + -// `spec_min2` (through `spec_move_down_from`). PINNING `move_down`'s `ens -// result.cursor == spec_move_down_target(&b.text, b.cursor)` KILLS the F-IDENT -// `return b` mutant. +// Exact target for downward navigation (#270). spec fn spec_move_down_target(text: &String, cursor: u64) -> u64 dec cursor { @@ -434,15 +269,8 @@ spec fn spec_move_down_target(text: &String, cursor: u64) -> u64 spec_line_end(text, cursor, text.len())) } -// Move the cursor to (approximately) the same column on the PREVIOUS line. Find -// the current line start; if it is 0 the cursor is already on the first line -// (no-op). Otherwise the newline before the start ends the previous line; find -// that line's start, clamp the target column to its length, and set the new -// cursor. PROVEN: the text is unchanged (length identity — a dropped-text mutant -// fails), the new cursor never advances past the old one (it moves up), AND (#270) -// the new cursor is EXACTLY `spec_move_up_target` — the exact-target pin that kills -// the F-IDENT `return b` mutant (the review's weak-contract escape). The new cursor -// stays in bounds (the Buffer type invariant — `cursor <= text.len()`). +// Move to the previous line, preserving the column when possible and clamping it +// to the previous line's length. The postcondition fixes the exact target (#270). fn move_up(b: Buffer) -> Buffer req true ens result.text.len() == b.text.len() @@ -463,13 +291,8 @@ fn move_up(b: Buffer) -> Buffer } } -// Move the cursor to (approximately) the same column on the NEXT line. Find the -// current line end; if it is the text end there is no next line (no-op). -// Otherwise the byte after the end starts the next line; find that line's end, -// clamp the target column to its length, and set the new cursor. PROVEN: the text -// is unchanged, the new cursor stays in bounds (`cursor <= text.len()` — the -// Buffer invariant), AND (#270) the new cursor is EXACTLY `spec_move_down_target` -// — the exact-target pin that kills the F-IDENT `return b` mutant. +// Move to the next line, preserving the column when possible and clamping it to +// the next line's length. The postcondition fixes the exact target (#270). fn move_down(b: Buffer) -> Buffer req true ens result.text.len() == b.text.len() @@ -492,11 +315,7 @@ fn move_down(b: Buffer) -> Buffer } } -// Convert a 0-based coordinate to the 1-based form ANSI cursor positioning needs. -// PROVEN: `result == x + 1` — so an off-by-one mutant (`+ 0` / `+ 2`) FAILS the -// postcondition and is killed. The 1-based conversion is thus PROVEN here, not an -// unverified literal floating in render_frame (that lets render_frame certify L3 -// without a surviving off-by-one). +// Convert a zero-based coordinate to ANSI's one-based coordinate system. fn to_1based(x: u64) -> u64 req x < 1_000_000 ens result == x + 1 @@ -505,44 +324,10 @@ fn to_1based(x: u64) -> u64 x + 1 } -// --------------------------------------------------------------------------- -// THE VERIFIED RENDER-FRAME (L3, #125) — the multi-line DISPLAY logic, PROVEN. -// --------------------------------------------------------------------------- - -// Build the full-screen ANSI frame as a String (no syscall — pure construction): -// * "\x1b[2J\x1b[H" — C1 escape literals: clear the screen + home; -// * b.text — the buffer contents (carried in by concat — the -// `\n` bytes render as terminal line breaks, so a -// multi-line buffer shows as multiple lines); -// * "\x1b[" + (row+1) + ";" + (col+1) + "H" -// — position the terminal cursor at row+1, col+1 (the -// C4 decimal coordinates of the VERIFIED cursor_row -// / cursor_col, 1-based via the PROVEN to_1based). -// PROVEN (non-vacuous): the frame is at least as long as the buffer text — -// `result.len() >= b.text.len()` — i.e. the whole buffer text is carried into the -// frame (a dropped-text mutant shortens the frame and fails the `ens`). The `req` -// bounds the text so every bounded `concat` stays within the §4.2 cage; the C4 -// coordinate concat discharges because `u64_to_string`'s `ens` bounds the -// formatted length <= 20 (blocker #105). VERIFIED Thermite (L3) — the multi-line -// display logic is proven, NOT a trusted wrapper. The actual `write` syscall -// (write_frame) is the trusted boundary. -// -// CONTENT PAYLOAD PIN (#276, `bytes_eq` — REQ-18, AC-15): the frame carries the -// WHOLE buffer text VERBATIM, byte-for-byte, at the post-clear offset. `clear` is -// the fixed 7-byte escape `"\x1b[2J\x1b[H"` (ESC `[` `2` `J` ESC `[` `H`), so the -// body `b.text` lands at offset 7 — pinned by -// `bytes_eq(result, b.text, 7, 0, b.text.len())` (the bare whole-result + bare-field -// operand forms, #279's sanctioned shapes lowering through both L3 + L1). This is the STRONGEST -// contiguous content claim: the prior `result.len() >= b.text.len()` proved only -// that the frame is long enough; this proves the buffer text is reproduced -// EXACTLY (a mutant that scrambles, truncates, or substitutes the body fails the -// window — a length-only `ens` cannot catch that). Discharged via -// `lemma_bytes_eq_bridge`. RESIDUAL (honest): the trailing `pos` cursor-coordinate -// escape `"\x1b["++(row)++";"++(col)++"H"` is NOT byte-pinned — its bytes depend on -// the runtime decimal `to_string()` of the verified row/col (variable length), and -// `bytes_eq` pins a FIXED-offset/FIXED-length window, not a content-dependent -// suffix. The leading `clear` is a compile-time constant and the body is fully -// pinned; the unpinned residual is exactly the format-dependent coordinate tail. +// Build the ANSI clear-screen frame, append the buffer, and position the cursor. +// The buffer begins at byte 7 after the fixed clear sequence, so `bytes_eq` +// verifies the complete payload (REQ-18, AC-15). The variable-length coordinate +// suffix is covered by its component contracts rather than a fixed byte window. fn render_frame(b: &Buffer) -> String req b.text.len() <= 900_000 ens result.len() >= b.text.len() @@ -557,25 +342,9 @@ fn render_frame(b: &Buffer) -> String clear.concat(body).concat(pos) } -// --------------------------------------------------------------------------- -// THE VERIFIED DECODE (L3, #125) — the editor's INPUT logic, PROVEN PURE + TOTAL. -// --------------------------------------------------------------------------- - -// Decode the raw read bytes (b0, b1, b2 — a keystroke, EOF=256, or an arrow escape -// sequence) to a single key code. A PURE TOTAL function over the bytes: -// * the arrow escape sequence 0x1b 0x5b 0x41..0x44 -> a synthetic code -// up=1000, down=1001, right=1002, left=1003 (the 0x41..0x44 tail offset); -// * Enter — CR (13) or LF (10) -> 1004 (insert a newline); -// * a printable byte 32..126 -> itself (insert it); -// * 127 (DEL) -> backspace; -// * 19 (Ctrl-S) -> 19 (save); -// * 17 (Ctrl-Q) -> 17 (quit); -// * anything else -> 0 (a no-op key). -// PROVEN (non-vacuous): a printable non-ESC byte decodes to itself; Enter (CR/LF) -// decodes to 1004; Ctrl-S to 19; Ctrl-Q to 17; the arrows to 1000..1003; and the -// result is always a bounded code (`result <= 1004`). The mapping is total and -// each special case is pinned (a decode that dropped a case fails the `ens`). -// VERIFIED Thermite (L3) — the keystroke interpretation is proven, not trusted. +// Decode a keystroke into the editor's internal key code. Printable bytes and +// Ctrl-S/Ctrl-Q retain their values; arrows map to 1000–1003, Enter maps to +// 1004, and unsupported input maps to zero. fn decode(b0: u64, b1: u64, b2: u64) -> u64 req true ens b0 < 32 || b0 > 126 || b0 == 27 || result == b0 @@ -642,32 +411,25 @@ fn decode(b0: u64, b1: u64, b2: u64) -> u64 } } -// --------------------------------------------------------------------------- -// THE MINIMAL TRUSTED SYSCALL BOUNDARY (L1 / extern-C, self-contained). -// --------------------------------------------------------------------------- - // Put the terminal into raw mode via extern-C tcgetattr/tcsetattr (libc through -// std). Returns a status (0 = ok / not-a-TTY no-op, 1 = error). Trusted-by-fiat; -// the foreign body handles a non-TTY stdin gracefully. #57-confined to `fx term` -// — the termios wrappers issue ONLY the `ioctl` syscall (16), which `term` grants -// (runtime-sandbox.md REQ-7, #106), so the editor runs FULLY sandboxed. +// std). Status is zero for success or a non-TTY no-op and one for error. The +// boundary is limited to the `term` effect and ioctl syscall (#57, REQ-7). #[boundary("os::raw_mode_on")] fn raw_mode_on() -> u64 req true ens result <= 1 fx term ; -// Restore the terminal's original mode. MUST run on exit. Trusted, #57-confined. +// Restore the terminal's original mode. #[boundary("os::raw_mode_off")] fn raw_mode_off() -> u64 req true ens result <= 1 fx term ; -// Read one keystroke, returning the raw bytes PACKED into a u64 for `decode`: b0 in +// Read one keystroke, returning the raw bytes packed into a u64 for `decode`: b0 in // bits 0..9, b1 in 9..18, b2 in 18..27. A plain key reads 1 byte; an ESC reads the -// 2-byte arrow tail. The packed maximum fits in 27 bits. Trusted, #57-confined to -// the `read` syscall set. +// two-byte arrow tail. The packed maximum fits in 27 bits. #[boundary("os::read_key_raw")] fn read_key_raw() -> u64 req true ens result <= 134_217_727 @@ -675,46 +437,31 @@ fn decode(b0: u64, b1: u64, b2: u64) -> u64 ; // Write the rendered frame String to stdout (and flush). Returns a status u64 (0 = -// ok, 1 = I/O error). Trusted, #57-confined to the `write` syscall set. +// ok, 1 = I/O error). #[boundary("os::write_frame")] fn write_frame(s: String) -> u64 req true ens result <= 1 fx write(output) ; -// LOAD the initial buffer content from the editor's fixed demo file (the -// THERMITE_EDITOR_FILE env path, else /tmp/thermite_editor.txt) into a String -// (the multi-line `\n` bytes preserved). A missing file yields the empty string (a -// fresh buffer) — the honest arm, no crash. The v0.1 `--entry run` synthesizes no -// path arg, so the load source is fixed. Trusted, #57-confined to the `read`/`open` -// syscall set (#125). +// Load the initial buffer from `THERMITE_EDITOR_FILE`, or from +// `/tmp/thermite_editor.txt` if the variable is unset. A missing file produces +// an empty buffer. #[boundary("os::read_file")] fn read_file() -> String req true ens result.len() <= 1_000_000 fx read(input) ; -// SAVE the buffer String's bytes (incl. the `\n` line breaks) to the editor's fixed -// demo file. Returns a status u64 (0 = ok, 1 = I/O error). Bound to Ctrl-S in the -// loop. Trusted, #57-confined to the `open`/`write` syscall set (#125). +// Save the buffer, including newline bytes, to the configured editor file. #[boundary("os::write_file")] fn write_file(content: String) -> u64 req true ens result <= 1 fx write(output) ; -// --------------------------------------------------------------------------- -// THE EVENT LOOP `run` (L1 — the honest `fx diverge` seam). -// --------------------------------------------------------------------------- - -// The interactive multi-line loop: enter raw mode; LOAD the initial buffer from the -// file (read_file); then read a keystroke, DECODE it (the L3-verified decode), -// dispatch the decoded code to the L3-verified edit + nav ops (insert_str — incl. -// the newline insert on Enter, backspace, move_left/right, move_up/down), build the -// multi-line frame (the L3-verified render_frame positioning the cursor by the -// verified row/col), write it, SAVE on Ctrl-S (write_file), and repeat until Ctrl-Q -// (decode -> 17) breaks; finally restore the terminal. This loop is the honest `fx -// diverge` seam; the LOGIC it drives is the L3-proven core. +// Run the interactive loop until Ctrl-Q, saving on Ctrl-S. This function is L1 +// because it performs boundary I/O and carries the `diverge` effect. fn run() -> u64 req true ens result <= 1 diff --git a/examples/formatter/README.md b/examples/formatter/README.md index e6444e032..79fc41d9c 100644 --- a/examples/formatter/README.md +++ b/examples/formatter/README.md @@ -1,53 +1,53 @@ -# Verified `u64` decimal formatter — acceptance program 1 of 3 (#103) +# Verified `u64` decimal formatter -`format.th` is a verified `u64` → decimal-`String` formatter, built ENTIRELY from -shipped verified primitives: cluster **C4**'s `n.to_string()` -(`.design/basis/07-strings.md` REQ-8) with its gold-standard round-trip contract. -No new toolchain feature — this is C4 composed into a real program. +[`format.th`](format.th) wraps the verified `u64::to_string` primitive. Its +contract states that parsing the emitted decimal bytes returns the original +number. -## What is proven +## Verified behavior ```thermite fn format(n: u64) -> String req true - ens parse_be(result) == n // THE ROUND-TRIP: the decimal bytes parse back to n - ens result.len() >= 1 // and the string is non-empty + ens parse_be(result) == n + ens result.len() >= 1 fx alloc { n.to_string() } ``` -`forge check format.th` certifies `format` at **L3**: for ALL `n`, the produced -decimal byte sequence parses back to exactly `n` (`parse_be` is the MSB-first -read-order parse). This is the gold standard, not a floor — a formatter that -emitted a wrong digit would produce bytes that do not parse back to `n`, so the -`ens` is real teeth (non-vacuity is pinned in -`forge/tests/string_format_conformance.rs`). +`forge check` certifies `format` at L3 for every `u64`. The round-trip +postcondition catches an omitted or incorrect digit, while the length +postcondition rules out an empty representation of zero. + +```sh +cargo run -q -p forge -- check examples/formatter/format.th +``` ## Run it -```bash -cargo run -p forge -- build examples/formatter/format.th --entry format_42 -# → format_42() = TString { data: [52, 50] } == "42" -cargo run -p forge -- build examples/formatter/format.th --entry format_0 -# → format_0() = TString { data: [48] } == "0" -cargo run -p forge -- build examples/formatter/format.th --entry format_1000000 -# → format_1000000() = TString { data: [49, 48, 48, 48, 48, 48, 48] } == "1000000" +```sh +cargo run -q -p forge -- build examples/formatter/format.th --entry format_42 --out ./fmt +./fmt +``` + +Other entries exercise zero and a longer value: + +```sh +cargo run -q -p forge -- build examples/formatter/format.th --entry format_0 +cargo run -q -p forge -- build examples/formatter/format.th --entry format_1000000 ``` -`forge build --entry ` lowers to runtime-checked Rust, compiles with `rustc`, -and runs the binary, printing the result's bytes. v1's `to_string` builds the -digits LSB-first then reverses to the human-readable MSB-first display order -(REQ-8 / blocker #96), so the bytes read left-to-right as the decimal: `52`='4', -`50`='2', `48`='0', `49`='1'. +The executable prints the underlying byte representation: -The entry points (`format_42` etc.) are zero-argument wrappers that fix `n` -internally — the v0.1 deterministic runner synthesizes no `String`/`u64` argument -for an entry (the `editor.th` `run` precedent), so an entry takes no parameters and -builds its inputs in the body. +```text +format_42() = TString { data: [52, 50] } +format_0() = TString { data: [48] } +format_1000000() = TString { data: [49, 48, 48, 48, 48, 48, 48] } +``` -## Verification +The implementation builds digits in least-significant-first order and reverses +them for display. The zero-argument entry functions supply fixed inputs because +the deterministic runner does not synthesize `String` or `u64` arguments. -Grounded by `forge/tests/acceptance_programs.rs`: -`formatter_round_trip_certifies_l3` (forge check → L3) and -`formatter_builds_and_runs_each_value` (build + run 42/0/1000000). The formatter -composes CLEANLY end-to-end — both `forge check` and `forge build` succeed. +Certification and executable behavior are tested in +`forge/tests/acceptance_programs.rs`. diff --git a/examples/formatter/format.th b/examples/formatter/format.th index 1a3fed49d..f1df734ec 100644 --- a/examples/formatter/format.th +++ b/examples/formatter/format.th @@ -1,24 +1,7 @@ -// ACCEPTANCE PROGRAM 1 of 3 (crosslink #103) — a verified u64 decimal FORMATTER. +// Verified decimal formatter example (crosslink #103). // -// THE COMPOSE-ANY-PROGRAM PROOF: this program is built ENTIRELY from shipped -// verified primitives — cluster C4's `n.to_string()` (u64 -> decimal String, -// .design/basis/07-strings.md REQ-8) with its GOLD-STANDARD round-trip contract -// `parse_be(result) == n` (the produced bytes parse back to exactly n). No new -// toolchain feature; the formatter is C4 composed into a real program. -// -// DELIVERABLE (a) — `forge check format.th` certifies `format` at L3 with the -// MEANINGFUL round-trip contract: for ALL n, the decimal bytes parse back to n. -// DELIVERABLE (b) — `forge build format.th --entry format_42` (etc.) COMPILES + -// RUNS, printing the human-readable MSB-first decimal: 42 -> [52, 50] == "42", -// 0 -> [48] == "0", 1000000 -> [49,48,48,48,48,48,48] == "1000000" (REQ-8 / -// blocker #96: to_string builds LSB-first then reverses to the readable order). - -// THE FORMATTER (forge check -> L3): the round-trip is the gold standard — the -// decimal byte sequence parses back to exactly `n` (parse_be is the MSB-first -// read-order parse). A formatter that emitted a wrong digit would produce bytes -// that do NOT parse back to n, so the `ens` is real teeth (R-DEFER-9 non-vacuity, -// pinned in forge/tests/string_format_conformance.rs's overclaim negative). The -// length floor `result.len() >= 1` pins that even 0 yields a non-empty string. +// The round-trip postcondition requires the emitted decimal bytes to parse back +// to `n`. The length condition covers the zero case. fn format(n: u64) -> String req true ens parse_be(result) == n @@ -28,10 +11,7 @@ fn format(n: u64) -> String n.to_string() } -// --- Runnable entry points (forge build --entry ): zero-arg wrappers that -// fix `n` internally (the v0.1 deterministic runner synthesizes no String/u64 -// arg for an entry, editor.th `run` precedent), each carrying the round-trip ens -// for the fixed value. The built binary prints the TString's decimal bytes. --- +// Fixed-input entries are used because the runner does not synthesize arguments. fn format_42() -> String req true diff --git a/examples/parser/README.md b/examples/parser/README.md index db59c17b2..36b7f95af 100644 --- a/examples/parser/README.md +++ b/examples/parser/README.md @@ -1,76 +1,57 @@ -# Verified line / CSV parser — acceptance program 3 of 3 (#103) +# Verified line and CSV parser -`parse_lines.th` composes shipped verified primitives — cluster **C5**'s `split` -(`String` → `Vec` on a separator byte, `.design/basis/07-strings.md` -REQ-15) over cluster **C6**'s non-`Copy` `Vec` wrapper (`TVecTString`), -plus C5's `contains` substring predicate (REQ-13). No new toolchain feature is -needed to CERTIFY. +[`parse_lines.th`](parse_lines.th) combines the verified string `split` +operation, `Vec`, and the `contains` predicate. -## What is proven +## Verified behavior ```thermite fn fields(s: String, sep: u64) -> Vec req true - ens result.len() == 1 + count_sep(s, sep) // the EXACT piece count + ens result.len() == 1 + count_sep(s, sep) fx alloc { s.split(sep) } fn has_sep(s: &String, sep: &String) -> bool req true - ens result == contains_sub(s, sep) // the substring relation + ens result == contains_sub(s, sep) fx pure { s.contains(sep) } ``` -- `has_sep` certifies **L3** through the FULL `forge check` §7-mutation-scored - ladder (the substring predicate is real teeth — a broken `contains` fails). -- `fields` (the split count-bound) certifies **L3 under real verus** on the - lowering (GROUNDED `7 verified, 0 errors`, REQ-15). Its thin `{ s.split(sep) }` - body delegates entirely to the proven `split` method, so `forge check`'s §7 gate - cannot mutation-score it (no scoreable body mutant — the documented split-caller - precedent in `forge/tests/string_search_conformance.rs`); its L3 is established by - running verus on the emitted Verus source. +`has_sep` certifies at L3 through the normal `forge check` path. The emitted +Verus lowering for `fields` also verifies at L3 (`7 verified, 0 errors`). +Because `fields` delegates directly to `split`, it has no scoreable scalar body +mutant; its proof is checked at the lowering level. -## Run it (the split core) - -```bash -cargo run -p forge -- build examples/parser/parse_lines.th --entry split_abc # see the gap below first +```sh +cargo run -q -p forge -- check examples/parser/parse_lines.th ``` -The split core RUNS — "a,b,c" split on ',' (byte 44) → 3 pieces: +## Run the split demo -``` -split_abc() = TVecTString { data: [TString { data: [97] }, TString { data: [98] }, TString { data: [99] }] } -# "a"=97 "b"=98 "c"=99 → 3 pieces +```sh +cargo run -q -p forge -- build examples/parser/parse_lines.th --entry split_abc --out ./parse +./parse ``` -The `split_abc` entry's `ens result.len() >= 1` is a non-vacuous floor (every split -yields at least one piece) that lowers to an L1 runtime check WITHOUT naming a C5 -spec fn, so the runnable binary compiles. +The result is a three-element `TVecTString` containing `a`, `b`, and `c`. +`split_abc` uses the runtime-checkable postcondition `result.len() >= 1`. -## FORCING-FUNCTION FINDING — the count-bound contracts cannot `forge build` +## Current build limitation -`forge build parse_lines.th` (the full program) **fails to compile**: +Building the complete program currently fails because two C5 specification +helpers lack L1 runtime forms: -``` +```text error[E0425]: cannot find function `count_sep` in this scope error[E0425]: cannot find function `contains_sub` in this scope ``` -Same class as the calculator's gap: `forge build` lowers every function's contract -to a runtime `thermite_check!`, and `fields`/`has_sep` name the **C5 spec fns** -`count_sep` / `contains_sub`, which `thermite-lower`'s `emit_string_runtime_l1` -does **not** emit an L1 runnable form for (only C4's numfmt spec fns got one). This -belongs to the **C5 / #102 build-side cluster** (the L1 mirror of the C5 contract -spec fns `count_sep` / `sep_free` / `occurs_at` / `contains_sub`). When that L1 -lowering lands, `parse_lines.th` builds + runs end-to-end with the count-bound -contract enforced at runtime. The C5 split/contains METHODS already have L1 forms -(the split core above runs); only the contract SPEC fns lack one. - -## Verification +The `split` and `contains` methods already have runnable implementations. The +missing work is limited to runtime forms for contract helpers such as +`count_sep`, `sep_free`, `occurs_at`, and `contains_sub`, described in +`.design/basis/07-strings.md`. -`forge/tests/acceptance_programs.rs`: `parser_contains_predicate_certifies_l3` -(forge check → L3 for `has_sep`), `parser_split_count_bound_verifies_under_real_verus` -(verus → L3 for `fields`), `parser_split_core_builds_and_runs_three_pieces` (the -split core RUNS → 3 pieces), and `parser_build_is_blocked_by_missing_l1_count_sep` -(PINS the gap; flips to assert end-to-end build when the C5 L1 lowering lands). +The certification, runnable core, and build limitation are covered in +`forge/tests/acceptance_programs.rs`. diff --git a/examples/parser/parse_lines.th b/examples/parser/parse_lines.th index fa62a1c34..416ee2d35 100644 --- a/examples/parser/parse_lines.th +++ b/examples/parser/parse_lines.th @@ -1,33 +1,8 @@ -// ACCEPTANCE PROGRAM 3 of 3 (crosslink #103) — a verified line / CSV PARSER. +// Verified line-splitting example (crosslink #103). // -// THE COMPOSE-ANY-PROGRAM PROOF: this program composes shipped verified -// primitives — cluster C5's `split` (String -> Vec on a separator byte, -// .design/basis/07-strings.md REQ-15) with its count-bound + sep-free contract, -// over cluster C6's non-Copy `Vec` wrapper (TVecTString), plus C5's -// `contains` substring predicate (REQ-13). No new toolchain feature. -// -// DELIVERABLE (a) — `forge check parse_lines.th` certifies `has_sep` at L3 via -// the §7-mutation-scored ladder (the `contains` predicate is real teeth). The -// `fields` split fn certifies L3 under REAL VERUS (the count-bound + sep-free -// contract, GROUNDED `7 verified, 0 errors` per REQ-15) — but its thin -// `{ s.split(sep) }` body delegates entirely to the proven method, so `forge -// check`'s §7 gate cannot mutation-score it (no scoreable body mutant, the -// documented parse_u64 / split-caller precedent); its L3 is established by verus -// on the lowering, as forge/tests/string_search_conformance.rs grounds `parts`. -// -// DELIVERABLE (b) — `forge build parse_lines.th --entry split_abc` COMPILES + -// RUNS: "a,b,c" split on ',' (byte 44) -> 3 pieces ([97],[98],[99] == "a","b","c"). -// The entry's `ens result.len() >= 1` is a non-vacuous floor that lowers to an -// L1 runtime check WITHOUT naming a C5 spec fn (count_sep has no L1 form), so the -// runnable binary compiles. (A `ens result.len() == 1 + count_sep(s, sep)` entry -// would NOT build — count_sep, like the other C5/C7 contract spec fns, has no L1 -// runnable emission; see README.md for that forcing-function finding.) - -// THE SPLITTER (forge check -> L3 under real verus): split on a separator byte -// into a Vec. The count-bound `result.len() == 1 + count_sep(s, sep)` -// pins the EXACT piece count (always 1 + the number of separators); a split that -// dropped a piece would violate it (REQ-15 non-vacuity, GROUNDED). `sep` is the -// separator byte in the byte_at -> u64 zero-extend convention. +// `fields` verifies the number of returned fields against the separator count. +// `count_sep` has no L1 lowering, so the runnable entry uses a simpler length +// postcondition. fn fields(s: String, sep: u64) -> Vec req true ens result.len() == 1 + count_sep(s, sep) @@ -36,11 +11,8 @@ fn fields(s: String, sep: u64) -> Vec s.split(sep) } -// A field-presence predicate (forge check -> L3, §7-mutation-scored): does `s` -// contain the separator string? The `ens result == contains_sub(s, sep)` is the -// substring relation — a broken `contains` that always returned true/false would -// fail the §7 gate (REQ-13 non-vacuity, the `has` form in string_search). This is -// the MEANINGFUL contract that certifies L3 through the full `forge check` ladder. +// Report whether `s` contains `sep`; the postcondition fixes the result to the +// specification-level substring relation. fn has_sep(s: &String, sep: &String) -> bool req true ens result == contains_sub(s, sep) @@ -49,11 +21,7 @@ fn has_sep(s: &String, sep: &String) -> bool s.contains(sep) } -// Runnable entry (forge build --entry split_abc): split "a,b,c" on ',' (byte 44), -// fixed internally (the v0.1 runner synthesizes no String/Vec arg). The built -// binary prints the Vec result — 3 pieces. The `ens result.len() >= 1` -// floor is non-vacuous (every split yields at least one piece, REQ-15) and lowers -// to an L1 runtime check that names no C5 spec fn, so the binary compiles + runs. +// Runnable fixed-input entry: split "a,b,c" on comma (byte 44). fn split_abc() -> Vec req true ens result.len() >= 1 diff --git a/forge/src/accessibility.rs b/forge/src/accessibility.rs index 55509d58a..f667f1d6b 100644 --- a/forge/src/accessibility.rs +++ b/forge/src/accessibility.rs @@ -5,7 +5,7 @@ //! registry-free call `wf()`) carries an ACCESSIBILITY obligation: the relation must //! be well-founded on the recursion's carrier for the recursion to be admitted. That proof //! is expensive and re-derivable, and — crucially — it is shared across every item that -//! recurses under the SAME (relation, carrier): the well-foundedness of `<` on `u32` is one +//! recurses under the same (relation, carrier): the well-foundedness of `<` on `u32` is one //! fact, not one-per-item. So it is content-addressed by the (relation, carrier) pair and //! cached ([`crate::cache::AccessibilityProof`]) under the same `CHECK_SCHEMA_VERSION` gate //! key as the per-item proof cache. @@ -13,7 +13,7 @@ //! This module is the CONSUMER that populates that cache from the check path: it extracts //! the (relation, carrier) of an item's `dec wf` measure ([`dec_wf_relation_and_carrier`]) //! and discharges-or-serves the accessibility proof through the cache -//! ([`discharge_accessibility`]). A re-check on an unchanged (relation, carrier) HITS the +//! ([`discharge_accessibility`]). A re-check on an unchanged (relation, carrier) hits the //! cache (AC-13) — exactly the cross-invocation hit the per-item proof cache uses (`forge //! check` writes, a re-check reads), observable through the cache layer. //! @@ -28,7 +28,7 @@ use crate::cache::{self, AccessibilityProof}; /// Extract the `(relation, carrier)` pair of an item's `dec wf ` measure, or `None` /// if the item carries no `dec wf` measure (REQ-9 / Q7). The carrier is the recursing -/// item's FIRST parameter type (the measure ranges over the structurally-decreasing +/// item's first parameter type (the measure ranges over the structurally-decreasing /// argument), rendered span-free so the same carrier caches identically regardless of /// source position (reuses [`crate::lemma_library::render_type`]). An item with no params /// has the unit carrier `()`. @@ -76,7 +76,7 @@ fn wf_relation(clause: &Clause) -> Option { /// Discharge OR serve the accessibility proof for a `(relation, carrier)` through the cache /// (REQ-9 / AC-13). On a cache HIT the stored proof is returned (flagged `cached: true`) — -/// a re-check on an unchanged (relation, carrier) does NOT re-derive (the observable +/// a re-check on an unchanged (relation, carrier) does not re-derive (the observable /// cache-layer hit). On a MISS, `derive()` computes the well-founded verdict, which is /// stored (best-effort — a write failure does not fail the check, R-CODE-2) and returned. /// @@ -104,7 +104,7 @@ pub fn discharge_accessibility( /// wf` measure (REQ-9 / AC-13), keyed by (relation, carrier). `derive(name)` supplies the /// accessibility verdict for the item named `name` (on the check path: whether its cert /// admitted the recursion). A side-effecting write-through pass: the cache is populated so -/// a SUBSEQUENT `forge check` re-check hits it (the cross-invocation hit the per-item proof +/// a subsequent `forge check` re-check hits it (the cross-invocation hit the per-item proof /// cache uses). Items with no `dec wf` measure are skipped — a no-op on the v1 corpus (no /// v1 item uses `dec wf`). pub fn cache_dec_wf_accessibility( @@ -161,15 +161,15 @@ mod tests { assert_eq!(dec_wf_relation_and_carrier(plain), None); } - // A `dec lex(...)` / plain `dec ` measure is NOT a `dec wf` — no accessibility key. + // A `dec lex(...)` / plain `dec ` measure is not a `dec wf` — no accessibility key. #[test] fn non_wf_measures_are_not_accessibility_keyed() { let prog = parse_ok("spec fn rank(n: u32) -> u32 dec n { n }"); assert_eq!(dec_wf_relation_and_carrier(&prog.items[0]), None); } - // REQ-9 / AC-13: the FIRST discharge derives + stores; a re-check on the same - // (relation, carrier) HITS the cache (no re-derivation), observable via the cache layer. + // REQ-9 / AC-13: the first discharge derives + stores; a re-check on the same + // (relation, carrier) hits the cache (no re-derivation), observable via the cache layer. #[test] fn recheck_hits_without_rederiving() { let dir = unique_dir("discharge"); @@ -183,7 +183,7 @@ mod tests { assert!(!first.cached, "the first discharge is a fresh derivation"); assert!(first.well_founded); assert_eq!(derivations.get(), 1, "derived once on the miss"); - // The re-check HITS — the derive closure must NOT run again. + // The re-check hits — the derive closure must not run again. let second = discharge_accessibility(&dir, "lt_rel", "Prim(U32)", || { derivations.set(derivations.get() + 1); panic!("a cache hit must not re-derive"); diff --git a/forge/src/audit.rs b/forge/src/audit.rs index 032c93bac..7eececf34 100644 --- a/forge/src/audit.rs +++ b/forge/src/audit.rs @@ -87,7 +87,7 @@ pub struct AuditManifest { /// nothing — it changes no exit code and alters no verdict (REQ-10). #[serde(default)] pub lean_fragment: LeanFragment, - /// The `@bv`-tagged clauses' SHADOW FLAGS (`.design/stage3-bv-reconstruction.md` + /// The `@bv`-tagged clauses' shadow flags (`.design/stage3-bv-reconstruction.md` /// REQ-3 / AC-4 — Lock 1): one row per machine-semantics clause aggregated across the /// cert collection, so the audit lists the project's semantic forks the same way the /// `tcb` lists `#[slag]` blocks. A pure projection of each obligation's `bv_shadow`. @@ -98,7 +98,7 @@ pub struct AuditManifest { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub bv_shadows: Vec, /// The "semantic forks and definition towers" section - /// (`.design/stage3-bv-reconstruction.md` REQ-6 / AC-7): the AGGREGATE legibility + /// (`.design/stage3-bv-reconstruction.md` REQ-6 / AC-7): the aggregate legibility /// surface over the per-clause `bv_shadows` above + the burned-lemma towers — bv-shadow /// density per module, every burned lemma's definition-tower depth, and the post-ship /// **F-F density tripwire**. A pure projection ([`crate::forks::SemanticForks::build`]); @@ -108,9 +108,9 @@ pub struct AuditManifest { /// `bv_shadows`. #[serde(default, skip_serializing_if = "Option::is_none")] pub semantic_forks: Option, - /// The REQ-8 / AC-9 RESIDUAL-TRUST STATEMENT: the kernel-checked-vs-solver-trusted split + /// The REQ-8 / AC-9 RESIDUAL-TRUST statement: the kernel-checked-vs-solver-trusted split /// after reconstruction's default-on per-clause trust migration, with the - /// still-solver-trusted clauses + fragments named honestly. A pure projection + /// still-solver-trusted clauses + fragments named. A pure projection /// ([`ResidualTrust::build`]) over the certs' per-clause trust bases; `None` (and /// omitted) for a project the bit-vector route did not run on, so the v1 / nlsat-only /// corpus serializes BYTE-IDENTICALLY (the additive `semantic_forks` discipline; @@ -156,15 +156,14 @@ impl BvShadowRow { } } -/// The REQ-8 / AC-9 RESIDUAL-TRUST STATEMENT (`.design/stage3-bv-reconstruction.md` +/// The REQ-8 / AC-9 RESIDUAL-TRUST statement (`.design/stage3-bv-reconstruction.md` /// REQ-8). After reconstruction's default-on per-clause trust migration, a project's -/// clauses split into two trust bases at the SAME rung: the ones whose `trust:` migrated to -/// the KERNEL-CHECKED form (the renderable QF_LIA + arithmetic/comparison QF_BV fragment, -/// plus the nlsat-relax kernel-grounded clauses) and the ones that STAYED solver-trusted -/// (the bitwise/shift/rotate QF_BV subset the exporter refuses, a Verus L3 base, and the -/// EPR-stratified rel/array residual the reconstruction fragment does not cover). This -/// section is that split, named honestly — "the audit names exactly what stayed -/// solver-trusted" (F-J is free: reconstruction was never load-bearing for any gate, so an +/// clauses split into two trust bases at the same rung: the ones whose `trust:` migrated to +/// the kernel-checked form (the renderable QF_LIA/QF_BV fragment plus nlsat-relax +/// kernel-grounded clauses) and the ones that stayed solver-trusted (expressions outside +/// those exporters, a Verus L3 base, and the EPR-stratified rel/array residual). This +/// section is that split, named — "the audit names exactly what stayed +/// solver-trusted" (F-J is free: reconstruction was never required for any gate, so an /// unsupported fragment regresses nothing by staying labeled). /// /// A pure projection ([`ResidualTrust::build`]) of each per-clause obligation's `trust` @@ -174,29 +173,25 @@ impl BvShadowRow { /// discipline; `manifest_version` stays `"v1"`). The section gates nothing — informational. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ResidualTrust { - /// The number of per-clause obligations now KERNEL-CHECKED / kernel-grounded (REQ-8): + /// The number of per-clause obligations now kernel-checked or kernel-grounded (REQ-8): /// the reconstruction-migrated bv clauses + the nlsat-relax clauses. pub kernel_checked_clauses: usize, - /// The number of per-clause obligations that STAYED solver-trusted (the bitwise/shift/ - /// rotate QF_BV subset + any non-migrated solver base). + /// The number of per-clause obligations that stayed solver-trusted. pub solver_trusted_clauses: usize, - /// The still-solver-trusted clauses, NAMED (item + per-clause obligation name + engine) - /// — the F-J inventory a reviewer audits, the same "grep finds exactly these" discipline + /// The still-solver-trusted clauses, named (item + per-clause obligation name + engine) + /// — the F-J inventory a reviewer audits, the same "grep finds these" discipline /// the `bv_shadows` / `tcb` sections follow. Source order (deterministic, REQ-6). #[serde(default, skip_serializing_if = "Vec::is_empty")] pub solver_trusted: Vec, - /// The named FRAGMENTS reconstruction does not support program-wide (the standing F-J - /// note): the bitwise/shift/rotate QF_BV subset (the bit-blasting wall) and the - /// EPR-stratified rel/array atoms (left z3-model-relative by `strat_lowering_faithful`, - /// the G2 residual — outside the QF_LIA/QF_BV reconstruction fragment, named not - /// migrated). Deterministic, R-CODE-5. + /// The named fragments reconstruction does not support program-wide. Literal QF_BV + /// terms are covered; EPR-stratified rel/array atoms remain model-relative. pub unsupported_fragments: Vec, /// The human one-line residual-trust statement (the auditor's headline). pub statement: String, } /// One still-solver-trusted clause in the residual-trust statement (REQ-8 / AC-9) — a pure -/// projection of an obligation that carries a per-clause `trust` base with NO kernel-checked +/// projection of an obligation that carries a per-clause `trust` base with no kernel-checked /// marker. Read verbatim from the cert (REQ-4). #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct ResidualClause { @@ -213,9 +208,8 @@ impl ResidualTrust { /// Aggregate the REQ-8 kernel-checked-vs-solver split across a settled cert collection /// (AC-9). A pure projection: every per-clause obligation that carries a non-empty /// `trust` base is classified by [`crate::engine::trust_is_kernel_checked`]; the - /// solver-trusted ones are named. The standing F-J fragments (bitwise/shift/rotate + - /// EPR rel/array) are named unconditionally for a bv project, because the - /// reconstruction fragment structurally excludes them program-wide. Returns `None` for a + /// solver-trusted ones are named. The standing EPR rel/array residual is named + /// unconditionally for a bv project. Returns `None` for a /// project the bit-vector route did not run on, so the v1 / nlsat goldens stay /// byte-identical. fn build(certs: &[Certificate], bv_present: bool) -> Option { @@ -242,23 +236,15 @@ impl ResidualTrust { } let solver_trusted_clauses = solver_trusted.len(); let unsupported_fragments = vec![ - "bitwise/shift/rotate QF_BV (`^`/`&`/`|`/`<<`/`>>`, rotate): outside the exporter's \ - renderable fragment — no clean Int encoding, the literal-BitVec path hits the \ - upstream bit-blasting `sorry` (z3-demotion.md), so these stay solver-trusted (F-J)" - .to_string(), "EPR-stratified rel/array atoms: left z3-model-relative by `strat_lowering_faithful` \ - (the G2 residual) — outside the QF_LIA/QF_BV reconstruction fragment, named not \ - migrated (F-J — reconstruction was never load-bearing for any gate)" + (the G2 residual) — outside the QF_LIA/QF_BV reconstruction fragment (F-J)" .to_string(), ]; let statement = format!( "Residual trust (REQ-8, default-on): {kernel_checked_clauses} clause(s) \ kernel-checked, {solver_trusted_clauses} clause(s) still solver-trusted. \ - Reconstruction is default-on for the QF_LIA + arithmetic/comparison QF_BV \ - fragment (trust migrated from solver(Z3) to the Lean kernel + the kernel-checked \ - BvModel faithfulness, same rung); the bitwise/shift/rotate QF_BV subset and the \ - EPR-stratified rel/array atoms stay solver-trusted, named above (F-J — no gate \ - regresses, reconstruction was never load-bearing)." + Reconstruction is default-on for QF_LIA and the literal QF_BV term fragment; \ + EPR-stratified rel/array atoms stay solver-trusted and are named above (F-J)." ); Some(ResidualTrust { kernel_checked_clauses, @@ -596,9 +582,9 @@ pub struct LeanFragmentRow { /// would export this item's contract obligation. pub exportable: bool, /// The coarse attempt class (REQ-7): - /// - `"auto"` — exportable AND [`ExportTier::is_auto`](crate::lean_export::ExportTier::is_auto) (tiers (a)/(b)): - /// `--engine lean` would export AND lake-invoke the auto battery; - /// - `"interactive"` — exportable AND `RecursiveInteractive` (tier (c)): + /// - `"auto"` — exportable and [`ExportTier::is_auto`](crate::lean_export::ExportTier::is_auto) (tiers (a)/(b)): + /// `--engine lean` would export and lake-invoke the auto battery; + /// - `"interactive"` — exportable and `RecursiveInteractive` (tier (c)): /// `--engine lean` exports but does not invoke lake (returns `Unknown`); /// - `"none"` — refused: `--engine lean` skips (`Verdict::Unknown`). pub tier: String, @@ -617,9 +603,9 @@ pub struct LeanFragmentRow { /// The coarse `tier` string for a refused row (REQ-7) — `--engine lean` skips an /// item it cannot export. const TIER_NONE: &str = "none"; -/// The coarse `tier` string for an exportable AUTO-tier row (REQ-7) — tiers (a)/(b). +/// The coarse `tier` string for an exportable automatic-tier row (REQ-7), tiers (a) and (b). const TIER_AUTO: &str = "auto"; -/// The coarse `tier` string for an exportable INTERACTIVE-tier row (REQ-7) — tier (c). +/// The coarse `tier` string for an exportable interactive-tier row (REQ-7) — tier (c). const TIER_INTERACTIVE: &str = "interactive"; impl LeanFragmentRow { @@ -1077,8 +1063,8 @@ mod tests { } // REQ-8 / AC-9: the residual-trust statement aggregates the kernel-checked-vs-solver - // split across a bv project's per-clause obligations and names the still-solver-trusted - // clauses + fragments. The mix64 split: one kernel-checked (arith) + one solver (xor). + // split across a bv project's per-clause obligations and names any solver-trusted + // clause. This fixture supplies one of each to exercise the aggregation. #[test] fn req8_residual_trust_aggregates_the_kernel_checked_vs_solver_split() { let cert = Certificate::new( @@ -1100,25 +1086,23 @@ mod tests { .expect("a bv project carries the REQ-8 residual-trust statement"); assert_eq!( rt.kernel_checked_clauses, 1, - "the arith clause is kernel-checked" + "the migrated clause is kernel-checked" ); assert_eq!( rt.solver_trusted_clauses, 1, - "the xor clause stays solver-trusted" + "the explicit solver-profile fixture stays solver-trusted" ); assert_eq!(rt.solver_trusted.len(), 1); assert_eq!(rt.solver_trusted[0].item, "mix64"); assert_eq!(rt.solver_trusted[0].clause, "mix64::ens#1"); assert_eq!(rt.solver_trusted[0].engine.as_deref(), Some("bitvector")); - // The standing F-J fragments are named: the bitwise/shift/rotate subset + the EPR - // rel/array residual. - assert_eq!(rt.unsupported_fragments.len(), 2); - assert!(rt.unsupported_fragments[0].contains("bitwise/shift/rotate")); - assert!(rt.unsupported_fragments[1].contains("rel/array")); + // Literal QF_BV is covered; the standing EPR rel/array residual remains. + assert_eq!(rt.unsupported_fragments.len(), 1); + assert!(rt.unsupported_fragments[0].contains("rel/array")); assert!(rt.statement.contains("kernel-checked")); } - // REQ-8 / AC-9: a non-bv project (the v1 / nlsat corpus) carries NO residual-trust + // REQ-8 / AC-9: a non-bv project (the v1 / nlsat corpus) carries no residual-trust // statement, so its audit manifest serializes byte-identically (the additive discipline). #[test] fn req8_non_bv_project_has_no_residual_trust_statement() { diff --git a/forge/src/battery.rs b/forge/src/battery.rs index 1eec92fd6..d6e98ecdf 100644 --- a/forge/src/battery.rs +++ b/forge/src/battery.rs @@ -1,10 +1,10 @@ -//! `forge/src/battery.rs` — the FROZEN forge-tier proof battery (REQ-5 / AC-9; +//! `forge/src/battery.rs` — the frozen forge-tier proof battery (REQ-5 / AC-9; //! `.design/stage1-forge-tier.md`, increment 2c). //! -//! This is the LOGIC the foundation (#20) + the 2a surface (#29) set up for. 2a parsed -//! `proof { … }` / `lemma … proof { … }` blocks VERBATIM (their tactic content captured +//! This is the logic the foundation (#20) + the 2a surface (#29) set up for. 2a parsed +//! `proof { … }` / `lemma … proof { … }` blocks verbatim (their tactic content captured //! as a raw [`thermite_syntax::ast::ProofBlock::text`], tactic parsing explicitly -//! deferred to THIS increment). Here we CONSUME that text: +//! deferred to this increment). Here we CONSUME that text: //! //! 1. The frozen [`REGISTRY`](static@FROZEN_TACTICS) — a single static, auditable, //! byte-deterministic source of truth modeled on the combinator registry @@ -24,9 +24,9 @@ //! residual goal(s) + the "missing simp bridge" heuristic (RFC-1 §8) — never silently //! `Proved` and never mis-classed as a solver `Timeout`. //! -//! ## Why the battery is frozen to exactly these +//! ## Why the battery is frozen to these //! -//! The tactic allowlist is the REQ-5 list verbatim. The simp set is the EXACT +//! The tactic allowlist is the REQ-5 list verbatim. The simp set is the exact //! `simp only [ … ]` list the generated auto battery emits //! (`lean_export.rs::auto_tactic_battery`) — the single auditable record of what the //! frozen battery knows how to rewrite. A citation outside it is an unlisted simp lemma @@ -65,7 +65,7 @@ static FROZEN_TACTICS: [BatteryEntry; 9] = [ ]; /// The frozen simp-lemma set (REQ-5) — the closed set a `simp [ … ]` citation inside a -/// frozen-battery proof may name. These are the EXACT `simp only [ … ]` lemmas the +/// frozen-battery proof may name. These are the exact `simp only [ … ]` lemmas the /// generated auto battery emits (`lean_export.rs::auto_tactic_battery`): the single /// auditable source of what the frozen battery knows how to rewrite. Pinned against the /// oracle's `simp_lemmas` array. @@ -131,7 +131,7 @@ pub fn all_simp_lemmas() -> &'static [BatteryEntry] { } /// Is `tactic` an allowlisted frozen-battery tactic head? (REQ-5.) Exact-match by name -/// — `simp_all`/`simp?` are NOT `simp` (the frozen set is closed to the listed heads). +/// — `simp_all`/`simp?` are not `simp` (the frozen set is closed to the listed heads). #[must_use] pub fn is_allowed_tactic(tactic: &str) -> bool { FROZEN_TACTICS.iter().any(|e| e.name == tactic) @@ -164,7 +164,7 @@ pub enum Citation { } /// A frozen-battery refusal (REQ-5 / AC-9): a proof cites a tactic or a simp lemma the -/// frozen battery does not list. A HARD error, named — never a warning (R-BAT-1). The +/// frozen battery does not list. A hard error, named — never a warning (R-BAT-1). The /// proof-tier analogue of `thermite_spec::SpecError` (the contract-cage refusal) and /// `covenant_engine::CovenantError` (the covenant refusal). #[derive(Debug, Clone, PartialEq, Eq)] @@ -288,7 +288,7 @@ fn leading_ident(s: &str) -> Option<&str> { /// Extract the tactic head(s) of one tactic-sequencing fragment (REQ-5). Handles the /// common proof shapes without a full Lean parser, returning 0..2 heads in order: -/// - a `calc` block first line `calc a = b := by tac` → both `calc` AND the inline step's +/// - a `calc` block first line `calc a = b := by tac` → both `calc` and the inline step's /// `by` tactic (`calc` opens a step-structured block); /// - a match/`induction … with` arm `| label args => tac` → the head is after the last /// `=>` (the arm label is not a tactic); @@ -418,7 +418,7 @@ fn simp_lemmas_in(bracketed: &str) -> Vec { /// Scan a verbatim proof block ([`thermite_syntax::ast::ProofBlock::text`]) into its /// tactic + simp-lemma citations, in document order (REQ-5). A deterministic citation -/// scanner — NOT a full Lean parser: it strips comments, splits into tactic-sequencing +/// scanner — not a full Lean parser: it strips comments, splits into tactic-sequencing /// units (newline / `;` / `<;>` / `|`-alternative), extracts each unit's tactic head /// ([`fragment_head`]), and parses every `simp [ … ]` lemma list it finds. Pure /// (R-CODE-5). @@ -483,11 +483,11 @@ fn find_word(s: &str, word: &str) -> Option { } /// The elaboration-time frozen-battery gate (REQ-5 / AC-9): refuse a proof block that -/// cites an unlisted tactic OR an unlisted simp lemma, naming the FIRST offender (scan is +/// cites an unlisted tactic OR an unlisted simp lemma, naming the first offender (scan is /// in document order). A clean proof (every citation in the frozen battery) returns /// `Ok(())`. `item` names the proved/lemma item for the refusal message (R-BAT-1). /// -/// This is the HARD gate — a violation is a refusal, never a warning: `check.rs` runs it +/// This is the hard gate — a violation is a refusal, never a warning: `check.rs` runs it /// before a forge-tier item is admitted, the proof-tier mirror of the /// `thermite_spec::validate` contract cage. #[allow( @@ -503,7 +503,7 @@ pub fn enforce(item: &str, proof_text: &str) -> Result<(), BatteryViolation> { /// The frozen-battery gate, made aware of the per-project lemma namespace (REQ-9 / AC-13, /// increment 3). Identical to [`enforce`] EXCEPT a `simp [ … ]` citation that names a -/// project lemma (`project_lemmas`) is NOT refused as an unlisted simp lemma — it is +/// project lemma (`project_lemmas`) is not refused as an unlisted simp lemma — it is /// DEFERRED to the REQ-9 certified-only citation gate /// ([`crate::lemma_library::enforce_citations`]), which refuses it only if the lemma did /// not certify. The frozen battery still refuses a citation that is neither a frozen spine @@ -527,7 +527,7 @@ pub fn enforce_with_project_lemmas( } Citation::SimpLemma(lemma) => { // A project-lemma citation is resolved by REQ-9 (certified-only), not - // refused here; only a citation outside BOTH the frozen set and the + // refused here; only a citation outside both the frozen set and the // project namespace is the unlisted-simp-lemma refusal. if !is_allowed_simp_lemma(&lemma) && !project_lemmas.contains(&lemma) { return Err(BatteryViolation::UnlistedSimpLemma { @@ -542,7 +542,7 @@ pub fn enforce_with_project_lemmas( } /// Enforce the frozen battery over every proof block a forge-tier item carries (REQ-5 / -/// AC-9), naming the FIRST offender. A `lemma … proof { … }` has one proof block; a +/// AC-9), naming the first offender. A `lemma … proof { … }` has one proof block; a /// `proof for f { ens#k by { … } … }` has one per obligation (checked in source order). A /// `prop fn` / `witness` block carries no proof to elaborate → `Ok(())`. This is the /// per-item elaboration gate `check.rs` runs before a forge-tier item is admitted. @@ -608,7 +608,7 @@ pub fn residual_goals(lake_output: &str) -> Vec { /// The "missing simp bridge" heuristic (REQ-5, the RFC-1 §8 transcript): if a residual /// goal still mentions a function-application head symbol the frozen battery simp set -/// does NOT normalize, name it + the simp bridge lemma (`_cons`) likely missing +/// does not normalize, name it + the simp bridge lemma (`_cons`) likely missing /// from the frozen set. Returns `None` when the residual mentions only frozen-normalized /// symbols / bound variables (no specific bridge to suggest). Deterministic: the first /// such symbol in the first goal. @@ -652,7 +652,7 @@ fn bridge_candidate(goal: &str) -> Option { } /// Is `leaf` a frozen simp lemma leaf or a known builtin (a logical/arith connective name -/// or a base type) the missing-bridge heuristic should NOT suggest a bridge for? +/// or a base type) the missing-bridge heuristic should not suggest a bridge for? fn is_frozen_or_builtin(leaf: &str) -> bool { if is_allowed_simp_lemma(leaf) { return true; @@ -769,7 +769,7 @@ mod tests { #[test] fn match_arm_and_calc_step_heads_are_after_arrow_and_by() { // `induction … with | zero => simp | succ k ih => omega` — the arm labels - // (`zero`/`succ`) are NOT tactics; the heads are the arm RHS tactics. + // (`zero`/`succ`) are not tactics; the heads are the arm RHS tactics. let cites = scan_citations("induction n with | zero => decide | succ k ih => omega"); assert_eq!( cites, @@ -888,7 +888,7 @@ mod tests { #[test] fn non_residual_output_is_not_stuck() { - // A lake failure that is NOT an unsolved-goals residual (e.g. an elaboration + // A lake failure that is not an unsolved-goals residual (e.g. an elaboration // error) yields no Stuck payload — the caller falls through to the verdict map. assert!(stuck_from_lake_output("error: type mismatch\n expected Nat").is_none()); assert!(stuck_from_lake_output("").is_none()); diff --git a/forge/src/bitvector.rs b/forge/src/bitvector.rs index cb6f831c9..08e8a38da 100644 --- a/forge/src/bitvector.rs +++ b/forge/src/bitvector.rs @@ -4,7 +4,7 @@ //! A `@bvN`-tagged clause is interpreted over fixed-width wraparound (machine) //! semantics: every variable is an `N`-bit bit-vector and every operator is its //! `2`'s-complement / unsigned machine counterpart, so addition, multiplication -//! and the bitwise/shift operators all overflow exactly as the hardware does. The +//! and the bitwise/shift operators all overflow as the hardware does. The //! clause is decided by Verus's `by(bit_vector)` mode — which is, mechanically, a //! QF_BV solver query (Z3's bit-blaster). Following the stage-1 [`crate::engine:: //! NlsatEngine`] precedent (which reaches Z3's nlsat tactic *directly* for QF_NRA @@ -27,20 +27,20 @@ //! - **The dedicated 64-bit multiplier budget profile.** A 64-bit bit-vector //! *multiplication between two non-constant terms* is the known QF_BV cost cliff //! (full 64×64 bit-blasting). [`BvBudgetProfile::for_query`] routes such a query -//! through a deliberately bounded `rlimit`/timeout profile so an over-budget +//! through a bounded `rlimit`/timeout profile so an over-budget //! multiplier query is reported as [`BvOutcome::Timeout`] under the named profile — //! **never** a silent `unknown` and never a silent downgrade (REQ-2 / AC-3). //! - **The verdict plumbing.** The engine implements the four-slot [`crate::engine:: //! Engine`] interface; the rich [`BvOutcome`] (which the three-arm `Verdict` cannot //! represent — a budget `Timeout` is distinct from an undecided `Unknown`) is the -//! route's real entry point, mapped down for the generic trait caller. +//! route's entry point, mapped down for the generic trait caller. //! //! A `@bv` clause certifies at the caged rung [`crate::manifest::Level::L4`]: it is //! decidable QF_BV with complete bit-pattern countermodels — the L4 refutation quality //! (RFC-1 §2/§4), never degraded. Rung and trust base are orthogonal: the rung records //! refutation quality, while the trust base (the QF_BV decision procedure, SOLVER) is //! recorded separately in the attribution. Kernel-grounding the bit-vector discharge -//! (proof reconstruction) is REQ-7/REQ-8, which shrinks that trust base at the SAME +//! (proof reconstruction) is REQ-7/REQ-8, which shrinks that trust base at the same //! rung; this module is the lowering only. use std::collections::BTreeMap; @@ -50,7 +50,7 @@ use thermite_syntax::{BinOp, BvWidth, Expr, UnaryOp}; /// The outcome of a `@bv` clause discharge over fixed-width QF_BV semantics /// (`.design/stage3-bv-reconstruction.md` REQ-2). Richer than the three-arm /// [`crate::engine::Verdict`]: a budget [`BvOutcome::Timeout`] (the 64-bit multiplier -/// cost cliff) is held DISTINCT from an honest [`BvOutcome::Unknown`] skip (Z3 absent +/// cost cliff) is held distinct from an [`BvOutcome::Unknown`] skip (Z3 absent /// / the clause outside the renderable fragment), so the route never launders the /// multiplier cliff into a silent `unknown` (AC-3). #[derive(Debug, Clone, PartialEq, Eq)] @@ -69,15 +69,15 @@ pub enum BvOutcome { }, /// The query exhausted its dedicated budget (the 64-bit multiplier cost cliff, /// AC-3). Carries the named budget profile and the Z3 detail; reported as - /// `Timeout`, NEVER `unknown` and never a silent downgrade. + /// `Timeout`, never `unknown` and never a silent downgrade. Timeout { /// The budget profile that bounded the query (e.g. `bv64-multiplier`). profile: String, /// The Z3 rlimit/timeout detail (the captured signal head). detail: String, }, - /// An honest skip: Z3 is absent, the clause is outside the renderable QF_BV - /// fragment, or the query did not render. NEVER a false verdict — and NEVER the + /// A skip: Z3 is absent, the clause is outside the renderable QF_BV + /// fragment, or the query did not render. It is never a false verdict or the /// image of a budget exhaustion (that is [`BvOutcome::Timeout`]). Unknown(String), } @@ -118,7 +118,7 @@ impl BvBitPattern { /// A QF_BV budget profile (`.design/stage3-bv-reconstruction.md` REQ-2 — the /// dedicated 64-bit multiplier profile). The `rlimit` bounds Z3's bit-blasting work /// and `timeout_secs` is the wall-clock cap; an over-budget query returns `unknown` -/// from Z3 WITH a budget set, which the engine reports as [`BvOutcome::Timeout`] +/// from Z3 with a budget set, which the engine reports as [`BvOutcome::Timeout`] /// (never a bare `unknown`). #[derive(Debug, Clone, PartialEq, Eq)] pub struct BvBudgetProfile { @@ -147,12 +147,12 @@ impl BvBudgetProfile { /// the QF_BV cost cliff, so a 64-bit multiplication between two non-constant /// terms is bounded by a tight `rlimit` and a short wall cap. An over-budget /// multiplier query then returns `unknown` (rlimit/timeout) and is reported as - /// [`BvOutcome::Timeout`] under THIS profile — the loud, non-silent failure REQ-2 + /// [`BvOutcome::Timeout`] under this profile — the loud, non-silent failure REQ-2 /// requires. #[must_use] pub fn multiplier64_profile() -> Self { BvBudgetProfile { - // A deliberately bounded resource budget. 64×64 multiplication + // A bounded resource budget. 64×64 multiplication // bit-blasts into a quadratic adder network whose validity queries // routinely exceed a modest rlimit; the bound makes the cliff a // deterministic `Timeout` rather than an unbounded hang. @@ -164,8 +164,8 @@ impl BvBudgetProfile { /// Select the budget profile for a clause query (`.design/stage3-bv-reconstruction.md` /// REQ-2). The dedicated 64-bit multiplier profile applies iff the tag width is 64 - /// AND the clause contains a multiplication whose two operands are both - /// non-constant (a genuine 64-bit multiplier, the cost cliff); every other query + /// and the clause contains a multiplication whose two operands are both + /// non-constant (a 64-bit multiplier, the cost cliff); every other query /// — including a multiply by a literal, which bit-blasts as a cheap shift/add — /// takes the default profile. #[must_use] @@ -178,10 +178,10 @@ impl BvBudgetProfile { } } -/// Does `e` contain a multiplication whose BOTH operands are non-constant +/// Does `e` contain a multiplication where both operands are non-constant /// (`.design/stage3-bv-reconstruction.md` REQ-2 — the 64-bit multiplier cost-cliff /// detector)? A `var * var` (or `var * (expr-with-a-var)`) is the expensive 64×64 -/// bit-blast; a `var * 8` is a cheap shift/add and does NOT trip the dedicated +/// bit-blast; a `var * 8` is a cheap shift/add and does not trip the dedicated /// profile. #[must_use] pub fn contains_variable_multiply(e: &Expr) -> bool { @@ -223,7 +223,7 @@ fn is_constant(e: &Expr) -> bool { /// `<<`→`bvshl`, `>>`→`bvlshr` (unsigned — the scalar types are all unsigned), the /// bitwise ops to `bvand`/`bvor`/`bvxor`, and prefix `!` (on a term) to `bvnot`. An /// integer literal renders as the width-`N` bit-vector constant. `Err` names the -/// out-of-fragment construct (an honest skip reason, never a silent mis-render). +/// out-of-fragment construct (a skip reason, never a silent mis-render). pub fn render_bv_term(e: &Expr, width: u32) -> Result { match e { Expr::IntLit { value, .. } => Ok(format!("(_ bv{} {width})", value % (modulus(width)))), @@ -263,11 +263,11 @@ pub fn render_bv_term(e: &Expr, width: u32) -> Result { } } -/// Render a PROPOSITION to an SMT-LIB2 `Bool` over fixed-width bit-vectors +/// Render a proposition to an SMT-LIB2 `Bool` over fixed-width bit-vectors /// (`.design/stage3-bv-reconstruction.md` REQ-2). Comparisons over the unsigned /// scalar types map to the unsigned bit-vector relations (`<`→`bvult`, `<=`→`bvule`, /// `>`→`bvugt`, `>=`→`bvuge`); `==`→`=`, `!=`→`(not (= …))`; the connectives to -/// `and`/`or`/`not`. `Err` names the out-of-fragment construct (an honest skip). +/// `and`/`or`/`not`. `Err` names the out-of-fragment construct (a skip). pub fn render_bv_prop(e: &Expr, width: u32) -> Result { match e { Expr::BoolLit(b) => Ok(if *b { "true" } else { "false" }.to_string()), @@ -363,14 +363,14 @@ impl BitVectorEngine { /// Discharge one `@bvN` clause over fixed-width QF_BV semantics /// (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-2 / AC-3). The query asserts - /// the precondition (if any) and the NEGATION of the clause over width-`N` + /// the precondition (if any) and the negation of the clause over width-`N` /// bit-vectors, then asks Z3: /// /// - `unsat` → [`BvOutcome::Proved`] (machine-valid at width `N`); /// - `sat` → [`BvOutcome::Counterexample`] carrying the witnessing bit pattern; - /// - `unknown` WITH a budget set → [`BvOutcome::Timeout`] under the named profile + /// - `unknown` with a budget set → [`BvOutcome::Timeout`] under the named profile /// (the 64-bit multiplier cliff — never a bare `unknown`); - /// - Z3 absent / unrenderable clause → [`BvOutcome::Unknown`] (an honest skip). + /// - Z3 absent / unrenderable clause → [`BvOutcome::Unknown`] (a skip). /// /// `vars` are the variables in scope (parameters, plus `result` for a function /// whose clause has already had `result` grounded by the body); `req` is the @@ -384,7 +384,7 @@ impl BitVectorEngine { width: BvWidth, ) -> BvOutcome { let n = width.bits(); - // Render the clause first — an unrenderable clause is an honest skip, never a + // Render the clause first — an unrenderable clause is a skip, never a // false verdict (and never a spurious counterexample from a dropped guard). let clause_smt = match render_bv_prop(clause, n) { Ok(s) => s, @@ -425,9 +425,9 @@ impl BitVectorEngine { bits: parse_bv_model(&model, vars, n), }, // `unknown` under a bounded profile is the budget cliff (AC-3): a - // resource-limited multiplier query. Reported as `Timeout`, NEVER a + // resource-limited multiplier query. Reported as `Timeout`, never a // bare `unknown`. The default profile is unbounded, so an `unknown` - // there is a genuine (rare for decidable QF_BV) solver event we still + // there is a (rare for decidable QF_BV) solver event we still // surface as a wall-timeout rather than a false verdict. "unknown" => BvOutcome::Timeout { profile: profile.name.clone(), @@ -446,8 +446,8 @@ impl BitVectorEngine { } } - /// Is the precondition `req` SATISFIABLE at width `N`? Anti-Goodhart vacuity check - /// (RFC-1 §10): a `@bv` clause is discharged as `req ⇒ clause`, so an UNSATISFIABLE + /// Is the precondition `req` satisfiable at width `N`? Anti-Goodhart vacuity check + /// (RFC-1 §10): a `@bv` clause is discharged as `req ⇒ clause`, so an unsatisfiable /// `req` proves *every* clause vacuously — the gaming vector the v1 cage rejects as /// `VacuousPrecondition`. The bv route's mutation gate only catches this for /// result-referencing clauses (every mutant survives); a param-only clause or a @@ -456,10 +456,10 @@ impl BitVectorEngine { /// rendering the discharge uses (`req` at width `N`), so the verdict is consistent /// with the discharge and never a width artifact. /// - /// `Some(true)` = satisfiable (non-vacuous); `Some(false)` = UNSAT (vacuous — reject); + /// `Some(true)` = satisfiable (non-vacuous); `Some(false)` = unsatisfiable (vacuous — reject); /// `None` = cannot decide (`req` absent/unrenderable, Z3 absent, or `unknown`), so the - /// caller does NOT flag vacuity and falls through to the normal discharge — - /// conservative, never a FALSE vacuity rejection. + /// caller does not flag vacuity and falls through to the normal discharge — + /// conservative, never a false vacuity rejection. #[must_use] pub fn req_satisfiable(&self, vars: &[String], req: &Expr, width: BvWidth) -> Option { let n = width.bits(); @@ -479,15 +479,15 @@ impl BitVectorEngine { } } - /// Discharge the `@bvN(nowrap)` no-overflow SIDE OBLIGATION over fixed-width QF_BV + /// Discharge the `@bvN(nowrap)` no-overflow side obligation over fixed-width QF_BV /// semantics (`.design/stage3-bv-reconstruction.md` REQ-5 / AC-6 — lock 3). A /// `nowrap` tag declares that, although the clause is interpreted at machine width, - /// wrap is NOT the author's intent: every wrap-prone arithmetic operation in the + /// wrap is not the author's intent: every wrap-prone arithmetic operation in the /// clause body (`+`, `-`, `*`) must stay within `N` bits for every input satisfying /// the precondition. The obligation is itself a QF_BV query — "does some operation /// overflow at width `N`?" — asked the OPPOSITE way round to the main clause: it - /// asserts the precondition AND the disjunction of the per-operation overflow - /// conditions (NOT negated, since we are hunting for an overflowing assignment). + /// asserts the precondition and the disjunction of the per-operation overflow + /// conditions (not negated, since we are hunting for an overflowing assignment). /// /// - `unsat` → no input overflows → [`BvOutcome::Proved`] (the obligation holds); /// - `sat` → a concrete overflowing input → [`BvOutcome::Counterexample`] carrying @@ -495,10 +495,10 @@ impl BitVectorEngine { /// - `unknown` under a bounded profile → [`BvOutcome::Timeout`] (the multiplier /// cliff — the overflow query zero-extends a 64-bit multiply to 128 bits, so it /// inherits the same dedicated budget; never a bare `unknown`); - /// - Z3 absent / an out-of-fragment operand → [`BvOutcome::Unknown`] (an honest skip). + /// - Z3 absent / an out-of-fragment operand → [`BvOutcome::Unknown`] (a skip). /// - /// A clause whose body carries NO wrap-prone operation (a pure comparison such as - /// `result == a`) has nothing that could overflow, so the obligation holds VACUOUSLY + /// A clause whose body carries no wrap-prone operation (a pure comparison such as + /// `result == a`) has nothing that could overflow, so the obligation holds vacuously /// ([`BvOutcome::Proved`]) without a solver round-trip. `vars` / `req` mirror /// [`BitVectorEngine::discharge_bv`] (the `result`-grounded clause closed over the /// parameters). @@ -512,7 +512,7 @@ impl BitVectorEngine { ) -> BvOutcome { let n = width.bits(); // Collect the per-operation overflow conditions. An out-of-fragment operand is an - // honest skip (never a silent pass) — the obligation is not quietly dropped. + // skip (never a silent pass) — the obligation is not quietly dropped. let mut conds = Vec::new(); if let Err(reason) = collect_overflow_conditions(clause, n, &mut conds) { return BvOutcome::Unknown(format!( @@ -547,7 +547,7 @@ impl BitVectorEngine { }; // The overflow query reuses the clause's budget profile: a 64-bit variable // multiply zero-extends to a 128-bit `bvmul` here, an even costlier bit-blast, so - // it deserves the dedicated multiplier budget exactly as the main query does. + // it deserves the dedicated multiplier budget as the main query does. let profile = BvBudgetProfile::for_query( width, &req.into_iter() @@ -560,7 +560,7 @@ impl BitVectorEngine { Ok((result, model)) => match result.as_str() { // No assignment overflows → the no-overflow obligation holds. "unsat" => BvOutcome::Proved, - // A concrete overflowing input → the obligation FAILS, witnessed by the + // A concrete overflowing input → the obligation fails, witnessed by the // bit pattern (AC-6). "sat" => BvOutcome::Counterexample { bits: parse_bv_model(&model, vars, n), @@ -585,7 +585,7 @@ impl BitVectorEngine { } /// Run Z3 over an SMT-LIB2 `query` (fed on stdin), returning `(result, model)`. - /// `Err` on Z3 absent / spawn failure / no result token (an honest skip reason, + /// `Err` on Z3 absent / spawn failure / no result token (a skip reason, /// never a silent success — R-CODE-4). Mirrors [`crate::engine::NlsatEngine`]'s /// `run_z3`, parameterized by the budget profile's wall timeout. fn run_z3(query: &str, timeout_secs: u64) -> Result<(String, String), String> { @@ -637,7 +637,7 @@ impl BitVectorEngine { /// Build the SMT-LIB2 `QF_BV` query whose satisfiability decides a `@bvN` clause /// (`.design/stage3-bv-reconstruction.md` REQ-2). Declares each variable as an /// `N`-bit bit-vector, sets the budget profile's `rlimit` (when bounded — the 64-bit -/// multiplier cliff), asserts the precondition and the NEGATION of the clause, and +/// multiplier cliff), asserts the precondition and the negation of the clause, and /// asks for a model (the bit-pattern witness on `sat`). #[must_use] pub fn build_bv_query( @@ -674,13 +674,13 @@ pub fn build_bv_query( /// Collect the per-operation no-overflow side conditions of a `@bvN(nowrap)` clause /// body (`.design/stage3-bv-reconstruction.md` REQ-5 / AC-6). For every wrap-prone /// arithmetic operation in `e` — addition, subtraction, multiplication — push the -/// SMT-LIB2 `Bool` that is TRUE exactly when THAT operation overflows `width` bits, with +/// SMT-LIB2 `Bool` that is true exactly when that operation overflows `width` bits, with /// its operands rendered as the actual width-`N` machine terms fed into it. The walk /// recurses through the whole expression (propositions, connectives, comparisons, and /// the operand sub-terms), so a nested `(a + b) * c` emits a condition for both the /// inner add and the outer multiply. The disjunction of the collected conditions is /// satisfiable iff some input overflows. `Err` names an out-of-fragment operand — an -/// honest skip, so the obligation is never silently dropped. Pure (R-CODE-5). +/// skip, so the obligation is never silently dropped. Pure (R-CODE-5). fn collect_overflow_conditions(e: &Expr, width: u32, out: &mut Vec) -> Result<(), String> { match e { Expr::Binary { op, lhs, rhs } => { @@ -741,7 +741,7 @@ fn overflow_condition( /// Build the SMT-LIB2 `QF_BV` query whose satisfiability decides a `@bvN(nowrap)` /// no-overflow side obligation (`.design/stage3-bv-reconstruction.md` REQ-5 / AC-6). /// Unlike [`build_bv_query`] (which asserts the NEGATED clause to seek a falsifying -/// model), this asserts the precondition and the overflow disjunction DIRECTLY — a `sat` +/// model), this asserts the precondition and the overflow disjunction directly — a `sat` /// model is a concrete OVERFLOWING input (the obligation fails), `unsat` means no input /// overflows (the obligation holds). Sets the budget profile's `rlimit` when bounded /// (the 64-bit multiplier cliff, now widened to a 128-bit `bvmul`). @@ -764,8 +764,8 @@ pub fn build_nowrap_query( if let Some(req) = req_smt { s.push_str(&format!("(assert {req})\n")); } - // The obligation FAILS iff `req ∧ (some operation overflows)` is satisfiable — so the - // overflow disjunction is asserted directly (NOT negated, unlike the main clause). + // The obligation fails iff `req ∧ (some operation overflows)` is satisfiable — so the + // overflow disjunction is asserted directly (not negated, unlike the main clause). s.push_str(&format!("(assert {overflow_smt})\n")); s.push_str("(check-sat)\n"); s.push_str("(get-model)\n"); @@ -774,7 +774,7 @@ pub fn build_nowrap_query( /// Parse Z3's `(get-model)` output into the falsifying bit pattern per variable /// (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-3). Extracts each `(define-fun -/// NAME () (_ BitVec W) VALUE)` and decodes VALUE — a hex `#x…`, a binary `#b…`, or a +/// NAME () (_ BitVec W) value)` and decodes value — a hex `#x…`, a binary `#b…`, or a /// `(_ bvK W)` literal — into a [`BvBitPattern`]. A variable Z3 omits (unconstrained) /// is recorded as all-zeros (a concrete representative of the free choice). #[must_use] @@ -793,7 +793,7 @@ pub fn parse_bv_model(model: &str, vars: &[String], width: u32) -> Vec BTreeMap { out } -/// Decode the VALUE of a `(define-fun NAME () (_ BitVec W) VALUE)` body into a -/// numeric bit pattern. The body is `NAME () (_ BitVec W) VALUE`; the value is the +/// Decode the value of a `(define-fun NAME () (_ BitVec W) value)` body into a +/// numeric bit pattern. The body is `NAME () (_ BitVec W) value`; the value is the /// trailing token(s) after the `(_ BitVec W)` sort. Handles `#x…`, `#b…`, and the /// `(_ bvK W)` literal form. fn decode_bv_value(inner: &str) -> Option { @@ -982,7 +982,7 @@ mod tests { var("a"), var("b") ))); - // var * 8 → cheap shift/add, does NOT trip. + // var * 8 → cheap shift/add, does not trip. assert!(!contains_variable_multiply(&bin( BinOp::Mul, var("a"), @@ -1029,7 +1029,7 @@ mod tests { assert!(q.contains("(declare-const a (_ BitVec 64))")); assert!(q.contains("(assert (not (= (bvmul a b) (bvmul b a))))")); assert!(q.contains("(check-sat)")); - // The default (unbounded) profile sets NO rlimit. + // The default (unbounded) profile sets no rlimit. let q = build_bv_query( &["a".to_string()], None, @@ -1079,7 +1079,7 @@ mod tests { } // ── Live QF_BV tests (REQ-2 / AC-2 / AC-3). z3 ships beside the verus - // distribution; a CI shard without z3 SKIPS rather than fails (the nlsat-route + // distribution; a CI shard without z3 skips rather than fails (the nlsat-route // precedent). These exercise `discharge_bv` directly against the real solver. fn bv_skip() -> bool { @@ -1122,7 +1122,7 @@ mod tests { return; } let engine = BitVectorEngine::new(); - // req x != y ; ens@bv64 (x << 1) != (y << 1) — FALSE (shl-by-1 is not injective). + // req x != y ; ens@bv64 (x << 1) != (y << 1) — false (shl-by-1 is not injective). let req = bin(BinOp::Ne, var("x"), var("y")); let clause = bin( BinOp::Ne, @@ -1139,7 +1139,7 @@ mod tests { BvOutcome::Counterexample { bits } => { assert_eq!(bits.len(), 2, "a bit pattern per variable"); assert!(bits.iter().all(|b| b.width == 64)); - // The witness genuinely falsifies: x != y but x<<1 == y<<1. + // The witness falsifies: x != y but x<<1 == y<<1. assert_ne!(bits[0].value, bits[1].value, "x != y in the witness"); assert_eq!( (bits[0].value << 1) & u64::MAX as u128, @@ -1152,7 +1152,7 @@ mod tests { } /// AC-3 (engine level): an over-budget 64-bit multiplier query is reported under the - /// dedicated budget profile and is NEVER a silent `unknown`. A factoring-style + /// dedicated budget profile and is never a silent `unknown`. A factoring-style /// validity (`a * b != ` with non-trivial bounded factors) forces the full /// 64×64 bit-blast — the cost cliff — so the bounded profile reports `Timeout`. The /// robust invariant asserted here (across z3 versions): the outcome is never @@ -1231,7 +1231,7 @@ mod tests { #[test] fn overflow_conditions_recurse_into_nested_operations() { - // (a + b) * c emits BOTH the inner add overflow and the outer multiply overflow, + // (a + b) * c emits both the inner add overflow and the outer multiply overflow, // with the outer multiply's left operand the wrapped `(bvadd a b)` machine value. let e = bin(BinOp::Mul, bin(BinOp::Add, var("a"), var("b")), var("c")); let conds = overflow_conds(&e, 64); @@ -1291,7 +1291,7 @@ mod tests { q.contains("(assert (bvult a (_ bv100 64)))"), "the precondition is a hypothesis" ); - // The overflow disjunction is asserted DIRECTLY (not wrapped in `(not …)`), so a + // The overflow disjunction is asserted directly (not wrapped in `(not …)`), so a // model is an overflowing input. assert!(q.contains("(assert (bvult (bvadd a b) a))")); assert!( @@ -1330,7 +1330,7 @@ mod tests { } /// AC-6 (engine level): an unconstrained `@bv64(nowrap)` `a + b` CAN overflow — the - /// obligation fails with a concrete overflowing bit pattern (the witness genuinely + /// obligation fails with a concrete overflowing bit pattern (the witness /// carries out of 64 bits). #[test] fn live_unbounded_sum_fails_with_a_concrete_overflowing_input() { @@ -1348,7 +1348,7 @@ mod tests { match out { BvOutcome::Counterexample { bits } => { assert_eq!(bits.len(), 2, "a bit pattern per variable"); - // The witness genuinely overflows: a + b wraps below an operand at width 64. + // The witness overflows: a + b wraps below an operand at width 64. let mask = u64::MAX as u128; let wrapped = (bits[0].value + bits[1].value) & mask; assert!( @@ -1413,7 +1413,7 @@ mod tests { } } - /// AC-6 (engine level): a non-arithmetic `nowrap` body holds VACUOUSLY without a + /// AC-6 (engine level): a non-arithmetic `nowrap` body holds vacuously without a /// solver round-trip (so it passes even with z3 absent — no skip guard needed). #[test] fn nowrap_obligation_is_vacuous_for_a_non_arithmetic_body() { diff --git a/forge/src/body_tv.rs b/forge/src/body_tv.rs index 64f3742ad..04d10c03a 100644 --- a/forge/src/body_tv.rs +++ b/forge/src/body_tv.rs @@ -111,7 +111,7 @@ pub struct BodyResult { } /// The aggregate body-TV report for one file (REQ-5). `divergent` is the headline: -/// any divergent body is a real body-lowering state-transformation finding, which +/// any divergent body is a body-lowering state-transformation finding, which /// drives a non-zero exit (the meaning-mismatch verdict). #[derive(Debug, Clone, Default)] pub struct BodyTvReport { @@ -969,7 +969,7 @@ fn run_obligation(program: &str, label: &str, seed: u64, rlimit: f64) -> Dischar } /// Parse the `N verified, M errors` summary line from verus output (mirrors -/// `exec_tv`'s parser / the teeth-test). `None` if no summary line is present. +/// `exec_tv`'s parser and the negative test). `None` if no summary line is present. fn parse_results(output: &str) -> Option<(u32, u32)> { let line = output .lines() @@ -1046,9 +1046,9 @@ pub fn render_report(report: &BodyTvReport, header: &str) -> String { pub const BODY_TV_DEFAULT_SEED: u64 = DEFAULT_SOLVER_SEED; pub const BODY_TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; -// ---- the forge-level Divergent teeth (REQ-5; blocker #189) ----------------- +// ---- forge-level Divergent regression tests (REQ-5; blocker #189) ---------- // -// The obligation-layer teeth (`thermite-tv/tests/body_teeth.rs` / `loop_teeth.rs`) +// The obligation-layer tests (`thermite-tv/tests/body_teeth.rs` / `loop_teeth.rs`) // prove a wrong `P_production` -> a verus error. They do not exercise the // forge-level step that maps that verus signal to a `BodyVerdict`: `discharge`'s // four-way classification. Over the corpus the faithful lowerer never produces a @@ -1057,7 +1057,7 @@ pub const BODY_TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; // Unverifiable, a counterexample -> Divergent) had no direct test coverage. // This is the divergence #189 pinned: a frame abort fabricated a Divergent. // -// This module is the end-to-end teeth for the forge classification, mirroring +// This module tests the forge classification end to end, mirroring // `exec_tv::divergent_teeth`: it builds a body obligation, discharges it through // the `discharge` fn, and asserts the verdict. It covers the positive control // (faithful -> Faithful), the counterexample Divergent trigger (a wrong-value @@ -1066,7 +1066,7 @@ pub const BODY_TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; // zero-obligation program each classify Unverifiable, not Divergent. // // Test-only: no production-logic change. `discharge` is a private sibling fn, -// reachable here via `super::`. The teeth drive a wrong production / a +// reachable here via `super::`. The tests drive a wrong production or a // frame abort -> a verus signal -> the `discharge` mapping, not a // mocked verdict. Skips with a printed reason when `verus` is absent. #[cfg(test)] @@ -1075,7 +1075,7 @@ mod divergent_teeth { use thermite_syntax::ast::{BinOp, Expr}; /// `true` iff a bare `verus` is spawnable (the same resolution `discharge` uses). - /// Skips with a printed reason otherwise so the teeth never silently pass. + /// Skips with a printed reason when the solver cannot be reached. fn verus_on_path() -> bool { Command::new("verus").arg("--version").output().is_ok() } diff --git a/forge/src/build.rs b/forge/src/build.rs index c8fff3c46..ac9368863 100644 --- a/forge/src/build.rs +++ b/forge/src/build.rs @@ -434,7 +434,7 @@ fn parse_program(path: &Path) -> Result { /// Emit the full compiled L1 source for `path` (incl. any `--entry` runner) /// without compiling it (REQ-1/REQ-5/AC-6). This is `build_file`'s codegen -/// source-of-truth — `build_file` compiles exactly these bytes, and the +/// source-of-truth — `build_file` compiles these bytes, and the /// reproducibility test asserts they are byte-identical across two calls (forge /// owns the emission determinism, independent of any rustc nondeterminism). The /// `--entry` runner is appended deterministically (`synthesize_entry_main`). @@ -758,9 +758,9 @@ fn invoke_rustc( path: scratch.path.display().to_string(), source: e, })?; - // The CANONICAL scratch path (REQ-5/AC-6, byte-reproducibility). On macOS the + // The canonical scratch path (REQ-5/AC-6, byte-reproducibility). On macOS the // temp root `/var/folders/...` is reached through the `/var → /private/var` - // symlink, and rustc records the CANONICAL cwd (`/private/var/...`, the + // symlink, and rustc records the canonical cwd (`/private/var/...`, the // DW_AT_comp_dir) in the artifact. A `--remap-path-prefix` keyed only on the // non-canonical `scratch.path` (`/var/...`) therefore silently MISSES, and the // per-run, PID-bearing absolute path leaks into the rlib — so two same-input @@ -803,7 +803,7 @@ fn invoke_rustc( .arg(&rs_name) .arg("--remap-path-prefix") .arg(format!("{}=.", scratch.path.display())) - // Also remap the CANONICAL scratch path: on macOS rustc records the + // Also remap the canonical scratch path: on macOS rustc records the // `/private/var/...` form, which the non-canonical remap above misses // (REQ-5/AC-6 — the byte-reproducibility fix). On Linux this equals the // line above (a harmless duplicate). diff --git a/forge/src/burn.rs b/forge/src/burn.rs index 2f1f2f4c0..d4313f079 100644 --- a/forge/src/burn.rs +++ b/forge/src/burn.rs @@ -1,11 +1,11 @@ //! `forge/src/burn.rs` — the L3 **burn receipt** (`.design/stage1-forge-tier.md` -//! REQ-7, increment 2e; the RFC-1 §9 L3 certificate shape; Q-BURN resolved). The +//! REQ-7, increment 2e; the RFC-1 §9 L3 certificate shape; Q-burn resolved). The //! thesis's "burn the cheap resource" made auditable: when a forge-tier proof //! closes a goal, the certificate records how much proof text was committed and //! which lemmas it cited, so a reader can weigh the burned proof against the claim //! it discharges (the program plan §6 "tokens per discharged L3 clause" metric). //! -//! ## The two token fields (Q-BURN, resolved) +//! ## The two token fields (Q-burn, resolved) //! //! - [`BurnReceipt::proof_tokens`] — ALWAYS present: the **lexer-token count of the //! committed proof text**, the project lexer ([`thermite_syntax::tokenize`]) run @@ -15,12 +15,12 @@ //! - [`BurnReceipt::authoring_tokens`] — OPTIONAL: the LLM tokens the authoring //! harness spent producing the proof, recorded only when the harness supplies them //! (absent otherwise). The burn-economics dashboard consumes this where present and -//! falls back to `proof_tokens` as a proxy (Q-BURN). +//! falls back to `proof_tokens` as a proxy (Q-burn). //! -//! BOTH fields are **oracle-EXCLUDED** (Q-ORACLE / Q-BURN): re-authoring a proof +//! both fields are **oracle-excluded** (Q-ORACLE / Q-burn): re-authoring a proof //! legitimately changes the committed token count and the authoring spend without -//! changing what was proven, so the burn receipt is NOT part of -//! [`crate::manifest::Certificate::oracle_subset`] — exactly like `solver_time_ms`. +//! changing what was proven, so the burn receipt is not part of +//! [`crate::manifest::Certificate::oracle_subset`] — like `solver_time_ms`. //! Adding it leaves the v1 golden certs byte-identical (a v1 item never burns a //! forge-tier proof, so its `burn` stays `None`). //! @@ -37,10 +37,10 @@ use crate::battery::{self, Citation}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct BurnReceipt { /// The lexer-token count of the committed proof text — the project lexer - /// ([`thermite_syntax::tokenize`]) run over the verbatim proof block (Q-BURN). + /// ([`thermite_syntax::tokenize`]) run over the verbatim proof block (Q-burn). /// Deterministic and re-derivable: a pure function of the committed text. pub proof_tokens: usize, - /// The LLM authoring tokens the harness spent, when it supplies them (Q-BURN). + /// The LLM authoring tokens the harness spent, when it supplies them (Q-burn). /// `None` unless the authoring harness records it; the dashboard falls back to /// `proof_tokens` as a proxy where absent. Oracle-excluded (re-authoring changes /// it without changing the claim). `#[serde(default, skip_serializing_if)]` so a @@ -72,7 +72,7 @@ impl BurnReceipt { } } - /// Attach the optional LLM authoring-token count (Q-BURN), returning the receipt + /// Attach the optional LLM authoring-token count (Q-burn), returning the receipt /// with `authoring_tokens` set. Called by an authoring harness that tracked the /// spend; absent on a receipt minted purely from the committed text. #[allow( @@ -89,7 +89,7 @@ impl BurnReceipt { } } -/// The lexer-token count of the committed proof text (Q-BURN): the number of tokens +/// The lexer-token count of the committed proof text (Q-burn): the number of tokens /// the project lexer ([`thermite_syntax::tokenize`]) emits over the verbatim text, /// EXCLUDING the trailing `Eof` sentinel (the lexer appends one `TokKind::Eof` to /// every stream — it is a parser marker, not committed proof content, so an empty @@ -128,7 +128,7 @@ fn cited_lemmas(proof_text: &str) -> Vec { mod tests { use super::*; - // REQ-7 / Q-BURN: `proof_tokens` is the project lexer's token count over the + // REQ-7 / Q-burn: `proof_tokens` is the project lexer's token count over the // committed proof text — deterministic and re-derivable. A simple `omega` proof // lexes to one token. #[test] @@ -159,7 +159,7 @@ mod tests { ); } - // Q-BURN: `authoring_tokens` is absent unless the harness supplies it; attaching + // Q-burn: `authoring_tokens` is absent unless the harness supplies it; attaching // it is opt-in and does not touch the deterministic fields. #[test] fn authoring_tokens_is_opt_in() { @@ -173,7 +173,7 @@ mod tests { ); } - // Q-BURN / Q-ORACLE: the burn receipt is ORACLE-EXCLUDED — a cert carrying a burn + // Q-burn / Q-ORACLE: the burn receipt is ORACLE-excluded — a cert carrying a burn // receipt and the same cert without it compare oracle-EQUAL, so re-authoring a proof // (which changes the receipt) never perturbs the cert oracle / breaks golden stability. #[test] diff --git a/forge/src/cache.rs b/forge/src/cache.rs index dab0bede5..2aaa4dfb1 100644 --- a/forge/src/cache.rs +++ b/forge/src/cache.rs @@ -92,7 +92,7 @@ const DOMAIN: &[u8] = b"thermite.forge.proof-cache.v1"; /// REQ-2: a hit must equal a fresh verify). /// 5 — blocker #101 (`.design/forge/equivalent-mutants.md` REQ-5): the §7 /// mutation gate now excludes a survivor Verus proves observably equivalent -/// to the real body under `req` from the kill-ratio denominator +/// to the body under `req` from the kill-ratio denominator /// (`check::mutation_score` → `equivalence_proves_equal`). This changes the /// gate verdict for forced-output fns (a `1/3` `WeakContract` `clamp_zero` /// becomes a certifying `1/1` once its two proved-equivalent survivors @@ -103,7 +103,7 @@ const DOMAIN: &[u8] = b"thermite.forge.proof-cache.v1"; /// 6 — blocker #269 (`.design/forge/mutation-scoring.md` REQ-9/REQ-10/REQ-12): /// the §7 early-return family now also synthesizes the F-IDENT identity /// returns (`return ` for every param whose type equals the return) -/// and the F-STRUCT-ZERO named-struct field-zero literal. Both are +/// and the F-STRUCT-zero named-struct field-zero literal. Both are /// verdict-changing widenings of the frozen mutant set (an item's `K/N` /// and even its certify/gate verdict can change, e.g. `move_up` gains a /// surviving `return b` identity mutant), so the check logic is no longer @@ -309,10 +309,10 @@ fn temp_sibling(cache_dir: &Path, key: &str) -> PathBuf { // Stage-1 forge tier (`.design/stage1-forge-tier.md` REQ-9 / Q7 / AC-13): a `dec wf ` // termination measure carries an ACCESSIBILITY obligation — the relation must be // well-founded on the recursion's carrier for the recursion to be admitted. That proof is -// expensive and re-derivable, so — exactly like the per-item proof cache above — it is +// expensive and re-derivable, so — like the per-item proof cache above — it is // content-addressed and cached by the (relation, carrier) pair, invalidated by the same // `CHECK_SCHEMA_VERSION` gate-set key (a gate change ⇒ a new key ⇒ a miss ⇒ a re-check). A -// `dec wf` re-check on an unchanged (relation, carrier) HITS the cache (AC-13), skipping the +// `dec wf` re-check on an unchanged (relation, carrier) hits the cache (AC-13), skipping the // re-derivation — observable through [`load_accessibility`] returning the stored proof. /// Domain-separation tag for the accessibility cache key (REQ-9), distinct from the @@ -400,7 +400,7 @@ fn accessibility_entry_path(cache_dir: &Path, key: &str) -> PathBuf { /// Look up a cached [`AccessibilityProof`] by `key` under `cache_dir` (REQ-9 / AC-13). /// Returns `Some(proof)` (flagged `cached: true`) on a hit, `None` on a miss. A miss /// includes no file, an unreadable file, and a corrupt/unparseable file — a damaged cache -/// degrades to re-derive, never to an error or a stale read (R-CODE-2), exactly like +/// degrades to re-derive, never to an error or a stale read (R-CODE-2), like /// [`load`]. #[must_use] pub fn load_accessibility(cache_dir: &Path, key: &str) -> Option { @@ -602,7 +602,7 @@ mod tests { assert_ne!(a, cache_key("lt", 0, "u32", "0.1.0")); } - // REQ-9 / AC-13: a `dec wf` re-check HITS the cache — a stored accessibility proof is + // REQ-9 / AC-13: a `dec wf` re-check hits the cache — a stored accessibility proof is // served (flagged `cached: true`) on the second look, observable via the cache layer; // the stored form is canonical `cached: false`. #[test] @@ -617,7 +617,7 @@ mod tests { ); let proof = AccessibilityProof::new("lt", "u32", true); store_accessibility(&dir, &key, &proof).expect("store"); - // The re-check HITS — same (relation, carrier), served from the cache. + // The re-check hits — same (relation, carrier), served from the cache. let hit = load_accessibility(&dir, &key).expect("HIT after store"); assert_eq!(hit.relation, "lt"); assert_eq!(hit.carrier, "u32"); @@ -639,7 +639,7 @@ mod tests { // sharing a schema differ, and the same pair across schemas would differ — pinned // structurally by `accessibility_cache_key` feeding `CHECK_SCHEMA_VERSION`. let k = accessibility_cache_key("lt", "u32"); - // A hand-built key WITHOUT the schema field must differ from the real key (proving + // A hand-built key without the schema field must differ from the real key (proving // the schema participates — the invalidation lever). let mut bare = Sha256::new(); bare.update(DOMAIN_ACCESSIBILITY); diff --git a/forge/src/check.rs b/forge/src/check.rs index 0e4675712..4d84e855f 100644 --- a/forge/src/check.rs +++ b/forge/src/check.rs @@ -169,13 +169,13 @@ pub fn check_file(path: impl AsRef) -> Result, ForgeError } /// Run the check pipeline on the Lean engine path (`.design/stage1-forge-tier.md` REQ-7, -/// increment 2e): the Verus base plus the forge-tier LEMMA discharge (the forge tier's +/// increment 2e): the Verus base plus the forge-tier lemma discharge (the forge tier's /// self-contained goals certify here, carrying the burn receipt on `Proven`). The /// convenience entry `forge fill`'s proof-hole re-check uses so a closed forge-tier goal /// surfaces its certifying verdict + burn receipt directly (AC-11), rather than the /// Verus-only default `check_file` which skips a forge-tier lemma. `source_file` is the /// checked path (the interactive-replay artifact location, unused for an in-`.th` lemma -/// proof). Lean-absent → the lemma is an honest non-certified skip (never a false L3). +/// proof). If Lean is absent, the lemma is a non-certified skip (never a false L3). pub fn check_file_lean(path: impl AsRef) -> Result, ForgeError> { let path = path.as_ref(); check_file_with_engine( @@ -239,10 +239,10 @@ pub enum EngineSelection { /// relaxable polynomial contract is discharged by a direct Z3 nlsat (QF_NRA) query /// and certifies at the kernel-grounded [`Level::L4`] (`engine: nlsat` /// attribution); a true-over-ℤ/false-over-ℝ clause yields a `RealWitness` - /// escalation (never a `Counterexample`); a non-relaxable item is an honest skip. + /// escalation (never a `Counterexample`); a non-relaxable item is a skip. Nlsat, /// `--engine forge` (`.design/stage1-forge-tier.md` REQ-10 / AC-14 — the G1 gate - /// route): the PER-CLAUSE hybrid route that drives the whole forge tier end to end + /// route): the per-clause hybrid route that drives the whole forge tier end to end /// on a covenant-routed `fn`. Each `ens` clause is classified by /// [`crate::relax::classify_fn`]: a RELAXABLE polynomial clause routes to the /// [`crate::engine::NlsatEngine`] and certifies at [`Level::L4`]; a NON-relaxable @@ -255,17 +255,17 @@ pub enum EngineSelection { /// its goldens stay byte-identical. Forge, /// `--engine bv` (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-2 / AC-3 — - /// stage-3): the PER-CLAUSE bit-vector route (the RFC's `mix64` shape). Each `ens` - /// clause is dispatched by its `@bvN` tag: a TAGGED clause lowers to fixed-width + /// stage-3): the per-clause bit-vector route (the RFC's `mix64` shape). Each `ens` + /// clause is dispatched by its `@bvN` tag: a tagged clause lowers to fixed-width /// QF_BV and is decided by the [`crate::bitvector::BitVectorEngine`] (a `Proved` /// certifies at the caged rung [`Level::L4`] — decidable with complete bit-pattern /// countermodels (RFC-1 §2/§4), solver-trusted (Z3 QF_BV) until REQ-7/8 kernel- /// grounds it; a falsified clause yields a bit-level /// `Counterexample` with the bit pattern; an over-budget 64-bit multiplier yields a - /// `Timeout` under the dedicated budget profile, never `unknown`). An UNTAGGED + /// `Timeout` under the dedicated budget profile, never `unknown`). An untagged /// clause lowers as before — routed to the [`crate::engine::NlsatEngine`] when it is /// a relaxable polynomial (the unbounded side, certifying at [`Level::L4`]), else an - /// honest skip. One function thus carries wraparound and unbounded clauses side by + /// skip. One function thus carries wraparound and unbounded clauses side by /// side, each labeled with its engine and semantics; the item level is the MIN over /// the clauses. A `@bv`-tagged `lemma` is discharged directly by the bit-vector /// engine with no author proof block. The v1 corpus carries no `@bv` tag, so its @@ -393,11 +393,11 @@ pub fn check_file_with_options( // falsify N; }` block that covenants it (a witness covenants the `fn` it follows in // source order). Computed once (a pure function of the program, R-CODE-5). A `fn` // ABSENT from this map is a plain v1 item (not covenant-routed) and burns - // unchanged; a `fn` PRESENT is forge-routed and must pass its covenant BEFORE the + // unchanged; a `fn` present is forge-routed and must pass its covenant before the // L3 burn (R-COV-1, covenant-before-burn). No v1 corpus item carries a `witness` // block, so the map is empty on the conformance corpus — a no-op on the v1 oracle. let covenant_bindings = crate::covenant_engine::witness_bindings(&parsed.program); - // Stage-1 forge tier — the per-project lemma namespace NAMES (`.design/stage1-forge-tier.md` + // Stage-1 forge tier — the per-project lemma namespace names (`.design/stage1-forge-tier.md` // REQ-9 / Q1, increment 3). The set of top-level `lemma` names, so the frozen-battery // gate DEFERS a `simp [ … ]` citation that names a project lemma (instead of refusing it // as an unlisted simp lemma): the certified-only resolution then decides it on the @@ -472,7 +472,7 @@ pub fn check_file_with_options( // v1 certification consumer yet (covenant 2b, battery 2c, proof view 2e, // library 3): they are SKIPPED here (no v1 cert), so a hole-free forge item // emits no certificate. EXCEPT (AC-7): a forge item carrying any open `?pN` - // proof hole is incomplete and must NOT certify — it short-circuits to a + // proof hole is incomplete and must not certify — it short-circuits to a // non-certified `OpenHole` cert through the shared `open_proof_hole_reason` // path (the proof-tier mirror of the `?N` body-hole short-circuit above), // before any lowering/verus. No corpus item is forge-tier, so this is a @@ -492,7 +492,7 @@ pub fn check_file_with_options( } // Stage-1 forge tier — the frozen battery (`.design/stage1-forge-tier.md` // REQ-5 / AC-9, increment 2c), the elaboration-time gate. A `lemma`/`proof` - // block's VERBATIM tactic content (captured by 2a) is scanned against the + // block's verbatim tactic content (captured by 2a) is scanned against the // frozen tactic allowlist + the frozen simp set; a proof citing an unlisted // tactic OR an unlisted simp lemma is REFUSED — named — never warned (the // proof-tier mirror of the `thermite_spec::validate` contract cage / the @@ -567,18 +567,18 @@ pub fn check_file_with_options( } // Stage-1 forge tier — the covenant engine (`.design/stage1-forge-tier.md` - // REQ-4, increment 2b), gating the L3 BURN. It runs AFTER the gate_fn + // REQ-4, increment 2b), gating the L3 burn. It runs after the gate_fn // short-circuits (`#[slag]`/`#[boundary]`/`fx diverge` certify L1 by fiat and a - // vacuity/weak-contract reject lands its L0 cert — none of these BURN, so the + // vacuity/weak-contract reject lands its L0 cert — none of these burn, so the // covenant, which gates the burn, does not pre-empt them: a proof-exempt slag // item keeps `slag: true`/L1, slag.md REQ-2). A forge-routed `fn` (one carrying a - // `witness` block) that reaches HERE is on the L3 proof-search path and must pass + // `witness` block) that reaches here is on the L3 proof-search path and must pass // its covenant first (R-COV-1, covenant-before-burn): author `inhabit` witnesses // are EXECUTED against `req` (a witness not satisfying `req` is a loud covenant // error, never dropped), and a `falsify` run rides the SplitMix64 generator over // the item's executable semantics for a `req`-satisfying input the body violates // `ens` on. A malformed/absent covenant is REFUSED — named — and a `falsify` hit - // is `CovenantRefuted` (a hard fail, never degraded); BOTH short-circuit here (the + // is `CovenantRefuted` (a hard fail, never degraded); both short-circuit here (the // `continue`), so the L3 proof search below is never reached without a validated // covenant (the closure-instrumented `covenant_engine::covenant_gate` pins the // structural invariant as a unit test). A VALIDATED covenant records its evidence @@ -636,7 +636,7 @@ pub fn check_file_with_options( // #52 §9 composition weaving (`.design/lower/boundary-composition.md` // REQ-2): weave the in-file `fn`s this item transitively references into - // its §5.3 sub-program — regular fns with their real body, boundary/slag + // its §5.3 sub-program — regular fns with their body, boundary/slag // fns as `#[verifier::external_body]` signatures — so `verus` resolves the // callee and the caller proves through its contract (was an undefined-callee // L0). Empty for a fn referencing only spec fns / combinators (the pure @@ -793,7 +793,7 @@ pub fn check_file_with_options( } // Clean (or a `spec fn`, which carries no contract to check): the solver - // runs the real L3 proof (REQ-3). Assemble the cert exactly as the + // runs the real L3 proof (REQ-3). Assemble the cert as the // non-cached path always has. let verus = run_verus(&lowered, item.name(), seed, rlimit)?; let cert = assemble_certificate(item, &verus); @@ -865,7 +865,7 @@ pub fn check_file_with_options( // #12 §7 step 4 — mutation scoring, after a successful L3 proof of the real // body (`.design/forge/mutation-scoring.md` REQ-7). Reached only on a - // `VerusOutcome::Proved` real body: the cert is `Level::L3` with no reject. + // `VerusOutcome::Proved` body: the cert is `Level::L3` with no reject. // A non-proving item (counterexample / timeout / a `spec fn`) is never // scored — §7's premise is "mutate a known-good body". Each mutant's // re-verify is content-addressed through the same proof cache (#8), so a @@ -892,7 +892,7 @@ pub fn check_file_with_options( // settled L3-certified + scored item (level L3, no reject, a // `MutationScore` produced), so the probe runs: it generates the // frozen candidate stronger-`ens` set, verifies each against the - // real body via the same `run_verus` + #8 cache, keeps the + // body via the same `run_verus` + #8 cache, keeps the // verifying + strictly-stronger ones, and attaches them as // advisory suggestions. The probe never changes the verdict // (`with_strengthening` only adds the additive field + the @@ -1056,7 +1056,7 @@ pub fn check_file_with_engine( } // REQ-8 relax route (`.design/stage1-forge-tier.md` REQ-8 / Q-NLSAT / AC-12, - // increment 2f): the `--engine nlsat` selection routes EVERY item through the + // increment 2f): the `--engine nlsat` selection routes every item through the // [`crate::engine::NlsatEngine`] (the relax fragment) instead of the Lean engine, // and returns early. The default Verus path and the `--engine lean|auto` paths are // untouched, so the v1 corpus stays byte-identical. @@ -1066,7 +1066,7 @@ pub fn check_file_with_engine( // REQ-10 / AC-14 G1 gate route (`.design/stage1-forge-tier.md`): `--engine forge` // drives the whole forge tier end to end on a covenant-routed `fn` via the - // PER-CLAUSE hybrid route (relaxable clauses → nlsat L4, the non-relaxable clause → + // per-clause hybrid route (relaxable clauses → nlsat L4, the non-relaxable clause → // an author-proof Lean L3 with the burn receipt; item level = min over clauses). // Returns early like the nlsat route; the v1 Verus path and the lean/auto paths are // untouched, so the v1 corpus stays byte-identical. @@ -1075,9 +1075,9 @@ pub fn check_file_with_engine( } // Stage-3 REQ-2 (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-2 / AC-3): the - // `--engine bv` PER-CLAUSE bit-vector route (the `mix64` shape). `@bv`-tagged + // `--engine bv` per-clause bit-vector route (the `mix64` shape). `@bv`-tagged // clauses lower to fixed-width QF_BV via the BitVectorEngine; untagged clauses - // lower as before (relaxable → nlsat L4, else honest skip). Returns early like the + // lower as before (relaxable → nlsat L4, else skip). Returns early like the // nlsat/forge routes; the v1 Verus path and the lean/auto paths are untouched, so // the v1 corpus (which carries no `@bv` tag) stays byte-identical. if selection == EngineSelection::Bv { @@ -1097,7 +1097,7 @@ pub fn check_file_with_engine( } }; // A forge-tier item's base cert (a `lemma`/`proof for` OpenHole / Battery* reject) - // is NOT a fn-contract obligation — the fn-Lean re-discharge below would mint an + // is not a fn-contract obligation — the fn-Lean re-discharge below would mint an // empty forge obligation and clobber the reject with a `LeanUnverifiable` skip // (REQ-7, increment 2e). Keep the base reject untouched; a CLEAN forge `lemma` // (absent from `base`, the base path skips it) is discharged by the dedicated @@ -1126,21 +1126,21 @@ pub fn check_file_with_engine( let new_cert = gate_definition_tower(new_cert, &parsed.program, &src, item); // REQ-6a anti-Goodhart (increment 2d): the certify-time arbitrary-result // re-elaboration tautology gate on the forge/Lean discharge path. A forge-tier - // cert whose contract's `ens` still elaborates for an ARBITRARY result (the + // cert whose contract's `ens` still elaborates for an arbitrary result (the // `ens` says nothing about the body) is refused here, at certify time — the L3 // counterpart of the Verus §7 solver-vacuity tautology check. let new_cert = gate_arbitrary_result_tautology(new_cert, &lean, &obligations.contract); out.push(new_cert); } - // Stage-1 forge-tier LEMMA discharge (`.design/stage1-forge-tier.md` REQ-7, increment - // 2e). A `lemma` is a forge-tier item with NO Verus base cert (the base path skips a + // Stage-1 forge-tier lemma discharge (`.design/stage1-forge-tier.md` REQ-7, increment + // 2e). A `lemma` is a forge-tier item with no Verus base cert (the base path skips a // clean forge item, REQ-3) — it is the forge tier's self-contained goal, discharged - // ONLY by the Lean engine. So here, on the Lean path, each clean `lemma` is + // only by the Lean engine. So here, on the Lean path, each clean `lemma` is // discharged via `export_lemma` + `discharge_source` and its cert (carrying the burn // receipt on `Proven`, REQ-7 / AC-11) is appended. A holed / battery-refused lemma // already carries its non-certified cert from the base path (the `OpenHole` / - // `Battery*` reject), so it is present in `out` and NOT re-discharged here. The + // `Battery*` reject), so it is present in `out` and not re-discharged here. The // default Verus path (`check_file`) never enters this function, so a clean lemma is // still skipped there (the v1 oracle + the 2c default-path behavior are untouched). for item in &parsed.program.items { @@ -1152,16 +1152,16 @@ pub fn check_file_with_engine( } // Stage-1 forge tier — the REQ-9 lemma library mechanics (`.design/stage1-forge-tier.md` - // REQ-9 / AC-13, increment 3), the LAST stage-1 feature increment. Runs HERE, after the + // REQ-9 / AC-13, increment 3), the last stage-1 feature increment. Runs here, after the // lemma discharge pass, because it reasons about settled certification status: // 1. Certified-only citation resolution (AC-13): a forge item whose proof cites a - // project lemma that did NOT certify is REFUSED, named — replacing its cert with an + // project lemma that did not certify is REFUSED, named — replacing its cert with an // `UncertifiedLemmaCitation` reject. (A frozen-spine / certified-project / unknown // citation is unaffected — `Unknown` is the frozen battery's concern, already gated.) // 2. Dedup-on-burn citation rewrite (AC-13): a burn receipt's `cited_lemmas` are // rewritten to their canonical (first-certified, same-statement) lemma, so a - // statement-hash duplicate is NOT stored as a copy — the citation points at the one - // stored lemma. Oracle-excluded (the burn receipt is, Q-BURN), so this never perturbs + // statement-hash duplicate is not stored as a copy — the citation points at the one + // stored lemma. Oracle-excluded (the burn receipt is, Q-burn), so this never perturbs // a cert's oracle subset / the v1 goldens. // The default Verus path never enters this function, and the v1 corpus carries no project // lemmas, so this is a no-op on the v1 oracle. @@ -1170,8 +1170,8 @@ pub fn check_file_with_engine( // Stage-1 forge tier — the REQ-9 `dec wf` accessibility cache (`.design/stage1-forge-tier.md` // REQ-9 / Q7 / AC-13, increment 3). Write-through: for every item carrying a `dec wf ` - // measure, cache its accessibility proof keyed by (relation, carrier), so a SUBSEQUENT - // `forge check` re-check on an unchanged (relation, carrier) HITS the cache (the + // measure, cache its accessibility proof keyed by (relation, carrier), so a subsequent + // `forge check` re-check on an unchanged (relation, carrier) hits the cache (the // cross-invocation hit the per-item proof cache uses, observable via the cache layer). The // accessibility verdict is whether the item's recursion was admitted — its cert reached an // L3 (non-rejected) rung. No v1 corpus item uses `dec wf`, so this is a no-op on the v1 @@ -1260,7 +1260,7 @@ fn uncertified_citation_for_item( } thermite_syntax::ForgeItem::Proof(p) if p.target == item => { // A `proof for f` block's several `ens#k` obligations share f's local context - // (Q6); each obligation's proof is scanned, the FIRST uncertified citation + // (Q6); each obligation's proof is scanned, the first uncertified citation // refuses the whole item. for ob in &p.obligations { if let Err(e) = @@ -1281,7 +1281,7 @@ fn uncertified_citation_for_item( /// `.design/stage1-forge-tier.md` REQ-6 / AC-10). Applied to a freshly-produced cert /// on the forge/Lean discharge path: /// -/// - a v1 / Verus-path cert (no `engine_attribution`) is returned UNCHANGED — the +/// - a v1 / Verus-path cert (no `engine_attribution`) is returned unchanged — the /// gate is a forge-tier gate, so the v1 goldens stay byte-identical; /// - a non-`fn` item (a `spec fn` has no contract to root a tower) is returned /// unchanged; @@ -1300,7 +1300,7 @@ fn gate_definition_tower( item: &Item, ) -> Certificate { // The gate is forge-tier-only: a cert with no engine attribution is the v1 Verus - // path (or an honest skip), left byte-identical. + // path (or a skip), left byte-identical. if cert.engine_attribution.is_none() { return cert; } @@ -1326,10 +1326,10 @@ fn gate_definition_tower( /// [`gate_definition_tower`]: /// /// - a v1 / Verus-path cert (no `engine_attribution`) or an already-rejected / -/// non-L3 cert is returned UNCHANGED — the gate is a forge-tier certify gate, so +/// non-L3 cert is returned unchanged — the gate is a forge-tier certify gate, so /// the v1 goldens stay byte-identical and an item that did not certify is not /// re-judged; -/// - otherwise the obligation is re-elaborated with an ARBITRARY result +/// - otherwise the obligation is re-elaborated with an arbitrary result /// ([`crate::engine::LeanEngine::arbitrary_result_reelaboration`]). If the `ens` /// still kernel-accepts for an arbitrary result, it is a body-ignoring tautology → /// the cert is REFUSED (`SemanticTautology`, the same `contract_quality.tautology` @@ -1341,7 +1341,7 @@ fn gate_arbitrary_result_tautology( obligation: &crate::obligation::Obligation, ) -> Certificate { // Forge-tier-only, and only a still-certifying cert: a Verus-path cert (no - // attribution), an honest skip, or an already-rejected/non-L3 cert is left as-is. + // attribution), a skip, or an already-rejected/non-L3 cert is left as-is. if cert.engine_attribution.is_none() || cert.reject.is_some() || cert.level != Level::L3 { return cert; } @@ -1371,7 +1371,7 @@ fn gate_arbitrary_result_tautology( } // Clean (the ens constrains the result) or Skipped (the check could not run — // export refusal / tier-(c) / lake absent): keep the cert. The gate only ever - // rejects a PROVEN tautology, never on an inconclusive run (R-CODE-4). + // rejects a proven tautology, never on an inconclusive run (R-CODE-4). crate::engine::ArbitraryResultOutcome::Clean | crate::engine::ArbitraryResultOutcome::Skipped(_) => cert, } @@ -1397,8 +1397,8 @@ fn lean_package_root() -> PathBuf { /// integer witness); /// - relaxable + `sat` real-only (true over ℤ, false over ℝ) → an L0 `RealWitness` /// escalation cert carrying the raw real point (handed UP to the forge as goal -/// metadata, NEVER a `Counterexample`); -/// - not relaxable / z3 absent / `unknown` → an honest non-certified skip cert naming +/// metadata, never a `Counterexample`); +/// - not relaxable / Z3 absent / `unknown` → a non-certified skip certificate naming /// the reason. /// /// A non-`fn` item (a `spec fn` / forge-tier item carries no relaxable contract) @@ -1418,14 +1418,14 @@ fn nlsat_check(base: Vec, program: &Program) -> Vec { /// Discharge one relaxable-candidate `fn` via the nlsat engine and build its cert /// (`.design/stage1-forge-tier.md` REQ-8 / AC-12). The four outcomes map to the four -/// cert shapes (L4 proved / counterexample / real-witness escalation / honest skip). +/// cert shapes (L4 proved / counterexample / real-witness escalation / skip). fn nlsat_item_cert( engine: &crate::engine::NlsatEngine, f: &thermite_syntax::FnItem, base: Certificate, ) -> Certificate { use crate::engine::NlsatOutcome; - // A non-relaxable item is an honest skip naming the disqualifying construct (never + // A non-relaxable item is a skip naming the disqualifying construct (never // a false verdict) — the relax route's fragment gate (REQ-8b). if !engine.admits_relax(&base.item) { let reason = match crate::relax::classify_fn(f) { @@ -1497,8 +1497,8 @@ fn nlsat_l4_cert(engine: &crate::engine::NlsatEngine, base: &Certificate) -> Cer } /// The L0 `Counterexample` cert an nlsat integer falsifier produces (`.design/ -/// stage1-forge-tier.md` REQ-8 / AC-12). The real relaxation was `sat` AND an integer -/// point in the radius-2 ℤⁿ box genuinely falsifies the integer clause — a real +/// stage1-forge-tier.md` REQ-8 / AC-12). The real relaxation was `sat` and an integer +/// point in the radius-2 ℤⁿ box falsifies the integer clause — a real /// counterexample over ℤ. The cert is non-certified (L0) with the integer witness and /// the per-clause [`CertVerdict::Counterexample`]. fn nlsat_counterexample_cert( @@ -1544,8 +1544,8 @@ fn nlsat_counterexample_cert( /// The L0 `RealWitness` escalation cert (`.design/stage1-forge-tier.md` REQ-8 / /// AC-12 — the headline relax behavior). The real relaxation was `sat` but the /// countermodel is real-only: true over ℤ, false over ℝ (no nearby integer falsifies -/// it). The clause does NOT refute — it escalates UP to the forge as goal metadata, -/// carrying the raw real point in a [`CertVerdict::RealWitness`]; it is NEVER a +/// it). The clause does not refute — it escalates UP to the forge as goal metadata, +/// carrying the raw real point in a [`CertVerdict::RealWitness`]; it is never a /// `Counterexample`. The cert is non-certified at the nlsat tier (L0) with the /// `RealWitnessEscalation` cause. fn nlsat_realwitness_cert( @@ -1593,31 +1593,31 @@ fn nlsat_realwitness_cert( // ───────────────────────────────────────────────────────────────────────────── // ───────────────────────────────────────────────────────────────────────────── // Stage-3 REQ-2 (`.design/stage3-bv-reconstruction.md` REQ-2 / AC-2 / AC-3): the -// `--engine bv` PER-CLAUSE bit-vector route (the RFC's `mix64` shape). A `@bvN`-tagged +// `--engine bv` per-clause bit-vector route (the RFC's `mix64` shape). A `@bvN`-tagged // clause lowers to fixed-width QF_BV via the BitVectorEngine (the `EngineName:: // BitVector` route alongside stage-1's `Nlsat`); an untagged clause lowers as before -// (relaxable polynomial → nlsat L4, else an honest skip). One function thus carries +// (relaxable polynomial → nlsat L4, else a skip). One function thus carries // wraparound and unbounded clauses side by side, each labeled per engine; the item // level is the MIN over the clauses. A `@bv`-tagged `lemma` is discharged directly by // the bit-vector engine with no author proof block (AC-2). // ───────────────────────────────────────────────────────────────────────────── /// The `--engine bv` per-clause bit-vector pass (`.design/stage3-bv-reconstruction.md` -/// REQ-2). Only a `@bv`-TAGGED `fn` is rebuilt by [`bv_fn_cert`] (its `ens` clauses +/// REQ-2). Only a `@bv`-tagged `fn` is rebuilt by [`bv_fn_cert`] (its `ens` clauses /// dispatched by their `@bv` tag); each `@bv`-tagged `lemma` is discharged directly by -/// the bit-vector engine ([`bv_lemma_cert`]). An UNTAGGED `fn` — and any non-`fn`, -/// non-bv-lemma item — keeps its base cert UNCHANGED: the bit-vector route is a per-item +/// the bit-vector engine ([`bv_lemma_cert`]). An untagged `fn` — and any non-`fn`, +/// non-bv-lemma item — keeps its base cert unchanged: the bit-vector route is a per-item /// overlay, never a wholesale re-route. This matters because the route is auto-selected /// by `forge audit`/`forge review` whenever a program contains *any* `@bv` tag (REQ-3 / /// AC-4); routing an ordinary Verus-provable `fn` through the bv route would reject its /// non-`@bv`, non-relaxable clauses (`BvUntaggedUnsupported`) and silently DOWNGRADE a -/// genuine L3 function to L0 in the audit — a faithfulness bug. The v1 corpus carries no +/// L3 function to L0 in the audit — a faithfulness bug. The v1 corpus carries no /// `@bv` tag, so its goldens are byte-identical. fn bv_check(base: Vec, program: &Program) -> Vec { let bv = crate::bitvector::BitVectorEngine::new(); let nlsat = crate::engine::NlsatEngine::new(program.clone()); // The reachable `struct`/`enum` decls the bv-semantics mutation battery (REQ-4 / - // AC-5) threads into `mutation::generate` for the F-STRUCT-ZERO early-return family — + // AC-5) threads into `mutation::generate` for the F-STRUCT-zero early-return family — // the same `adt_deps` shape the Verus + Lean batteries weave. let adt_deps: Vec = program .items @@ -1664,7 +1664,7 @@ fn lemma_has_bv_tag(l: &thermite_syntax::LemmaItem) -> bool { l.ens.iter().any(|c| c.bv.is_some()) } -/// Does the program carry ANY `@bv`-tagged clause (`.design/stage3-bv-reconstruction.md` +/// Does the program carry any `@bv`-tagged clause (`.design/stage3-bv-reconstruction.md` /// REQ-3 / AC-4)? `true` iff some `fn`'s `ens` or some `lemma`'s `ens` is `@bv`-tagged. /// `forge audit` / `forge review` consult this to route a bit-vector project through the /// bv engine (so the shadow flags surface), while a tag-free program — every v1 / non-bv @@ -1702,7 +1702,7 @@ fn expr_mentions_result(e: &thermite_syntax::Expr) -> bool { /// Ground a `@bv` clause's `result` by the function body (`.design/stage3-bv-reconstruction.md` /// REQ-2). A clause that names `result` is rewritten with `result := body` (the /// forge-route precedent, [`substitute_result_with_body`]) so the QF_BV query is closed -/// over the parameters; a clause naming `result` on a body-less `fn` is an honest skip +/// over the parameters; a clause naming `result` on a body-less `fn` is a skip /// (`Err`) rather than a query with a free, unconstrained `result` (which could mint a /// spurious counterexample). fn ground_result_in_clause( @@ -1727,14 +1727,14 @@ fn ground_result_in_clause( /// Build one `fn`'s `--engine bv` certificate (`.design/stage3-bv-reconstruction.md` /// REQ-2 / AC-2 / AC-3 — the `mix64` shape). Each `ens` clause is dispatched by its -/// `@bv` tag: a TAGGED clause → the [`crate::bitvector::BitVectorEngine`] (QF_BV at the -/// tag width); an UNTAGGED clause → the [`crate::engine::NlsatEngine`] when it is a -/// relaxable polynomial (the unbounded side), else an honest skip. A `Proved` tagged +/// `@bv` tag: a tagged clause → the [`crate::bitvector::BitVectorEngine`] (QF_BV at the +/// tag width); an untagged clause → the [`crate::engine::NlsatEngine`] when it is a +/// relaxable polynomial (the unbounded side), else a skip. A `Proved` tagged /// clause certifies at the caged rung [`Level::L4`] (decidable, complete bit-pattern /// countermodels; solver-trusted Z3 QF_BV), as does an nlsat `Proved`; -/// the item level is the MIN. The FIRST non-certifying clause (a bit-level +/// the item level is the MIN. The first non-certifying clause (a bit-level /// counterexample, an over-budget multiplier timeout, an undecided/unsupported clause) -/// short-circuits to its honest non-certified cert. +/// short-circuits to its non-certified certificate. fn bv_fn_cert( bv: &crate::bitvector::BitVectorEngine, nlsat: &crate::engine::NlsatEngine, @@ -1760,7 +1760,7 @@ fn bv_fn_cert( Err(reason) => return bv_skip_cert(&f.name, &effects, slag, k, tag, &reason), }; // Anti-Goodhart vacuity gate (RFC-1 §10): a `@bv` clause is discharged as - // `req ⇒ clause`, so an UNSATISFIABLE `req` proves EVERY clause vacuously. + // `req ⇒ clause`, so an unsatisfiable `req` proves every clause vacuously. // The bv mutation gate only catches this for result-referencing clauses // (every mutant survives → WeakContract); a param-only clause would otherwise // certify L4 — and, post-REQ-8, carry a kernel-checked trust label — on a @@ -1787,7 +1787,7 @@ fn bv_fn_cert( match bv.discharge_bv(&vars, Some(req), &clause_expr, tag.width) { BvOutcome::Proved => { // Lock 3 (REQ-5 / AC-6): a `nowrap` clause additionally discharges its - // no-overflow side obligation in-cage. A witnessed overflow REJECTS + // no-overflow side obligation in-cage. A witnessed overflow rejects // (the nowrap promise is violated); a holds/undecided verdict rides the // clause's `bv_shadow.nowrap_obligation`. let nowrap = if tag.nowrap { @@ -1810,7 +1810,7 @@ fn bv_fn_cert( &bv_attr, nowrap, )); - // A `@bv` clause is decidable QF_BV with COMPLETE bit-pattern + // A `@bv` clause is decidable QF_BV with complete bit-pattern // countermodels — the L4 (caged) refutation quality, RFC-1 §2/§4. // The rung is the refutation quality; the SOLVER trust base // (`solver Z3 QF_BV`) is recorded separately in the attribution and @@ -1834,7 +1834,7 @@ fn bv_fn_cert( } } else { // Untagged → the unbounded side: nlsat when the clause is a relaxable - // polynomial, else an honest skip (this route lowers only the bit-vector + // polynomial, else a skip (this route lowers only the bit-vector // clauses and the relaxable unbounded clauses). let synth = single_ens_fn(f, ens); if crate::relax::classify_fn(&synth).is_relaxable() { @@ -1887,14 +1887,14 @@ fn bv_fn_cert( .graduate_triage_clean() .with_engine_attribution(attribution); // Lock 2 (REQ-4 / AC-5, RFC-1 §10 anti-Goodhart): the bv-semantics mutation battery. - // Run the FROZEN mutation catalogue against this fn's `@bv` clauses with the WRAP-AWARE + // Run the frozen mutation catalogue against this fn's `@bv` clauses with the WRAP-AWARE // kill check (each mutant re-discharged at the tag width). `bv_mutation_score` returns: // - `None` when the fn carries no result-referencing `@bv` clause — a tagged clause // closed over the parameters only (`mix64`'s `a + b == b + a`) is a body-INVARIANT // algebraic identity, not a body-constraining discriminator, so there is nothing - // to pin; it is NOT gated (gating it would spuriously reject a machine-valid fn); + // to pin; it is not gated (gating it would spuriously reject a machine-valid fn); // - `Some(score)` when a result-referencing `@bv` clause IS mutation-discriminating. - // Then the wrap-aware kill ratio GATES the cert exactly as the Verus and Lean + // Then the wrap-aware kill ratio gates the cert as the Verus and Lean // paths do (`meets_floor`): a below-floor score is a `WeakContract` reject, never a // silent L4. Without this gate a weak/tautological result clause (`ens@bv64 // result + 0 == result`) would survive every mutant yet still certify — the @@ -1917,12 +1917,12 @@ fn bv_fn_cert( } /// The bv-semantics mutation battery for a `@bv`-tagged `fn` -/// (`.design/stage3-bv-reconstruction.md` REQ-4 / AC-5 — lock 2). Reuses the FROZEN -/// mutation operator catalogue [`crate::mutation::generate`] UNCHANGED (the stage-1 -/// re-elaboration precedent — only the kill check swaps, exactly as +/// (`.design/stage3-bv-reconstruction.md` REQ-4 / AC-5 — lock 2). Reuses the frozen +/// mutation operator catalogue [`crate::mutation::generate`] unchanged (the stage-1 +/// re-elaboration precedent — only the kill check swaps, as /// [`forge_reelaboration_mutation`] swaps Verus for the Lean engine): for each mutant, /// every result-referencing `@bv`-tagged `ens` clause is re-discharged with `result` -/// grounded by the mutant's effective body, over fixed-width QF_BV semantics AT THE TAG +/// grounded by the mutant's effective body, over fixed-width QF_BV semantics AT the TAG /// WIDTH (the wrap-aware kill check). The classification reuses /// [`crate::mutation::classify_mutant`]'s oracle, now evaluated at width: /// @@ -1936,7 +1936,7 @@ fn bv_fn_cert( /// an unrenderable mutant) does not decide the mutant; a mutant with no decisive /// clause is dropped from the denominator (the OQ-5 precedent — never a silent kill). /// -/// Returns `None` when the fn carries NO result-referencing `@bv` clause: a tagged +/// Returns `None` when the fn carries no result-referencing `@bv` clause: a tagged /// clause closed over the parameters (`mix64`'s `a + b == b + a`) is invariant under a /// body mutation, so it neither kills nor distinguishes any mutant — there is no /// scoreable discriminator and the cert keeps its forward-declared score (so the AC-2 @@ -1970,7 +1970,7 @@ fn bv_mutation_score( let vars: Vec = f.params.iter().map(|p| p.name.clone()).collect(); let req = &f.contract.req.expr; let mutants = crate::mutation::generate(f, 0, adt_deps); - // The ORIGINAL body's effective result — the reference for the observable-equivalence + // The original body's effective result — the reference for the observable-equivalence // exclusion below (#101, at width). let orig_result = f.body.as_ref().and_then(effective_result_expr); let mut killed = 0usize; @@ -1986,7 +1986,7 @@ fn bv_mutation_score( continue; }; // Re-discharge every result-referencing tagged clause at its width with `result` - // grounded by THIS mutant's body — the wrap-aware kill check. + // grounded by this mutant's body — the wrap-aware kill check. let mut decided = false; // at least one tagged clause gave a decisive verdict let mut caught = false; // some tagged clause refuted the mutant at width for (tag, clause_expr) in &result_clauses { @@ -2019,9 +2019,9 @@ fn bv_mutation_score( } // The mutant SURVIVED at width. Apply the observable-equivalence exclusion (#101) // AT WIDTH before counting it: a mutant whose result is provably equal to the - // ORIGINAL body's result over QF_BV — mod 2^width for EVERY discriminator clause — + // original body's result over QF_BV — mod 2^width for every discriminator clause — // is not a real discriminator (it computes the same observable value), so it is - // netted out of `scored` rather than counted a survivor, exactly as the Verus path + // netted out of `scored` rather than counted a survivor, as the Verus path // nets out proved-equivalent mutants before the floor gate. Without this, a loose // but legitimate contract (`ens@bv64 result >= x` over `{ x }`, whose `return x` // variants are equivalent) would be spuriously gated `WeakContract`. @@ -2062,7 +2062,7 @@ fn bv_mutation_score( /// REQ-2 / AC-2 — "the injectivity lemma discharges at `@bv64` with no proof block"). /// A lemma carries no `result` and no body: each `@bv`-tagged `ens` clause is a closed /// QF_BV query over the parameters under the lemma's `req`. A non-tagged lemma clause is -/// an honest skip (this route lowers only the bit-vector clauses). The lemma certifies +/// a skip (this route lowers only the bit-vector clauses). The lemma certifies /// at [`Level::L4`] (the caged rung — decidable, complete bit-pattern countermodels; /// solver-trusted Z3 QF_BV); a counterexample / timeout short-circuits. fn bv_lemma_cert( @@ -2155,21 +2155,17 @@ fn bv_lemma_cert( /// The per-clause [`ObligationResult`] a bit-vector `Proved` records (`.design/ /// stage3-bv-reconstruction.md` REQ-2 / AC-2 + REQ-8 / AC-9): the engine (`bitvector`), /// the clause's trust base (migrated per REQ-8 — see below), the `Proved` verdict, and a -/// name that states the engine AND the fixed-width semantics ("each clause's certificate +/// name that states the engine and the fixed-width semantics ("each clause's certificate /// naming its engine and semantics"). /// /// REQ-8 default-on trust migration: `clause_expr` is the `result`-grounded clause body /// (the same expression the QF_BV query decided). The per-clause fragment-support check /// [`crate::lean_smt_export::clause_reconstruction_supported`] decides the trust base: /// -/// - a RECONSTRUCTION-SUPPORTED clause (the arithmetic/comparison QF_BV subset the exporter -/// renders) migrates its `trust:` to the KERNEL-CHECKED base -/// ([`crate::engine::bv_kernel_checked_trust_profile`] — the lean-smt reconstruction over -/// the bounded-integer model + the kernel-checked `BvModel.frmInt_iff_frmBV` faithfulness), -/// with Z3 no longer load-bearing — same rung (L4), smaller trust; -/// - an UNSUPPORTED clause (the bitwise/shift/rotate subset the exporter refuses) keeps the -/// SOLVER base `solver_attr.trust_profile` (`Z3 QF_BV`), labeled as today (the F-J residual -/// the audit names). +/// - a reconstruction-supported clause migrates its `trust:` to the kernel-checked +/// literal-`BitVec N` base ([`crate::engine::bv_kernel_checked_trust_profile`]); +/// - a clause with an expression outside the exporter's QF_BV surface keeps the solver +/// base `solver_attr.trust_profile` (`Z3 QF_BV`). /// /// The engine tag stays `bitvector` (the bit-vector route decided the clause); only the /// trust base — the orthogonal axis — moves. Default-on: no flag gates the migration. @@ -2189,14 +2185,13 @@ fn bv_proved_obl( ( crate::engine::bv_kernel_checked_trust_profile().items, "kernel-checked (the (P_prod) ⟺ (P_ref) obligation reconstructed in the Lean kernel \ - via lean-smt over the bounded-integer model + the kernel-checked BvModel.lean \ - faithfulness; Z3 no longer load-bearing — REQ-8 default-on)", + over literal BitVec N semantics; Z3 no longer load-bearing — REQ-8 default-on)", ) } else { ( solver_attr.trust_profile.clone(), - "solver-trusted (Z3 QF_BV) — the bitwise/shift/rotate subset is outside the \ - reconstruction-supported fragment, named honestly (F-J residual)", + "solver-trusted (Z3 QF_BV) — the clause expression is outside the \ + reconstruction-supported QF_BV surface (F-J residual)", ) }; ObligationResult::discharged(format!( @@ -2216,7 +2211,7 @@ fn bv_proved_obl( .with_bv_shadow(bv_shadow_for(tag, nowrap_obligation)) } -/// The outcome of a `@bvN(nowrap)` clause's no-overflow SIDE OBLIGATION (`.design/ +/// The outcome of a `@bvN(nowrap)` clause's no-overflow side obligation (`.design/ /// stage3-bv-reconstruction.md` REQ-5 / AC-6 — lock 3), run after the main clause is /// `Proved`. The obligation is discharged in-cage by [`crate::bitvector::BitVectorEngine:: /// discharge_nowrap`]; this triages the result into a verdict the certificate records. @@ -2230,7 +2225,7 @@ enum NowrapVerdict { /// silently passed and never a false `nowrap` claim). Undecided(String), /// A concrete overflowing input — the `nowrap` promise is violated, so the cert is - /// REJECTED (a witnessed nowrap violation must not certify). The verdict + bit + /// rejected (a witnessed nowrap violation must not certify). The verdict + bit /// pattern ride the rejection cert's `bv_shadow.nowrap_obligation`. Overflow { verdict: String, @@ -2290,7 +2285,7 @@ fn render_bv_pattern(bits: &[crate::bitvector::BvBitPattern]) -> String { /// The rejection cert a FAILED `@bvN(nowrap)` no-overflow side obligation produces /// (`.design/stage3-bv-reconstruction.md` REQ-5 / AC-6 — lock 3). The main clause was /// `Proved` at width, but the `nowrap` promise is violated by a concrete overflowing -/// input, so the item does NOT certify: a witnessed nowrap violation must not pass. The +/// input, so the item does not certify: a witnessed nowrap violation must not pass. The /// witness obligation records the overflowing bit pattern in `bv_shadow.nowrap_obligation` /// (so `grep bv_shadow` still finds this tagged clause) with the per-clause /// [`crate::verdict::CertVerdict::Counterexample`]. @@ -2320,7 +2315,7 @@ fn bv_nowrap_overflow_cert( ); let witness_obl = ObligationResult::failed(format!("{item}#ens#{k}#nowrap"), None, Some(detail.clone())) - // Lock 1 + Lock 3: the shadow flag stays greppable AND carries the nowrap + // Lock 1 + Lock 3: the shadow flag stays greppable and carries the nowrap // verdict, so the failing side obligation is visible in the certificate. .with_bv_shadow(bv_shadow_for(tag, Some(verdict.to_string()))); let cert_verdict = crate::verdict::CertVerdict::Counterexample { @@ -2500,10 +2495,10 @@ fn bv_timeout_cert( cert.with_engine_attribution(attr.clone()) } -/// The honest-skip cert a non-rendering / Z3-absent `@bv` clause produces (`.design/ +/// The skip cert a non-rendering / Z3-absent `@bv` clause produces (`.design/ /// stage3-bv-reconstruction.md` REQ-2): non-certified, naming the reason — never a false /// verdict (the nlsat-route `NlsatUnknown` precedent). It still carries Lock 1 — the -/// shadow flag (REQ-3 / AC-4): a skipped clause is STILL a `@bv`-tagged clause, so the +/// shadow flag (REQ-3 / AC-4): a skipped clause is still a `@bv`-tagged clause, so the /// machine-semantics fork must stay greppable even when the route could not decide it. /// The skip obligation records no engine/trust/verdict (nothing was discharged), only /// the failure diagnostic and the shadow flag. @@ -2586,7 +2581,7 @@ fn forge_gate_check(base: Vec, program: &Program, src: &str) -> Vec /// per-clause discharge (the burn), then the meaning audit + re-elaboration mutation, then /// the assembled certificate. A non-certifying outcome at any gate (a refused/refuted /// covenant, an over-budget tower, an undecided clause, a sub-floor mutation score) lands -/// its honest non-certified certificate. +/// its non-certified certificate. #[allow( clippy::too_many_arguments, reason = "the gate threads the two engines, the \ @@ -2851,7 +2846,7 @@ fn forge_gate_item_cert( /// A synthetic single-`ens` clone of `f` (`.design/stage1-forge-tier.md` REQ-10): the /// same signature + `req`, but only the one `ens` clause — so `relax::classify_fn` decides -/// THAT clause's relaxability (the gate routes per clause) and `NlsatEngine::discharge_relax` +/// that clause's relaxability (the gate routes per clause) and `NlsatEngine::discharge_relax` /// proves it in isolation. fn single_ens_fn( f: &thermite_syntax::FnItem, @@ -2955,7 +2950,7 @@ fn synth_l3_lemma( /// Discharge the synthesized L3-clause lemma via the Lean engine (`.design/ /// stage1-forge-tier.md` REQ-7 / REQ-10): export the `∀ params, req → ens` theorem and run /// lake + the certify-time axiom gate (the same path the forge-tier `lemma` discharge -/// uses). An export refusal is an honest `Unknown` (a skip, never a false `Proven`). The +/// uses). An export refusal is an `Unknown` (a skip, never a false `Proven`). The /// clauses reference only arithmetic (no spec fns), so the closure is empty. fn discharge_gate_l3_clause( lean: &crate::engine::LeanEngine, @@ -2974,12 +2969,12 @@ fn discharge_gate_l3_clause( } /// The re-elaboration mutation score of the L3 clause (`.design/stage1-forge-tier.md` -/// REQ-6b / REQ-10 anti-Goodhart). Reuses the FROZEN mutation operator catalogue +/// REQ-6b / REQ-10 anti-Goodhart). Reuses the frozen mutation operator catalogue /// [`crate::mutation::generate`] (the same catalogue the Verus + Lean batteries use): for /// each mutant, the L3 clause is re-discharged with `result` grounded by the mutant's -/// effective body and the SAME author proof. A mutant the clause catches (the +/// effective body and the same author proof. A mutant the clause catches (the /// decision-procedure proof no longer closes) is KILLED; one the clause still admits -/// SURVIVES. An un-exportable mutant is excluded from the denominator (the OQ-5 precedent). +/// survives. An un-exportable mutant is excluded from the denominator (the OQ-5 precedent). fn forge_reelaboration_mutation( lean: &crate::engine::LeanEngine, program: &Program, @@ -3139,7 +3134,7 @@ fn verus_verdict_of(cert: &Certificate) -> crate::engine::Verdict { return Verdict::Proven(Evidence { verified: 1, key }); } // A witnessed counterexample (a failing obligation carrying a `--> span`) is a - // genuine refutation; a witness-less failure (timeout / fast-unknown) is Unknown + // refutation; a witness-less failure (timeout / fast-unknown) is Unknown // (REQ-3.1 — refutation requires a witnessing input). let witnessed = cert.obligations.iter().any(|o| o.location.is_some()); if witnessed { @@ -3169,7 +3164,7 @@ fn lean_proven_cert( let attribution = crate::engine::attribution_for(lean); // Schema-v2 per-clause block (REQ-1/AC-4): a Lean-discharged clause records its // engine, named trust base, and the cert-level verdict (`Proved` — this function is - // reached ONLY on a Lean `Verdict::Proven`). This is a forge-tier path, never the v1 + // reached only on a Lean `Verdict::Proven`). This is a forge-tier path, never the v1 // Verus corpus, so the v1 golden certs stay byte-identical (their clauses carry no // per-clause block). let cert = Certificate::new( @@ -3263,10 +3258,10 @@ fn lean_interactive_proven_cert( /// and emit its certificate (`.design/stage1-forge-tier.md` REQ-7, increment 2e). A /// `lemma` is the forge tier's self-contained goal: [`crate::lean_export::export_lemma`] /// emits the `∀ params, req → ens` theorem proved by the author's frozen-battery tactics, -/// and [`crate::engine::LeanEngine::discharge_source`] runs lake + the SAME certify-time +/// and [`crate::engine::LeanEngine::discharge_source`] runs lake + the same certify-time /// axiom gate every Lean path runs. On `Proven` the lemma certifies L3 (kernel-accepted, -/// the INTERACTIVE trust profile — the proof is author-authored), and the cert carries -/// the BURN RECEIPT (the committed proof's lexer-token count + cited lemmas, REQ-7 / +/// the interactive trust profile — the proof is author-authored), and the cert carries +/// the burn receipt (the committed proof's lexer-token count + cited lemmas, REQ-7 / /// AC-11). An export refusal (out-of-fragment / incomplete registry) or a non-`Proven` /// verdict (lake failure / surviving `sorry` / axiom-gate refusal / Lean absent) is an /// HONEST non-certified L0 cert naming the cause — never a false L3, and no burn receipt @@ -3330,10 +3325,10 @@ fn discharge_forge_lemma( } /// The L3 cert a `Proven` forge-tier `lemma` produces (REQ-7, increment 2e): like -/// [`lean_interactive_proven_cert`] (the INTERACTIVE trust profile — the proof is the +/// [`lean_interactive_proven_cert`] (the interactive trust profile — the proof is the /// author's frozen-battery tactics, a reviewed step) but built directly for the lemma -/// (no fn base cert), and carrying the BURN RECEIPT minted from the committed proof text -/// (the lexer-token count + cited lemmas, REQ-7 / AC-11 — oracle-excluded per Q-BURN, so +/// (no fn base cert), and carrying the burn receipt minted from the committed proof text +/// (the lexer-token count + cited lemmas, REQ-7 / AC-11 — oracle-excluded per Q-burn, so /// it does not perturb the cert oracle). fn lean_lemma_proven_cert( lean: &crate::engine::LeanEngine, @@ -3371,16 +3366,16 @@ fn lean_program(lean: &crate::engine::LeanEngine) -> &Program { lean.program() } -/// The SHARED mutant catalogue the L3 re-elaboration mutation battery scores +/// The shared mutant catalogue the L3 re-elaboration mutation battery scores /// (`.design/stage1-forge-tier.md` REQ-6 / AC-10, increment 2d — anti-Goodhart defense /// (b)). The L3 counterpart of the shipped Verus mutation gate (`mutation_score`, #12): -/// it reuses the FROZEN mutation operator catalogue [`crate::mutation::generate`] -/// UNCHANGED — the same operator families and the same `MUTANT_CAP` = 64 deterministic -/// order-prefix `generate` applies internally. The catalogue is SHARED, never forked +/// it reuses the frozen mutation operator catalogue [`crate::mutation::generate`] +/// unchanged — the same operator families and the same `MUTANT_CAP` = 64 deterministic +/// order-prefix `generate` applies internally. The catalogue is shared, never forked /// (AC-10 pins this with a test: the re-elaboration battery's mutant set IS /// `mutation::generate`'s, so a future fork breaks the test). /// -/// Only the KILL CHECK differs from the Verus gate, exactly as REQ-6b specifies: the +/// Only the KILL check differs from the Verus gate, as REQ-6b specifies: the /// Verus gate runs a per-mutant Verus SOLVER search; the L3 path RE-ELABORATES the /// mutant's obligation through the existing Lean discharge path /// ([`lean_mutation_score`] → [`crate::engine::LeanEngine::discharge`], which exports @@ -3391,18 +3386,18 @@ fn lean_program(lean: &crate::engine::LeanEngine) -> &Program { /// Budd–Angluin floor gate, [`crate::engine::LeanMutationTally::meets_floor`]). /// /// Performance (the flagged REQ-6a/b risk): up to `MUTANT_CAP` = 64 re-elaborations -/// per item. Each is ONE lake elaboration (no proof search), and the battery is a -/// POST-proof QUALITY gate — exactly parallel to the shipped Verus `mutation_score`, -/// which already runs up to 64 verus runs per item AFTER the L3 proof. It is NOT inside +/// per item. Each is one lake elaboration (no proof search), and the battery is a +/// POST-proof QUALITY gate — parallel to the shipped Verus `mutation_score`, +/// which already runs up to 64 verus runs per item after the L3 proof. It is not inside /// the per-clause [`crate::engine`] `KernelBudget` (Q4 30s/clause), which bounds the -/// discharge of ONE clause's proof, not the post-proof mutation battery. So the 64 +/// discharge of one clause's proof, not the post-proof mutation battery. So the 64 /// re-typechecks do not exceed the per-clause budget (they are not within it); the /// `MUTANT_CAP` budget is the same bound the Verus gate already lives under. pub(crate) fn reelaboration_mutants( f: &thermite_syntax::FnItem, adt_deps: &[Item], ) -> Vec { - // The SHARED frozen catalogue (REQ-6b / AC-10 — not a fork). `generate` applies the + // The shared frozen catalogue (REQ-6b / AC-10 — not a fork). `generate` applies the // `MUTANT_CAP` 64 order-prefix internally, so the returned set is already bounded. crate::mutation::generate(f, 0, adt_deps) } @@ -3428,9 +3423,9 @@ fn lean_mutation_score( let mut tally = crate::engine::LeanMutationTally::default(); let base_program = lean_program(lean); // The Lean-path caller threads the whole program's items as `adt_deps` - // (REQ-11) so the F-STRUCT-ZERO family resolves any struct return — the same + // (REQ-11) so the F-STRUCT-zero family resolves any struct return — the same // items the per-mutant Lean engine exports from (`program_with_mutant`). The mutant - // set is the SHARED frozen catalogue via `reelaboration_mutants` (REQ-6b / AC-10: + // set is the shared frozen catalogue via `reelaboration_mutants` (REQ-6b / AC-10: // the L3 re-elaboration battery reuses `mutation::generate`, never a fork) — the // per-mutant kill check below is the re-elaboration (export → lake type-check), not // a Verus solver run. @@ -3443,7 +3438,7 @@ fn lean_mutation_score( let mutant_program = program_with_mutant(base_program, &mutant.item); let mutant_engine = crate::engine::LeanEngine::new(mutant_program.clone(), lean_package_root()); - // The mutant's contract obligation (the same closure the real item carries — + // The mutant's contract obligation (the same closure the item carries — // a mutant body references the same spec-fns), over the mutant program. let called = reachable_spec_fn_names_full(&mutant_program, &mutant.item); let obligation = crate::obligation::Obligation::contract_for_fn(&mutant.item, called); @@ -3488,8 +3483,8 @@ fn program_with_mutant(base: &Program, mutant: &thermite_syntax::FnItem) -> Prog } /// Build the Unverifiable-skip cert a Lean `Unknown` produces under `--engine -/// lean` (`.design/verified/proof-backends.md` OQ-1 — "non-exportable → honest -/// Unverifiable/skip reporting"). Level::L0 with a structured reject naming the skip +/// lean` (`.design/verified/proof-backends.md` OQ-1). This is `Level::L0` with a +/// structured reject naming the skip /// reason, never a false `Proven` and never a silent pass. pub(crate) fn lean_unverifiable_cert( base: &Certificate, @@ -3501,10 +3496,10 @@ pub(crate) fn lean_unverifiable_cert( } }; // Classify the non-discharge through the cert-level vocabulary (REQ-1/AC-1): a Lean - // elaboration/kernel-budget exhaustion is `KernelBudget` (produced UPSTREAM via the + // elaboration/kernel-budget exhaustion is `KernelBudget` (produced upstream via the // textually-distinct signal in the reason detail, Q-KBSIGNAL), never mis-labelled a // solver `Timeout`. The classification is recorded in the reject reason so the skip - // is honestly attributed (a budget exhaustion vs a plain unverifiable skip). + // is attributed (a budget exhaustion vs a plain unverifiable skip). let cert_verdict = crate::verdict::cert_verdict_for_lean( &detail, &crate::engine::Verdict::Unknown(reason.clone()), @@ -3559,7 +3554,7 @@ pub fn check_l2_file(path: impl AsRef) -> Result, ForgeEr // The file's pure `spec fn`s are shared dependencies woven into every per-item // sub-program (so a `fn` whose `ens` references one still lowers + checks), - // exactly as the L3 path does (§5.3 per-item isolation). + // as the L3 path does (§5.3 per-item isolation). let spec_items: Vec = parsed .program .items @@ -3730,7 +3725,7 @@ fn gate_fn(f: &thermite_syntax::FnItem) -> GateOutcome { // is met by `return 0`, so the §7 battery would mis-reject it `WeakContract` // at L0). The §7.1 (a)/(b)/(c) triage still applies (divergence exempts // proving total correctness, not stating a non-vacuous contract — a diverge - // fn with a vacuous `ens` is still rejected), exactly as for `#[boundary]`. + // fn with a vacuous `ens` is still rejected), as for `#[boundary]`. // The cap is built here, before any prover runs, keyed strictly on the // `fx diverge` declaration (R-DEFER-9): it is a structural cap, not a // verus-timeout degrade and not a counterexample (degrade-ladder.md REQ-9). @@ -3819,7 +3814,7 @@ fn diverge_l1_cert(item: String, effects: Vec) -> Certificate { /// - A `fn` is verified against itself, the file's `spec fn`s (the pure shared /// dependencies its contract may reference), and the in-file `fn`s its body /// transitively references (`fn_deps`, the #52 §9 composition weaving). A -/// regular reachable fn is woven with its real body (fully lowered + proved); +/// regular reachable fn is woven with its body (fully lowered + proved); /// a `#[boundary]`/`#[slag]` reachable fn is woven as a /// `#[verifier::external_body]` signature (`thermite_lower::lower`'s /// composition arm), so `verus` resolves the foreign callee and the caller @@ -3880,7 +3875,7 @@ fn item_subprogram( } // A `struct`/`enum` whose `inv`/`well_formed` predicate names a user // `spec fn` must weave that spec fn's definition into its sub-program — - // exactly as the `Item::Fn` arm weaves the file's `spec_items` (#232). + // as the `Item::Fn` arm weaves the file's `spec_items` (#232). // The stale "dead-in-1a: dies at the validator" premise was wrong: a // struct with an `inv` lowers to a `pub open spec fn well_formed` whose // body calls the named spec fn, and live `forge check` does produce a @@ -4196,7 +4191,7 @@ fn mint_item_obligations(program: &Program, item: &Item) -> ItemObligations { // (its dec-check is the common registry-termination discharge path). The Verus // fragment admits the whole frozen subset, so this holds; a narrower future // engine that did not admit a class would block the conjunction (the obligation - // would be an honest `Unknown`, never a silent skip). `debug_assert` records the + // would be an `Unknown`, never a silent skip). `debug_assert` records the // invariant without changing the release verdict (R-CODE-2 — no panic in prod). let engine = crate::engine::VerusEngine; debug_assert!( @@ -4544,7 +4539,7 @@ pub(crate) fn collect_expr_spec_fn_calls( /// (`error[E0425]: cannot find type`), verus fails, and the item degrades to L0. /// /// `referrers` is the checked item plus every woven `fn` dependency (a regular -/// fn-dep woven with its real body may itself reference an ADT — the whole +/// fn-dep woven with its body may itself reference an ADT — the whole /// dependency class must resolve, not just the checked item). The roots are /// collected from each referrer's signature types (param + return), contract /// clauses (`req`/`ens`/`dec`), and body; the closure then follows the field types @@ -4698,7 +4693,7 @@ fn collect_type_adt_refs( } // Basis Stage 4 (`.design/basis/04-collections.md`): a bounded `Vec` // recurses into its element type so a `Vec` reaches `Account` - // (the element-invariant ADT ref), exactly as `Box` reaches `List`. + // (the element-invariant ADT ref), as `Box` reaches `List`. thermite_syntax::Type::Box(inner) | thermite_syntax::Type::Slice(inner) | thermite_syntax::Type::Vec(inner) => { @@ -4713,7 +4708,7 @@ fn collect_type_adt_refs( // Cluster C7 (`.design/basis/09-option-result.md` REQ-2): the built-in // `Option` / `Result` recurse into their type argument(s) so a // `Result` reaches the in-file error enum `ParseErr` (the - // `E` parameter is an ordinary user ADT), exactly as `Box` reaches + // `E` parameter is an ordinary user ADT), as `Box` reaches // `List`. `Option`/`Result` themselves are built-ins, never an in-file ADT. thermite_syntax::Type::Option(inner) => { collect_type_adt_refs(inner, adt_decls, out); @@ -4725,7 +4720,7 @@ fn collect_type_adt_refs( // Cluster C12 (`.design/basis/13-map.md` REQ-5): a `Map` reaches an // in-file ADT in either type argument (a `Map` reaches // `Account` — the #68 ADT weave so the value's decl is woven into the - // per-item subprogram), so both the key and value are recursed, exactly as + // per-item subprogram), so both the key and value are recursed, as // `Result`'s two arguments. `Map` itself is a built-in, never an in-file ADT. thermite_syntax::Type::Map(k, v) => { collect_type_adt_refs(k, adt_decls, out); @@ -5199,7 +5194,7 @@ fn run_verus( // belonging to `is_owner` (certified L3) and concluded the L3 refusal path had // a hole; it did not. A harness names what it checks. // - // The sub-program is deliberately not a parameter: taking the label from it + // The sub-program is not a parameter: taking the label from it // is what went wrong, and a harness that cannot see the woven set cannot name // one of its members by accident. // @@ -5586,7 +5581,7 @@ fn ladder_for_timeout( // REQ-2(c): fold the engine's named trust base into a fast-`unknown` degrade // reason so the auditor sees which engine's base was attempted before the // degrade. This enriches only the REQ-3.1 incompleteness-`unknown` path (a - // genuine `Timeout` keeps its shipped profile-derived reject below); it is + // `Timeout` keeps its shipped profile-derived reject below); it is // oracle-free (the degrade reason is not in the cert oracle). Per-obligation // attribution as a cert field is REQ-4, increment (iii). let verdict = match verdict { @@ -5602,12 +5597,12 @@ fn ladder_for_timeout( // The degrade `reason` carried onto a lower rung (REQ-4) on the `Unknown` // (timeout / fast-unknown) edge prefers the assembled `l3_cert`'s reject (the // `Certificate::timeout` `RejectReason`) so the existing `VerusTimeout` reason - // text is preserved byte-identically for the genuine-timeout case. + // text is preserved byte-identically for the timeout case. let timeout_reason = l3_cert.reject.clone(); let proved_cert = l3_cert.clone(); let cx_cert = l3_cert; let l3 = crate::engine::verdict_ladder_action(&verdict, obligation.role, proved_cert, cx_cert); - // Preserve the shipped `VerusTimeout` reason text on a genuine timeout (REQ-4 + // Preserve the shipped `VerusTimeout` reason text on a timeout (REQ-4 // byte-identity): `verdict_ladder_action` synthesizes a generic reason, but the // assembled `Certificate::timeout` reject carries the profile-derived detail — // splice it back so the degrade reason on a timeout is unchanged. @@ -5615,7 +5610,7 @@ fn ladder_for_timeout( (crate::degrade::L3Verdict::Timeout { reason: generic }, Some(reject)) if reject.cause == "VerusTimeout" => { - // A genuine timeout: keep the shipped profile-derived reject text. + // A timeout: keep the shipped profile-derived reject text. let _ = generic; crate::degrade::L3Verdict::Timeout { reason: reject } } @@ -5664,7 +5659,7 @@ fn ladder_for_timeout( /// Score the frozen mutant set of `f` against its own (unchanged) contract (#12 /// §7 step 4; `.design/forge/mutation-scoring.md` REQ-3/REQ-4/REQ-5/REQ-7). -/// Called from the per-item L3 path only after `f`'s real body proved L3 (the +/// Called from the per-item L3 path only after `f`'s body proved L3 (the /// caller gates on `cert.level == L3 && reject.is_none()`). /// /// For each mutant (`mutation::generate`, the frozen + ordered + capped set): @@ -5712,7 +5707,7 @@ fn mutation_score( for mutant in mutants { // Keep the mutant's `FnItem` so a survivor can be equivalence-checked - // against the real body (#101); clone what the obligation needs before + // against the body (#101); clone what the obligation needs before // moving the item into the sub-program. let mutant_item = mutant.item.clone(); let item = Item::Fn(mutant.item); @@ -5729,7 +5724,7 @@ fn mutation_score( Err(_) => continue, }; - // Content-address the mutant exactly as the L3 path does (#8). A mutant's + // Content-address the mutant as the L3 path does (#8). A mutant's // verdict is a deterministic function of its lowered source + seed + // versions, so it caches like any item. let key = cache::cache_key(&lowered, seed, verus_version, THERMITE_VERSION); @@ -5757,7 +5752,7 @@ fn mutation_score( // The mutant survived (verus proved it against the unchanged contract). // Issue the per-survivor equivalence query (#101 REQ-1; #269 REQ-7): is the - // mutant body observably equal to the real body under `f`'s `req`, for all + // mutant body observably equal to the body under `f`'s `req`, for all // inputs (modulo callee contracts when the body is call-bearing — the same // `fn_deps` closure woven above)? A verified query is a proof of // equivalence → the survivor is a true equivalent mutant (not contract @@ -5920,8 +5915,8 @@ fn equivalence_proves_equal( /// call-bearing harness's `eq`). Every other outcome keeps the survivor counted; /// the variants distinguish "the prover found a distinguishing input / timed out" /// (`NotProved`) from "the probe could not even ask the question" (`Unsupported`, -/// carrying the structured reason — REQ-9, so an operator can tell a genuine -/// contract weakness from an out-of-scope obligation shape, R-HONEST-3). +/// carrying the structured reason — REQ-9, so an operator can tell a contract +/// weakness from an out-of-scope obligation shape, R-HONEST-3). #[derive(Debug, Clone)] enum EquivOutcome { /// Verus proved equivalence (modulo callee contracts for a call-bearing body) @@ -5937,7 +5932,7 @@ enum EquivOutcome { /// Run the #14 §7 step-5 strengthening probe for `f` /// (`.design/forge/strengthening-probes.md` REQ-2/REQ-3/REQ-4). Called from the -/// per-item L3 path only after `f`'s real body proved L3 and its mutant set met +/// per-item L3 path only after `f`'s body proved L3 and its mutant set met /// the floor (the caller gates on `cert.level == L3 && reject.is_none()` + a /// produced `MutationScore`, REQ-5). It delegates the candidate template + /// verify/filter pipeline to `strengthen::probe`, threading two verify closures @@ -5947,7 +5942,7 @@ enum EquivOutcome { /// unchanged, `strengthen::candidate_fn`), build the same per-item sub-program /// (`item_subprogram`), lower (`thermite_lower::lower`), content-address (the #8 /// cache), and `run_verus`. Returns `Ok(true)` iff verus proved the candidate -/// against the real body (the §7 "proves with no body change"); `Ok(false)` on a +/// against the body (the §7 "proves with no body change"); `Ok(false)` on a /// non-`Proved` outcome or an un-lowerable woven fn (parallel to #12's drop), and /// `Err` on an environment failure (R-CODE-4). /// - `verify_survivor` — verify the candidate `ens` against the survivor body (the @@ -6023,7 +6018,7 @@ fn strengthen_certificate( f, spec_items, score, - // verify_body: the candidate `ens` over the real body. + // verify_body: the candidate `ens` over the body. |woven| verify_woven(woven), // verify_survivor: the candidate `ens` over the survivor body. If the // survivor body could not be resolved (no recorded survivor), the candidate @@ -6059,8 +6054,8 @@ mod tests { use crate::manifest::ObligationStatus; // REQ-6 / AC-10 (increment 2d, anti-Goodhart defense (b)): the L3 re-elaboration - // mutation battery reuses the FROZEN mutation operator catalogue - // `mutation::generate` — the catalogue is SHARED, not forked. This test pins that + // mutation battery reuses the frozen mutation operator catalogue + // `mutation::generate` — the catalogue is shared, not forked. This test pins that // contract: the mutant set the re-elaboration seam (`reelaboration_mutants`, the // set `lean_mutation_score` re-elaborates per mutant) scores is byte-for-byte // `mutation::generate`'s — same families, same order, same descriptions, same @@ -6087,7 +6082,7 @@ fn to_1based(x: u32) -> u32 }) .expect("fixture has fn to_1based"); - // The re-elaboration battery's catalogue IS `mutation::generate` (the SHARED + // The re-elaboration battery's catalogue IS `mutation::generate` (the shared // frozen set), not a fork. let shared = reelaboration_mutants(&f, &parsed.program.items); let frozen = crate::mutation::generate(&f, 0, &parsed.program.items); @@ -6429,7 +6424,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost // // These exercise the check.rs glue HERMETICALLY (no lake / no verus): they feed // `apply_lemma_library` a synthesized settled cert collection + a parsed program and - // assert the AC-13 transforms, exactly as the forge/Lean path applies them after the + // assert the AC-13 transforms, as the forge/Lean path applies them after the // lemma discharge pass. The live end-to-end path is covered by the lake-guarded // engine::tests; the pure resolution logic by lemma_library::tests. @@ -6453,7 +6448,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost lemma user(n: u32) req n > 0 ens n >= 1 proof { simp [melems_cons]; omega }"; let parsed = thermite_syntax::parse(src); assert!(parsed.is_clean(), "fixture parses: {:?}", parsed.errors); - // `melems_cons` did NOT certify; `user` carries some placeholder cert. + // `melems_cons` did not certify; `user` carries some placeholder cert. let certs = vec![ Certificate::rejected( "melems_cons".to_string(), @@ -6532,13 +6527,10 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost // fragment-support check, so the migration is unit-testable without z3. // ─────────────────────────────────────────────────────────────────────────── - /// REQ-8 / AC-9: a reconstruction-supported (arith/cmp) `@bv` clause migrates its - /// `trust:` to the kernel-checked base while a bitwise (xor) clause on the SAME item - /// stays solver-trusted — the mix64 split, the actual trust flip. Default-on: no flag - /// gates the migration. Same rung (the obligation is `Proved` either way); only the - /// trust base moves. + /// REQ-8 / AC-9: arithmetic and bitwise `@bv` clauses both migrate to the + /// literal-`BitVec N` kernel-checked trust base. #[test] - fn req8_supported_clause_migrates_to_kernel_checked_bitwise_stays_solver() { + fn req8_arithmetic_and_bitwise_clauses_migrate_to_kernel_checked() { use thermite_syntax::{BinOp, BvTag, BvWidth, Expr}; fn var(s: &str) -> Expr { Expr::Path(vec![s.to_string()]) @@ -6557,7 +6549,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost }; let attr = bv_attribution(); - // mix64::ens#0 — `a + b == b + a` (wraparound-add commutativity): SUPPORTED. + // mix64::ens#0 — `a + b == b + a` (wraparound-add commutativity): supported. let add = bin( BinOp::Eq, bin(BinOp::Add, var("a"), var("b")), @@ -6569,15 +6561,18 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost "the arith clause's trust migrates to the kernel-checked base (REQ-8)" ); assert!( - supported.trust.iter().any(|t| t.contains("BvModel")), - "the migrated trust cites the BvModel faithfulness metatheorem" + supported + .trust + .iter() + .any(|t| t.contains("literal BitVec N")), + "the migrated trust names literal BitVec semantics" ); assert!( !supported.trust.iter().any(|t| t.contains("Z3 QF_BV")), "Z3 is no longer load-bearing for the reconstruction-supported clause" ); - // mix64::ens#1 — `a ^ b ^ b == a` (xor self-inverse): UNSUPPORTED, stays solver. + // mix64::ens#1 — `a ^ b ^ b == a` (xor self-inverse): supported literally. let xor = bin( BinOp::Eq, bin( @@ -6587,29 +6582,29 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost ), var("a"), ); - let solver = bv_proved_obl("mix64", 1, &tag, &xor, &attr, None); + let bitwise = bv_proved_obl("mix64", 1, &tag, &xor, &attr, None); assert!( - !crate::engine::trust_is_kernel_checked(&solver.trust), - "the bitwise clause stays solver-trusted (F-J — the exporter refuses xor)" + crate::engine::trust_is_kernel_checked(&bitwise.trust), + "the bitwise clause migrates to the literal BitVec kernel path" ); assert!( - solver.trust.iter().any(|t| t.contains("Z3 QF_BV")), - "the unsupported clause names the Z3 QF_BV solver base, as today" + !bitwise.trust.iter().any(|t| t.contains("Z3 QF_BV")), + "the bitwise clause no longer names the solver base" ); // Both invariants hold across the split: the engine tag stays `bitvector` (only the // orthogonal trust axis moved) and Lock 1's shadow flag rides both obligations. assert_eq!(supported.engine.as_deref(), Some("bitvector")); - assert_eq!(solver.engine.as_deref(), Some("bitvector")); - assert!(supported.bv_shadow.is_some() && solver.bv_shadow.is_some()); + assert_eq!(bitwise.engine.as_deref(), Some("bitvector")); + assert!(supported.bv_shadow.is_some() && bitwise.bv_shadow.is_some()); } // ─────────────────────────────────────────────────────────────────────────── // REQ-4 / AC-5 (stage-3 lock 2 — bv-semantics mutation). The bv-semantics mutation - // battery reuses the FROZEN catalogue UNCHANGED; only the kill check swaps to a + // battery reuses the frozen catalogue unchanged; only the kill check swaps to a // WRAP-AWARE discharge at the tag width. These tests need the `bv` parse-gate (to // build a `@bv`-tagged clause) and z3 (the live QF_BV / QF_NRA queries); a shard - // without z3 SKIPS, mirroring `bitvector.rs`'s live tests. + // without z3 skips, mirroring `bitvector.rs`'s live tests. // ─────────────────────────────────────────────────────────────────────────── /// The single `@bv`-tagged `fn` of the AC-5 fixture (the `bv_wrap_mutation.th` @@ -6631,9 +6626,9 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost } /// AC-5: a wrap-exploiting mutant is killed by the bv-semantics mutation run AT WIDTH - /// while the SAME mutant survives the unbounded check. The frozen off-by-one mutator + /// while the same mutant survives the unbounded check. The frozen off-by-one mutator /// turns the body `x + 0`'s literal `0` into `1`, so its grounded clause is - /// `x + 1 >= x` — valid over unbounded integers (it SURVIVES the nlsat unbounded + /// `x + 1 >= x` — valid over unbounded integers (it survives the nlsat unbounded /// route) but false over QF_BV64 at `x = 2^64 - 1` (the wrap-aware kill finds a /// bit-level counterexample). The contrast is the lock 2 invariant: the kill check is /// width-aware, not unbounded. @@ -6650,7 +6645,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost } let f = parse_succ_ge(); - // The frozen catalogue (UNCHANGED) produces the wrap-exploiting mutant: the + // The frozen catalogue (unchanged) produces the wrap-exploiting mutant: the // off-by-one `0->1` on the body literal, giving the mutant body `x + 1`. let mutants = crate::mutation::generate(&f, 0, &[]); let wrap = mutants @@ -6687,7 +6682,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost // (2) UNBOUNDED survival: the same grounded clause `x + 1 >= x` is valid over // unbounded integers, so the nlsat real-relaxation route (the codebase's - // unbounded path) PROVES it → the mutant SURVIVES the unbounded check. + // unbounded path) proves it → the mutant survives the unbounded check. let synth = thermite_syntax::FnItem { contract: thermite_syntax::Contract { ens: vec![thermite_syntax::Clause { @@ -6714,7 +6709,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost ); } - /// AC-5: the bv-semantics mutation RUN (`bv_mutation_score`) kills the wrap-exploiting + /// AC-5: the bv-semantics mutation run (`bv_mutation_score`) kills the wrap-exploiting /// mutant — its certificate carries a non-trivial kill ratio with the off-by-one /// `x + 1` mutant among the killed (and the early-return `0` mutant too). The /// survivors are the body-equivalent mutants (`return x`, `x - 0`), which the bv @@ -6755,7 +6750,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost score.meets_floor(crate::mutation::MUTATION_FLOOR), "with equivalents excluded the kill ratio (2/2) meets the floor: {score:?}" ); - // The first survivor is a body-equivalent mutant, NOT the wrap-exploiting one + // The first survivor is a body-equivalent mutant, not the wrap-exploiting one // (which was killed) — the wrap mutant never reaches the survivor slot. let survivor = score.survivor.as_deref().unwrap_or(""); assert!( @@ -6765,7 +6760,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost } /// REQ-4 regression: a `@bv` clause closed over the parameters (no `result`) is - /// invariant under a body mutation, so it is NOT a body-constraining discriminator — + /// invariant under a body mutation, so it is not a body-constraining discriminator — /// `bv_mutation_score` returns `None` and the cert keeps its forward-declared score. /// This is why the AC-2 `mix64` golden (whose `@bv` clauses are `a + b == b + a` and /// `a ^ b ^ b == a`) is unperturbed by lock 2. @@ -6795,8 +6790,8 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost // ─────────────────────────────────────────────────────────────────────────── // REQ-5 / AC-6 (stage-3 lock 3 — the `nowrap` side obligation). A `@bvN(nowrap)` - // clause additionally discharges a no-overflow side obligation IN-CAGE; its verdict - // rides `bv_shadow.nowrap_obligation`. A body that can overflow FAILS the obligation + // clause additionally discharges a no-overflow side obligation in-cage; its verdict + // rides `bv_shadow.nowrap_obligation`. A body that can overflow fails the obligation // with a concrete overflowing input (the cert is rejected); a body that cannot // overflow records the discharged verdict and certifies. These drive the full cert // through `bv_fn_cert`. @@ -6829,7 +6824,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost bv_fn_cert(&bv, &nlsat, &f, &base, &[]) } - /// AC-6: a `@bv64(nowrap)` clause whose body can overflow FAILS its side obligation + /// AC-6: a `@bv64(nowrap)` clause whose body can overflow fails its side obligation /// with a concrete overflowing input, and the verdict is recorded in /// `bv_shadow.nowrap_obligation`. The clause `result == a + b` is machine-valid at /// width (result is DEFINED as `a + b` mod 2^64, so the equality is tautological), @@ -6848,7 +6843,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost fx pure { a + b }", "add_nowrap", ); - // A witnessed nowrap overflow must NOT certify (the promise is violated). + // A witnessed nowrap overflow must not certify (the promise is violated). let reject = cert .reject .as_ref() @@ -6879,7 +6874,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost } /// AC-6 (the holds side): a `@bv64(nowrap)` clause whose body carries no wrap-prone - /// arithmetic discharges the no-overflow obligation VACUOUSLY (no operation can + /// arithmetic discharges the no-overflow obligation vacuously (no operation can /// overflow), certifies at L4, and records the discharged verdict in /// `bv_shadow.nowrap_obligation`. `result == a` over body `{ a }` needs no z3 (the /// obligation is vacuous — there is nothing that could overflow). @@ -6920,7 +6915,7 @@ note: Cost * Instantiations: 150 (Instantiated 10 times - 71% of the total, cost ); } - /// A bare `@bv64` (NO `nowrap`) clause runs NO side obligation, so its + /// A bare `@bv64` (no `nowrap`) clause runs no side obligation, so its /// `bv_shadow.nowrap_obligation` stays `None` — the slot is filled only for the /// `nowrap` spelling (REQ-5). Guards against the lock firing on every tagged clause. #[cfg(feature = "bv")] diff --git a/forge/src/cli.rs b/forge/src/cli.rs index cdae447eb..17d83ce56 100644 --- a/forge/src/cli.rs +++ b/forge/src/cli.rs @@ -294,7 +294,7 @@ enum Command { /// emits it as the stable `--json` document or a human summary. The default-config /// path is the reproducible trust statement (OQ-3). `--meaning` (REQ-6c, increment /// 2d) additionally prints each `fn`'s unfolded definition tower, the pinned hash, - /// and the Q2 budget status — a READ-ONLY companion that gates nothing (the budget + /// and the Q2 budget status — a READ-only companion that gates nothing (the budget /// gate is certify-time, in `forge check`; #274 "audit gates nothing"). Audit { file: PathBuf, @@ -368,7 +368,7 @@ enum Command { /// `inv`/`dec` clause it discharges the per-clause Z3 equivalence obligation /// `P_production <==> P_reference` (the production lowering vs the independent /// `thermite-tv` reference encoder) through verus, reporting each clause - /// faithful or divergent (a real lowering-fidelity finding). `--generated [N]` + /// faithful or divergent (a lowering-fidelity finding). `--generated [N]` /// also runs the off-corpus generated clause space (REQ-3, the corpus-bound /// escape; default N = [`TV_GENERATED_DEFAULT_N`]). Tv { @@ -410,7 +410,7 @@ enum Command { /// admission classifier (`thermite_spec::classifier`) byte-equal to the Lean kernel /// `Thermite.Strat.Cls.admitted` (via `lake env lean --run`); any verdict /// disagreement is a verification-failure exit, and the unknown-on-admitted tripwire - /// escalates as classifier-suspect. lake-absent is an honest skip (exit 0). + /// escalates as classifier-suspect. lake-absent is a skip (exit 0). StratTv { json: bool, /// `--generated [N]` — the formula count (default [`crate::strat_tv::STRAT_TV_DEFAULT_N`]). @@ -437,11 +437,11 @@ enum Command { seed: Option, }, /// `forge g2-gate --axiom-probe <0|1> --doc-drift <0|1> --differential <0|1> - /// --two-phase <0|1> [--json]` — THE G2 GATE (`.design/stage2-stratified-cage.md` + /// --two-phase <0|1> [--json]` — the G2 gate (`.design/stage2-stratified-cage.md` /// REQ-9 / AC-9). The runtime enforcer `make audit` drives after running the four /// stage-2 checks: it combines their green/red outcomes through - /// [`thermite_tv::strat_two_phase::g2_flip_permitted`], prints the EFFECTIVE trust - /// profile (the proven scoped form iff the declaration `G2_FLIPPED` is on AND all four + /// [`thermite_tv::strat_two_phase::g2_flip_permitted`], prints the effective trust + /// profile (the proven scoped form iff the declaration `G2_FLIPPED` is on and all four /// green, else the conservative `UNPROVEN` form), and EXITS NONZERO when G2 is declared /// while any of the four is red — the mechanical block of the trust flip. G2Gate { @@ -475,7 +475,7 @@ enum Command { /// pure view over the shipped `check::check_file` cert collection + the re-parsed /// AST contract (given/want); adds no verification. An optional second positional /// restricts the render to one item. Holes (`?N`) are increment (iii), not in - /// this verb yet. `--proof` switches to the forge-tier PROOF VIEW + /// this verb yet. `--proof` switches to the forge-tier proof view /// (`.design/stage1-forge-tier.md` REQ-7): forge-routed goals (`lemma` / `proof /// for f`) rendered with their hypotheses in scope + open `?pN` proof holes. Goal { @@ -504,9 +504,9 @@ enum Command { }, /// `forge edit --restratify [--json]` — the restratification rewrite, end to end /// (`.design/stage2-stratified-cage.md` REQ-7 / AC-7). Runs the §6 kv-alternation - /// worked example through `restrat`: shows the original φ REJECTED (the `Key ⇄ Value` - /// cycle), the rewritten φ' = `A ∧ p` ADMITTED, the `Side(φ', φ) = p ⇒ B` obligation - /// ADMITTED, discharges `Side` in-cage, and certifies φ. R-SIDE-1: certification is + /// worked example through `restrat`: shows the original φ rejected (the `Key ⇄ Value` + /// cycle), the rewritten φ' = `A ∧ p` admitted, the `Side(φ', φ) = p ⇒ B` obligation + /// admitted, discharges `Side` in-cage, and certifies φ. R-side-1: certification is /// WITHHELD when `Side` is undischarged (a tested code path, mirroring the Lean /// `restrat_conservative` / `PinRestratDropSide`). Restratify { json: bool }, @@ -527,7 +527,7 @@ enum Command { /// exporter (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8). With a ``, /// emits a `(P_prod) ⟺ (P_ref)` Lean theorem (discharged `by smt`, then a /// `#print axioms` probe) for every renderable contract `ens` clause — QF_LIA for - /// an untagged clause, QF_BV (the bounded-integer machine-model) for a `@bvN` clause + /// an untagged clause, literal `BitVec N` QF_BV for a `@bvN` clause /// in a `bv`-feature build. Without a ``, emits the canonical /// reconstruction-supported demo batch (the source of `lean/Thermite/SmtExport.lean`). /// `--out ` writes the Lean file there (else stdout). @@ -726,14 +726,14 @@ fn parse_args(args: &[String]) -> Result { "--json" => json = true, // `--meaning` (REQ-6c, increment 2d): the read-only definition-tower // companion — print each fn's unfolded meaning tower + the pinned - // hash + the Q2 budget status. It GATES NOTHING (the budget gate is + // hash + the Q2 budget status. It gates nothing (the budget gate is // certify-time, in `forge check`, not here — #274 "audit gates // nothing"): `forge audit --meaning` never changes the exit code. "--meaning" => meaning = true, // `--metrics` (umbrella REQ-7 / AC-12): the read-only §6 metrics // dashboard companion — the cage-vs-forge share by routing reason, the // seven-verdict counts, and the TV phase split, projected from the - // certificate telemetry + a contract-TV run. It GATES NOTHING (#274 + // certificate telemetry + a contract-TV run. It gates nothing (#274 // "audit gates nothing"): `forge audit --metrics` never changes the // exit code, and its output is not part of the certificate oracle. "--metrics" => metrics = true, @@ -1166,7 +1166,7 @@ fn parse_args(args: &[String]) -> Result { // `forge g2-gate --axiom-probe <0|1> --doc-drift <0|1> --differential <0|1> // --two-phase <0|1> [--json]` (`.design/stage2-stratified-cage.md` REQ-9 / // AC-9). Each of the four flags takes a 0/1 (or pass/fail / true/false) - // verdict; all four are REQUIRED — the gate cannot honestly evaluate a check it + // verdict; all four are REQUIRED — the gate cannot evaluate a check it // was not told about (a missing verdict is a usage error, never an optimistic // green). let mut json = false; @@ -1584,7 +1584,7 @@ fn dispatch(args: &[String]) -> Result { /// `check::check_file` cert collection — given/want from the re-parsed contract, /// per-obligation status with counterexamples from the cert's `obligations`. /// -/// Exit code: a render is a successful query (SUCCESS) — the verdict (discharged / +/// Exit code: a render is a successful query (success) — the verdict (discharged / /// open obligation) lives in the rendered goal state, not in the exit code (the /// goal REPL is a view, not a gate). An environment failure (verus absent, file /// unreadable, parse failure) propagates as a `ForgeError`. @@ -1605,7 +1605,7 @@ fn run_goal(file: &Path, item: Option<&str>, proof: bool) -> Result) -> Result { let rendered = goal_repl::render_battery(file, item)?; @@ -1619,7 +1619,7 @@ fn run_battery(file: &Path, item: Option<&str>) -> Result /// the addressed node's span in the file, re-emits, re-checks the affected item, /// and prints the new goal state. /// -/// Exit code: a successful edit + re-check is SUCCESS (the new goal state is the +/// Exit code: a successful edit + re-check is success (the new goal state is the /// output). A bad/unresolvable address, a re-parse failure after the splice, or an /// IO / environment failure propagates as a `ForgeError` (the environment exit /// code; never a panic — REQ-7). @@ -1633,7 +1633,7 @@ fn run_edit(file: &Path, addr: &str, replace: &str) -> Result Result /// Run `forge smt-export`: the automated Rust→Lean obligation exporter /// (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8). With a `file`, parses it and /// exports a `(P_prod) ⟺ (P_ref)` `smt`-discharged Lean theorem per renderable -/// contract `ens` clause (QF_LIA for an untagged clause; QF_BV bounded-integer model +/// contract `ens` clause (QF_LIA for an untagged clause; literal `BitVec N` QF_BV /// for a `@bvN` clause in a `bv` build); a non-renderable clause is reported as a /// named skip on stderr, never silently dropped. Without a `file`, emits the canonical /// reconstruction-supported demo batch (the source of `lean/Thermite/SmtExport.lean`). @@ -1699,17 +1699,17 @@ fn run_smt_export(file: Option<&Path>, out: Option<&Path>) -> Result Result { use thermite_spec::classifier::{classify, to_wire, Verdict}; @@ -1719,7 +1719,7 @@ fn run_restratify(json: bool) -> Result { let orig_verdict = classify(&phi); // Discharge `Side` in-cage (it is admitted — see below), certifying φ. let cert = certify(&phi, true); - // Cross-check the withheld path so the rendered report can attest R-SIDE-1 honestly. + // Cross-check the withheld path so the rendered report can attest R-side-1. let withheld = certify(&phi, false); let result = match &cert { @@ -1834,7 +1834,7 @@ fn run_check( }, )?, // `--engine lean` / `--engine auto`: the proof-backends increment-(iii) Lean - // surface (OQ-1). A genuine engine disagreement (Verus Proven ⊕ Lean Refuted, + // surface (OQ-1). A engine disagreement (Verus Proven ⊕ Lean Refuted, // or vice versa, on the same obligation) halts as a `ForgeError::SoundnessAlarm`, // never resolved by preference (REQ-5). (CheckLevel::L3, sel) => check::check_file_with_engine( @@ -1906,7 +1906,7 @@ fn run_audit( metrics: bool, ) -> Result { // Parse the file once for the boundary contracts' enforced req/ens/fx (the - // §9 per-function contracts the TCB enumerates) AND to decide the route below. + // §9 per-function contracts the TCB enumerates) and to decide the route below. // A pure read of the parsed AST (deterministic, R-CODE-5), never a verification. let src = std::fs::read_to_string(file).map_err(|e| ForgeError::Io { path: file.display().to_string(), @@ -1955,8 +1955,8 @@ fn run_audit( } // REQ-6c (increment 2d): the `--meaning` read-only companion — print each fn's - // unfolded definition tower + the pinned hash + the Q2 budget status. It GATES - // NOTHING (#274 "audit gates nothing"; the budget gate is certify-time in + // unfolded definition tower + the pinned hash + the Q2 budget status. It gates + // nothing (#274 "audit gates nothing"; the budget gate is certify-time in // `forge check`): the exit code below is the manifest headline, unchanged by this // print. In `--json` mode it goes to stderr so the stdout JSON stays a valid v1 // document; in human mode it appends to the stdout report. @@ -1972,8 +1972,8 @@ fn run_audit( // Umbrella REQ-7 / AC-12: the `--metrics` read-only §6 dashboard companion — the // cage-vs-forge share BY routing reason, the seven-verdict counts, and the TV phase // split, projected from the certificate per-clause telemetry + a contract-TV run over - // the same file. It GATES NOTHING (#274 "audit gates nothing"): the exit code below is - // the manifest headline, unchanged by this print, and the dashboard is NOT part of the + // the same file. It gates nothing (#274 "audit gates nothing"): the exit code below is + // the manifest headline, unchanged by this print, and the dashboard is not part of the // certificate oracle. In `--json` mode it goes to stderr so the stdout JSON stays a // valid v1 document; in human mode it appends to the stdout report. if metrics { @@ -2017,7 +2017,7 @@ fn run_audit( /// Render the `forge audit --meaning` read-only companion section (REQ-6c, increment /// 2d): each `fn`'s unfolded definition tower + the pinned hash + the Q2 budget /// status, in source order. A pure projection of the parsed AST + source -/// (`meaning::build_tower`) — it re-runs no prover and GATES NOTHING (the budget gate +/// (`meaning::build_tower`) — it re-runs no prover and gates nothing (the budget gate /// is certify-time, in `forge check`; #274 "audit gates nothing"). A `spec fn` / ADT /// has no contract to root a tower, so only `fn` items are shown. fn render_meaning(program: &thermite_syntax::Program, src: &str) -> String { @@ -2044,7 +2044,7 @@ fn render_meaning(program: &thermite_syntax::Program, src: &str) -> String { /// for timeout items only, report the rest), then renders the per-item repair /// report. A one-shot, deterministic, re-runnable pass (OQ-4 reading (a)). /// -/// The exit code (REQ-5 parallel): SUCCESS iff every repaired item upgraded to L3 +/// The exit code (REQ-5 parallel): success iff every repaired item upgraded to L3 /// and no item remains a hard fail (a no-op corpus is vacuously success); else the /// verification-failure code (a still-sub-L3 or not-repairable item means the /// project does not fully certify). An environment failure (verus absent / @@ -2063,7 +2063,7 @@ fn run_repair(file: &Path, item: Option<&str>, json: bool) -> Result, json: bool) -> Result) -> Result { @@ -2279,7 +2279,7 @@ fn run_strat_tv(json: bool, generated: usize, seed: Option) -> Result { @@ -2330,7 +2330,7 @@ fn run_strat_tv(json: bool, generated: usize, seed: Option) -> Result) -> Result Result { ); } - // Any divergent body is a real body-lowering state-transformation finding → + // Any divergent body is a body-lowering state-transformation finding → // verification-failure exit. A clean audit (Faithful / Skipped / // Unverifiable only) exits 0 (the same convention `forge exec-tv` uses). if counts.divergent == 0 { @@ -3362,7 +3362,7 @@ mod tests { ); // A stray positional is a Usage error. assert!(parse_args(&argv(&["edit", "--restratify", "f.th"])).is_err()); - // `--json` WITHOUT `--restratify` stays an unknown flag for plain `edit`. + // `--json` without `--restratify` stays an unknown flag for plain `edit`. assert!(parse_args(&argv(&["edit", "f.th", "x", "--replace", "c", "--json"])).is_err()); } diff --git a/forge/src/closure.rs b/forge/src/closure.rs index e58422c6c..1cb1ad7f4 100644 --- a/forge/src/closure.rs +++ b/forge/src/closure.rs @@ -65,7 +65,7 @@ //! reachable_in_file_fns` reuses the same walker (a new `CallGraph::reachable_fns` //! DFS sibling of `reach_crossing`) to return every in-file `Item::Fn` a caller //! transitively references, which `check::item_subprogram` weaves into the -//! caller's §5.3 sub-program (regular fns with their real body, boundary/slag fns +//! caller's §5.3 sub-program (regular fns with their body, boundary/slag fns //! as `#[verifier::external_body]` signatures). No walker is duplicated. //! //! ## Cluster C10 — ergonomics ripple (`.design/basis/11-ergonomics.md`, #112) @@ -176,7 +176,7 @@ impl CallGraph { /// referenced from `start`'s body, excluding `start` itself: a cycle-safe, /// bounded DFS over the same out-edges [`reach_crossing`] walks (#52 /// composition weaving). Used by `check::item_subprogram` to weave a caller's - /// regular-fn dependencies (real body) and boundary/slag dependencies + /// regular-fn dependencies (body) and boundary/slag dependencies /// (external_body signature) into its §5.3 sub-program so `lower`/`verus` /// resolve every referenced callee. /// @@ -282,7 +282,7 @@ pub fn classify(program: &Program) -> BTreeMap { /// weaving (`.design/lower/boundary-composition.md` REQ-2, crosslink #52). /// /// `check::item_subprogram` consumes this to build a caller `f`'s isolated §5.3 -/// sub-program: every regular reachable fn is woven with its real body (proved), +/// sub-program: every regular reachable fn is woven with its body (proved), /// and every `#[boundary]`/`#[slag]` reachable fn is woven as a /// `#[verifier::external_body]` signature (`thermite_lower::lower`), so `verus` /// resolves the foreign callee and `f` proves through its contract (was an @@ -627,7 +627,7 @@ fn caller(x: u32) -> u32 req x < 100 ens result == x fx pure { ext_id(x) }"; ); } - // #52 REQ-2 (transitive): h's sub-program weaves both g (real body) and + // #52 REQ-2 (transitive): h's sub-program weaves both g (body) and // ext_id (external_body); both are reachable `fn`s. #[test] fn reachable_fns_is_transitive_through_an_intermediary() { diff --git a/forge/src/contract_tv.rs b/forge/src/contract_tv.rs index 93fa03d5b..70f77d230 100644 --- a/forge/src/contract_tv.rs +++ b/forge/src/contract_tv.rs @@ -198,7 +198,7 @@ fn tv_fn( // path emits). Slice params are bound view-consistently as `&[elem]` (#149) and // threaded as production's `slices` (per clause, in `tv_clause`), so production // emits `xs@` for every slice use (bare `spec_sum(xs@)` and indexed - // `xs@.subrange(..)`), mirroring the real fn signature path, and the reference + // `xs@.subrange(..)`), mirroring the fn signature path, and the reference // emits the matching `xs@`; both columns typecheck under the one binding. let nat_fns = nat_fn_names(f); @@ -442,7 +442,7 @@ fn tv_clause( // P_production — the production lowering of this clause. The frame's // slice params (bound `&[elem]`, #149) are passed as production's `slices` so a // slice use takes its `@`-view (`spec_sum(xs@)` / `xs@.subrange(..)`), mirroring - // the real fn signature path (`tests/golden/lower/sum.verus.rs`), and typechecks + // the fn signature path (`tests/golden/lower/sum.verus.rs`), and typechecks // against the `&[elem]` binding. nat_fns drive the `as nat` coercion. let slice_params = slice_param_names(base_frame); let slices: Vec<&str> = slice_params.iter().map(String::as_str).collect(); @@ -650,7 +650,7 @@ fn touch(xs: &[u32], ys: &[u32], n: usize) -> bool // #150 gap #2: a `String`-param fn so `emit_string_wrapper` materializes the // `TString` wrapper (its `spec_len`/`spec_byte_at` spec fns) into the preamble — // the off-corpus String byte-view obligation binds `t: &TString` and dispatches -// `t.byte_at(i)`/`t.len()` to those spec fns on BOTH columns. +// `t.byte_at(i)`/`t.len()` to those spec fns on both columns. fn touch_string(t: String) -> u64 req t.len() > 0 ens result == t.byte_at(0) @@ -946,7 +946,7 @@ impl SpecType { /// typechecks: `xs@` is the `Seq` view of `xs: &[elem]`. Under a bare /// `Seq` binding, `xs@` is a type error (`Seq` has no `view`), so the /// indexed clause `acc == spec_sum(&xs[..i])` could not discharge (Unverifiable). - /// This mirrors the real fn lowering (`tests/golden/lower/sum.verus.rs`: + /// This mirrors the fn lowering (`tests/golden/lower/sum.verus.rs`: /// `fn sum(xs: &[u32])` emits `xs@` everywhere); the reference encoder then /// emits the matching `xs@` form (the param is not seq-bound), so both columns /// typecheck under one binding and Z3 proves them equivalent. @@ -1132,7 +1132,7 @@ fn discharge(program: &str, label: &str, seed: u64, rlimit: f64) -> ClauseVerdic // No `--output-json` here — verus then emits the plain-text // `verification results:: N verified, M errors` summary line that - // [`parse_results`] reads (the same form the `thermite-tv` teeth-test parses). + // [`parse_results`] reads (the same form parsed by the `thermite-tv` negative test). // The pinned `--rlimit` + `smt.random_seed` keep the discharge deterministic // (R-CODE-5), matching `forge check`'s verus invocation config. let output = Command::new("verus") @@ -1192,7 +1192,7 @@ fn discharge(program: &str, label: &str, seed: u64, rlimit: f64) -> ClauseVerdic } /// Parse the `N verified, M errors` summary line from verus output (mirrors the -/// teeth-test parser). `None` if no summary line is present. +/// negative-test parser). `None` if no summary line is present. fn parse_results(output: &str) -> Option<(u32, u32)> { let line = output .lines() @@ -1268,17 +1268,17 @@ pub fn render_report(report: &TvReport, header: &str) -> String { pub const TV_DEFAULT_SEED: u64 = DEFAULT_SOLVER_SEED; pub const TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; -// ---- the forge-level contract Divergent teeth (REQ-5; blocker #166) --------- +// ---- forge-level contract Divergent regression tests (REQ-5; blocker #166) - // -// The obligation-layer teeth (`thermite-tv/tests/teeth.rs` F1–F4) prove a wrong -// `P_production` -> a real verus error. They do not exercise the forge-level step +// The obligation-layer tests (`thermite-tv/tests/teeth.rs` F1–F4) prove a wrong +// `P_production` -> a verus error. They do not exercise the forge-level step // that maps that verus signal to a `ClauseVerdict`: `discharge`'s four-way // classification. Over the corpus/off-corpus space the faithful lowerer never // produces a Divergent, so the Divergent arm (and the Unverifiable boundary) had no // direct test coverage. This is the #166 analog of the #157 (`exec_tv`) / #189 // (`body_tv`) gap — the same parallel seam. // -// This module is the end-to-end teeth for the forge classification, mirroring +// This module tests the forge classification end to end, mirroring // `exec_tv::divergent_teeth` and `body_tv::divergent_teeth`: it builds a real // per-clause equivalence obligation, discharges it through the actual `discharge` // fn, and asserts the verdict. It covers the positive control (faithful -> @@ -1294,11 +1294,11 @@ pub const TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; // including an rlimit-exhausted run (a results line counting the exhausted obligation // as an error). That is the same #189-class bug: a solver-budget timeout fabricated // into a contract infidelity. The minimal fix added `is_rlimit_signal` + an -// rlimit-hit arm ahead of the Divergent arm. The `rlimit_signal_*` teeth pin it. +// rlimit-hit arm ahead of the Divergent arm. The `rlimit_signal_*` tests pin it. // // Test-only: no further production-logic change. `discharge`/`is_rlimit_signal` are -// private sibling fns, reachable here via `super::`. The teeth drive a real -// wrong production -> a real verus counterexample -> the real `discharge` mapping, +// private sibling fns, reachable here via `super::`. The tests drive a +// wrong production -> a verus counterexample -> the real `discharge` mapping, // never a mocked verdict. Skips with a logged message when `verus` is absent. #[cfg(test)] mod divergent_teeth { @@ -1307,7 +1307,7 @@ mod divergent_teeth { /// `true` iff a bare `verus` is spawnable (the same resolution `discharge` uses — /// `Command::new("verus")`, i.e. PATH). Skip with a logged message otherwise so - /// the teeth never silently pass when the discharge cannot reach a solver. + /// the tests do not pass without reaching a solver. fn verus_on_path() -> bool { Command::new("verus").arg("--version").output().is_ok() } @@ -1439,8 +1439,8 @@ mod divergent_teeth { /// pin this — contract_tv's first arm is `errors == 0 && status.success()` without a /// `verified >= 1` guard, so a vacuous success classifies Faithful; the no-results /// abort is the malformed-outcome the `_` arm catches.) The #166 audit - /// confirmed `discharge` already classified this arm as Unverifiable; this teeth - /// pins it so a future regression to Divergent fails. + /// confirmed `discharge` already classified this arm as Unverifiable; this test + /// prevents a regression to Divergent. #[test] fn frame_abort_classifies_unverifiable_not_divergent() { if !verus_on_path() { @@ -1532,7 +1532,7 @@ mod divergent_teeth { /// on the rlimit text; the `discharge` source then routes `errors >= 1 && rlimit_hit` /// to Unverifiable ahead of the `errors >= 1` Divergent arm. Together they pin the /// full #189-class mapping (rlimit -> Unverifiable) by inspection + execution of the - /// discriminator, as `body_tv`'s `is_rlimit_signal` unit teeth do. + /// discriminator, as `body_tv`'s `is_rlimit_signal` unit tests do. #[test] fn rlimit_output_text_is_not_a_divergence() { use crate::tv_signal::is_rlimit_signal; diff --git a/forge/src/covenant.rs b/forge/src/covenant.rs index f1a871699..7aa824b22 100644 --- a/forge/src/covenant.rs +++ b/forge/src/covenant.rs @@ -7,15 +7,15 @@ //! covenant-before-burn — which is why the record is a NON-OPTIONAL parameter of //! [`crate::engine::Engine::discharge`] (a type-level seam, not a runtime convention). //! -//! ## What the FOUNDATION ships vs what 2b builds +//! ## What the foundation ships vs what 2b builds //! -//! This increment (the foundation) introduces the record TYPE and threads it through +//! This increment (the foundation) introduces the record type and threads it through //! the `Engine::discharge` signature and every call site, so the cross-cutting signature -//! ripple happens ONCE. It does NOT build the covenant LOGIC (the `inhabit` type-check + +//! ripple happens ONCE. It does not build the covenant logic (the `inhabit` type-check + //! execute, the `falsify` generator run, the covenant-before-burn enforcement) — that is -//! increment 2b. Today the `witness` SURFACE SYNTAX is REQ-3 and not present in the +//! increment 2b. Today the `witness` surface SYNTAX is REQ-3 and not present in the //! parser, so every program legitimately carries a [`CovenantRecord::none`] — a truthful -//! "no covenant declared" record, NOT a stub: there is nothing to declare yet, and 2b +//! "no covenant declared" record, not a stub: there is nothing to declare yet, and 2b //! both adds the syntax and fills in the producing logic at the seam this record marks. //! //! The Q3 defaults (a fixed-seed `falsify 50_000` when a covenant is declared without an @@ -47,7 +47,7 @@ pub const DEFAULT_FALSIFY_SEED: u64 = 0x5EED_0000_0000_C0DE; /// covenant-before-burn — no call site changes, because the seam already threads this. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct CovenantRecord { - /// Whether a `witness` block was DECLARED for this item. `false` for every program + /// Whether a `witness` block was declared for this item. `false` for every program /// today (the surface syntax is REQ-3, not yet present), so [`CovenantRecord::none`] /// is the truthful record, not a placeholder. pub declared: bool, @@ -64,7 +64,7 @@ pub struct CovenantRecord { impl CovenantRecord { /// The truthful "no covenant declared" record (REQ-4): the record every program /// carries today, because the `witness` surface syntax (REQ-3) is not present yet. - /// NOT a stub — there is nothing to declare, so no witnesses, no falsification owed. + /// not a stub — there is nothing to declare, so no witnesses, no falsification owed. #[must_use] pub fn none() -> Self { CovenantRecord { diff --git a/forge/src/covenant_engine.rs b/forge/src/covenant_engine.rs index 6c79fb601..f16bae9be 100644 --- a/forge/src/covenant_engine.rs +++ b/forge/src/covenant_engine.rs @@ -1,7 +1,7 @@ -//! `forge/src/covenant_engine.rs` — the covenant PRODUCER + the `falsify` driver + +//! `forge/src/covenant_engine.rs` — the covenant producer + the `falsify` driver + //! the covenant-before-burn gate (REQ-4; `.design/stage1-forge-tier.md`, increment 2b). //! -//! This is the LOGIC the foundation (#20) and the 2a surface (#29) set up for. The +//! This is the logic the foundation (#20) and the 2a surface (#29) set up for. The //! foundation threaded a non-optional [`CovenantRecord`] through //! [`crate::engine::Engine::discharge`] and wired [`crate::verdict::CertVerdict:: //! CovenantRefuted`] as a `Counterexample`-class hard fail in the degrade ladder; 2a @@ -19,9 +19,9 @@ //! [`CertVerdict::CovenantRefuted`](crate::verdict::CertVerdict::CovenantRefuted) //! hard-fail material). Q3 default: a fixed-seed `falsify 50_000` when the budget is //! unstated. -//! 3. [`covenant_gate`] enforces covenant-before-burn STRUCTURALLY (R-COV-1): the burn -//! closure (the L3 proof search) is invoked ONLY when the covenant validated; a -//! refuted or malformed covenant returns WITHOUT invoking burn. This is the +//! 3. [`covenant_gate`] enforces covenant-before-burn structurally (R-COV-1): the burn +//! closure (the L3 proof search) is invoked only when the covenant validated; a +//! refuted or malformed covenant returns without invoking burn. This is the //! closure-instrumented invariant (the `degrade.rs` style), not a convention. //! //! ## Binding: a witness covenants the preceding `fn` @@ -66,10 +66,10 @@ pub struct CovenantEvidence { pub seed: u64, } -/// A malformed or absent covenant on a covenant-routed item (REQ-4): refused BEFORE +/// A malformed or absent covenant on a covenant-routed item (REQ-4): refused before /// burn, named (R-COV-1, AC-8). Distinct from a [`CovenantCounterexample`] (a /// `falsify` refutation of a well-formed covenant) — these are author errors in the -/// covenant declaration itself, surfaced loudly rather than silently dropped. +/// covenant declaration itself, surfaced rather than silently dropped. #[derive(Debug, Clone, PartialEq, Eq)] pub enum CovenantError { /// A `witness` block with no author-stated `inhabit` witness (REQ-4: a covenant @@ -99,8 +99,8 @@ pub enum CovenantError { /// The rendered offending witness tuple + the mismatch detail. detail: String, }, - /// An `inhabit` witness that does NOT satisfy `req` (REQ-4: a covenant error, - /// surfaced loudly — the author claims an inhabitant of the precondition that is + /// An `inhabit` witness that does not satisfy `req` (REQ-4: a covenant error, + /// surfaced — the author claims an inhabitant of the precondition that is /// not one). Refused before burn. WitnessRefutesReq { /// The covenanted item's name. @@ -179,7 +179,7 @@ impl CovenantError { } } -/// The result of analyzing a covenant-routed item (REQ-4), BEFORE the burn gate. The +/// The result of analyzing a covenant-routed item (REQ-4), before the burn gate. The /// pure analysis: the `inhabit` witnesses are validated against `req` and the /// `falsify` run has executed against the body → `ens`. #[derive(Debug, Clone, PartialEq, Eq)] @@ -194,7 +194,7 @@ pub enum CovenantAnalysis { }, /// A `falsify` input satisfied `req` but the body violated `ens` — /// [`CertVerdict::CovenantRefuted`](crate::verdict::CertVerdict::CovenantRefuted). - /// The burn is NOT entered (R-COV-1 / the never-degrades treatment). + /// The burn is not entered (R-COV-1 / the never-degrades treatment). Refuted { /// The concrete falsifying input + the seed. counterexample: CovenantCounterexample, @@ -207,7 +207,7 @@ pub enum CovenantAnalysis { /// The outcome of [`covenant_gate`]: the covenant analysis composed with the burn /// closure (R-COV-1). On a validated covenant the burn ran (its result `T` is carried -/// with the evidence); on a refutation/refusal the burn did NOT run. +/// with the evidence); on a refutation/refusal the burn did not run. #[derive(Debug, Clone, PartialEq, Eq)] pub enum CovenantGate { /// The covenant validated and the burn closure ran. Carries the burn result + the @@ -218,7 +218,7 @@ pub enum CovenantGate { /// The covenant evidence for the certificate. evidence: CovenantEvidence, }, - /// A `falsify` refutation — the burn did NOT run (covenant-before-burn / the + /// A `falsify` refutation — the burn did not run (covenant-before-burn / the /// never-degrades treatment, R-COV-1). Refuted { /// The concrete falsifying input + the seed. @@ -226,16 +226,16 @@ pub enum CovenantGate { /// The covenant evidence (`falsify_refuted == 1`). evidence: CovenantEvidence, }, - /// A malformed/absent covenant — the burn did NOT run; the refusal is named. + /// A malformed/absent covenant — the burn did not run; the refusal is named. Refused { /// The named covenant error. error: CovenantError, }, } -/// Enforce covenant-before-burn STRUCTURALLY (R-COV-1, AC-8): invoke the `burn` closure -/// (the L3 proof search) ONLY when the covenant validated; on a refutation or a -/// malformed/absent covenant return WITHOUT invoking it. This is the closure-instrumented +/// Enforce covenant-before-burn structurally (R-COV-1, AC-8): invoke the `burn` closure +/// (the L3 proof search) only when the covenant validated; on a refutation or a +/// malformed/absent covenant return without invoking it. This is the closure-instrumented /// invariant in the [`crate::degrade`] style — the proof-search path cannot start /// without a valid covenant record, proven by the closure never being called on the /// non-`Validated` arms (the `covenant_gate_never_burns_without_covenant` test). @@ -249,7 +249,7 @@ where { match analysis { CovenantAnalysis::Validated { record, evidence } => { - // The ONLY arm that invokes burn — the covenant is in hand (R-COV-1). + // The only arm that invokes burn — the covenant is in hand (R-COV-1). let result = burn(&record); CovenantGate::Burned { result, evidence } } @@ -347,7 +347,7 @@ fn bind_params( (ParamKind::Int(width), Value::Int(n)) => { // Width-check the witness against the parameter's integer type: a value // outside `0..=width.max` is not an inhabitant of the parameter type, so - // it is an ill-typed witness, NOT a `req`-satisfying input (without this, + // it is an ill-typed witness, not a `req`-satisfying input (without this, // an out-of-range author witness like `inhabit (4294967296)` for a `u32` // truncates and manufactures a false CovenantRefuted on a sound item). // Generated inputs are always in-range (`gen_value` caps at the max), so @@ -381,7 +381,7 @@ fn bind_params( } /// Evaluate the `inhabit` argument tuple to concrete values (REQ-4): the witness -/// expressions are evaluated in the EMPTY environment (an `inhabit` tuple is a closed +/// expressions are evaluated in the empty environment (an `inhabit` tuple is a closed /// constant tuple). A non-constant or out-of-fragment witness expression is a /// [`CovenantError::UnsupportedItem`]. fn eval_inhabit( @@ -449,7 +449,7 @@ fn gen_value(rng: &mut Rng, kind: ParamKind) -> Value { /// Analyze a covenant-routed item (REQ-4): bind the `witness` block to `fn` `f`, /// validate each `inhabit` witness against `req`, then drive the `falsify` run against -/// the executable body → `ens`. The pure analysis BEFORE the burn gate — see +/// the executable body → `ens`. The pure analysis before the burn gate — see /// [`covenant_gate`]. #[must_use] pub fn analyze_covenant(f: &FnItem, witness: &WitnessBlock) -> CovenantAnalysis { @@ -567,7 +567,7 @@ pub fn analyze_covenant(f: &FnItem, witness: &WitnessBlock) -> CovenantAnalysis let env = match bind_params(&item, &f.params, &kinds, &vals) { Ok(e) => e, // Arity/kind cannot mismatch here (we built `vals` from `kinds`), but map it - // honestly rather than unwrap. + // rather than unwrap. Err(e) => return CovenantAnalysis::Error(e), }; // Only `req`-satisfying inputs are candidates (REQ-4). @@ -776,7 +776,7 @@ mod tests { #[test] fn out_of_range_author_witness_is_a_type_mismatch_not_a_refutation() { // `inhabit (4294967296)` (= 2^32) for a `u32` param is not a `u32` inhabitant — - // an ill-typed witness (WitnessTypeMismatch), NOT a `req`-satisfying input. Without + // an ill-typed witness (WitnessTypeMismatch), not a `req`-satisfying input. Without // the width check the truncating model would compute result 0 and manufacture a // false CovenantRefuted on a sound item (#300). let src = "fn idu(x: u32) -> u32 req true ens result == x fx pure { x } \ @@ -799,8 +799,8 @@ mod tests { } // R-COV-1, the closure-instrumented covenant-before-burn invariant (the degrade.rs - // style): the burn closure is invoked ONLY on a validated covenant. On a refutation - // and on a refusal the closure must NOT run (a Cell records invocation). + // style): the burn closure is invoked only on a validated covenant. On a refutation + // and on a refusal the closure must not run (a Cell records invocation). #[test] fn covenant_gate_never_burns_without_covenant() { // (a) Validated → burn runs, gets the record. @@ -833,7 +833,7 @@ mod tests { } )); - // (b) Refuted → burn must NOT run. + // (b) Refuted → burn must not run. let refuted = CovenantAnalysis::Refuted { counterexample: CovenantCounterexample { input: "(3, 7)".to_string(), @@ -857,7 +857,7 @@ mod tests { ); assert!(matches!(gate_r, CovenantGate::Refuted { .. })); - // (c) Error (no author witness) → burn must NOT run; the refusal is named. + // (c) Error (no author witness) → burn must not run; the refusal is named. let refused = CovenantAnalysis::Error(CovenantError::NoAuthorWitness { item: "f".to_string(), }); diff --git a/forge/src/covenant_eval.rs b/forge/src/covenant_eval.rs index 65c086b26..4ba6c1f8a 100644 --- a/forge/src/covenant_eval.rs +++ b/forge/src/covenant_eval.rs @@ -13,30 +13,30 @@ //! string*, this computes the concrete [`Value`] so 50_000 `falsify` inputs run //! in-process (a Verus run per input would be infeasible). //! -//! ## The fragment (and why it is bounded honestly) +//! ## The fragment (and why it is bounded) //! //! The evaluator admits the pure scalar fragment: integer (`u32`/`u64`/`usize`) and //! `bool` values; the arithmetic/comparison/logical/bitwise operators; `!`; `if` //! expressions; `as` casts; and a fn body of `let`/`if`/`return`/tail statements over //! that fragment. Anything outside it — a sequence/`Seq` value, a combinator call, a -//! method call, a `match`, a struct/enum, a loop — is an honest +//! method call, a `match`, a struct/enum, a loop — is an //! [`CovenantEvalError::Unsupported`] carrying the offending shape (it never silently //! evaluates a wrong value, mirroring [`thermite_tv::exec_encode`]'s //! `RefEncodeError::Unsupported`, R-CODE-2 / R-APG-1). A covenant declared on an item -//! outside the fragment surfaces that error loudly rather than dropping the witness. +//! outside the fragment surfaces that error rather than dropping the witness. //! -//! ## The integer-value model (and what the covenant does NOT discriminate) +//! ## The integer-value model (and what the covenant does not discriminate) //! //! Integers are evaluated as mathematical `i128`, wide enough to hold every `u64`, //! with `as` casts modelling the truncating bit-width semantics (`x as u32` reduces //! mod 2³²). Arithmetic, shifts, and bitwise ops (including the type-directed integer -//! `!`) do NOT truncate to the operand width: the covenant checks the AGREEMENT between -//! the body's computed value and what `ens` asserts, and it evaluates BOTH sides under +//! `!`) do not truncate to the operand width: the covenant checks the AGREEMENT between +//! the body's computed value and what `ens` asserts, and it evaluates both sides under //! the same `i128` semantics, so a width-truncation difference (an overflowing `*`, a //! `<<` past the width, a `!` complement) on one side that the other side also computes //! identically can never manufacture a spurious refutation. The absolute width-truncated //! value (the exec-TV / L3 surface, `forge/src/exec_tv.rs`) is not the covenant's -//! discrimination target — only the body-vs-`ens` agreement is. A genuine runtime trap with no `ens` bearing — +//! discrimination target — only the body-vs-`ens` agreement is. A runtime trap with no `ens` bearing — //! a divide-by-zero / shift-out-of-range — is a [`CovenantEvalError::Trap`]: the //! `falsify` driver treats a trapped input as not-evaluated (skipped), not a hit //! (REQ-4: a hit is an `ens` violation on a `req`-satisfying input, `req` is expected @@ -53,9 +53,9 @@ use std::fmt; use thermite_syntax::ast::{BinOp, Block, Expr, PrimType, Stmt, Type, UnaryOp}; -/// An honest failure to evaluate a construct, never a wrong value (REQ-4, the dual of +/// An failure to evaluate a construct, never a wrong value (REQ-4, the dual of /// [`thermite_tv::exec_encode`]'s `RefEncodeError`). The covenant producer surfaces -/// every variant loudly; the `falsify` driver maps a [`CovenantEvalError::Trap`] to a +/// every variant; the `falsify` driver maps a [`CovenantEvalError::Trap`] to a /// skipped input (a partial-operator trap is not an `ens` violation) and any other /// variant to a covenant error (the item is outside the covenant-checkable fragment). #[derive(Debug, Clone, PartialEq, Eq)] @@ -230,7 +230,7 @@ pub fn eval_expr(expr: &Expr, env: &Env) -> Result { )), } } - // Everything else is outside the covenant scalar fragment — honest Unsupported. + // Everything else is outside the covenant scalar fragment and returns Unsupported. other => Err(CovenantEvalError::Unsupported(expr_shape(other))), } } @@ -240,7 +240,7 @@ pub fn eval_expr(expr: &Expr, env: &Env) -> Result { /// a [`CovenantEvalError::Trap`]), the bitwise ops, the comparisons (→ `bool`), and the /// short-circuiting `&&`/`||`. fn eval_binary(op: BinOp, lhs: &Expr, rhs: &Expr, env: &Env) -> Result { - // Short-circuit the logical connectives BEFORE evaluating the rhs (the exec + // Short-circuit the logical connectives before evaluating the rhs (the exec // semantics: `a && b` does not evaluate `b` when `a` is false). match op { BinOp::And => { @@ -307,7 +307,7 @@ fn eval_binary(op: BinOp, lhs: &Expr, rhs: &Expr, env: &Env) -> Result Value::Bool(a >= b), // Add/Sub/Mul/Div/Rem/Shl/Shr/BitAnd/BitOr/BitXor/Lt/Le/Gt/Ge handled above; // Eq/Ne/And/Or returned earlier. This arm is unreachable for the closed BinOp - // set, but we map it to an honest error rather than panic (R-APG-1). + // set, but we map it to an error rather than panic (R-APG-1). BinOp::Eq | BinOp::Ne | BinOp::And | BinOp::Or => { return Err(CovenantEvalError::Unsupported( "binary operator dispatch".to_string(), @@ -319,8 +319,8 @@ fn eval_binary(op: BinOp, lhs: &Expr, rhs: &Expr, env: &Env) -> Result Result { match (l, r) { (Value::Int(a), Value::Int(b)) => Ok(a == b), @@ -332,7 +332,7 @@ fn value_eq(l: Value, r: Value) -> Result { } /// The integer width of a cast target type, or `None` for a `bool` cast. A non-scalar -/// cast target (a slice, a generic) is an honest [`CovenantEvalError::Unsupported`]. +/// cast target (a slice, a generic) is an [`CovenantEvalError::Unsupported`]. fn cast_width(ty: &Type) -> Result, CovenantEvalError> { match ty { Type::Prim(p) => Ok(IntWidth::of_prim(*p)), @@ -347,12 +347,12 @@ fn cast_width(ty: &Type) -> Result, CovenantEvalError> { /// `e`'s value), then evaluate the tail expression. A block with no tail and no /// `return` (a unit-valued block) is a [`CovenantEvalError::Type`] — a covenant item /// returns a scalar value. Loops / `break` / `continue` / mutation are outside the -/// fragment (honest Unsupported). +/// fragment and return Unsupported. pub fn eval_block(block: &Block, env: &Env) -> Result { let mut local = env.clone(); // The names a `let mut` introduced — the only names a `Stmt::Assign` may reassign // (Rust/Verus reject assignment to an immutable binding, E0384). Params + plain - // `let` bindings are absent, so reassigning one is an honest error, not a silent + // `let` bindings are absent, so reassigning one is an error, not a silent // mutation (the covenant-eval faithfulness contract). let mut mutable = BTreeSet::new(); if let Some(v) = eval_stmts(&block.stmts, &mut local, &mut mutable)? { @@ -401,7 +401,7 @@ fn eval_stmts( let name = &segments[0]; if !mutable.contains(name) { // Assignment to a param or a non-`mut` `let` binding — invalid - // executable code (Verus E0384). Surface it loudly rather than + // executable code (Verus E0384). Surface it rather than // silently threading the mutation (the faithfulness contract). return Err(CovenantEvalError::Unsupported(format!( "assignment to immutable binding `{name}` (a covenant body must \ @@ -427,11 +427,11 @@ fn eval_stmts( } }, Stmt::If { cond, then, else_ } => { - // A STATEMENT-position `if/else` (the parser only emits `Stmt::If` here; + // A statement-position `if/else` (the parser only emits `Stmt::If` here; // a tail-position `if/else` is an `Expr::If` handled by `eval_block`'s - // tail). Its VALUE is discarded — Rust/Verus semantics — so a branch's - // tail expression does NOT short-circuit the enclosing block; only a - // `return` STATEMENT inside the taken branch does (`eval_stmts` returns + // tail). Its value is discarded — Rust/Verus semantics — so a branch's + // tail expression does not short-circuit the enclosing block; only a + // `return` statement inside the taken branch does (`eval_stmts` returns // `Some`). Evaluating the branch tail as a block return was a bug that // manufactured a false refutation on e.g. `{ if c { x } else { x } 0 }` // (which returns `0`, not `x`). @@ -544,7 +544,7 @@ mod tests { #[test] fn logical_short_circuit_does_not_touch_unbound_rhs() { - // `false && ` must short-circuit to false WITHOUT evaluating the rhs + // `false && ` must short-circuit to false without evaluating the rhs // (so an unbound name on the dead side is not an error — the exec semantics). let e = env(&[("x", Value::Int(0))]); assert_eq!(eval_src("x > 5 && z == 1", &e), Ok(Value::Bool(false))); @@ -573,7 +573,7 @@ mod tests { #[test] fn unsupported_construct_is_loud_not_silent() { - // A combinator call is outside the scalar fragment — an honest Unsupported. + // A combinator call is outside the scalar fragment — an Unsupported. let e = env(&[("x", Value::Int(1))]); let r = eval_src("forall_in(x, |i| true)", &e); assert!( @@ -626,8 +626,8 @@ mod tests { #[test] fn stmt_position_if_value_is_discarded_not_an_early_return() { - // A STATEMENT-position `if/else` value is DISCARDED (Rust/Verus semantics): the - // body returns the trailing tail `0`, NOT the taken branch's tail `x`. A bug here + // A statement-position `if/else` value is DISCARDED (Rust/Verus semantics): the + // body returns the trailing tail `0`, not the taken branch's tail `x`. A bug here // (taking the branch tail as a block return) manufactures a false refutation on a // correct item — the divergence the critic pinned (#298). let f = parse_fn( @@ -649,7 +649,7 @@ mod tests { #[test] fn assignment_to_immutable_binding_is_a_loud_error_let_mut_works() { // Assignment to a non-`mut` `let` binding is invalid executable code (Verus - // E0384) — an honest error, never a silent mutation (#299, the faithfulness + // E0384) — an error, never a silent mutation (#299, the faithfulness // contract). The only textual difference from the legal control is `mut`. let immutable = parse_fn( "fn setone(x: u64) -> u64 req true ens result == 1 fx pure \ diff --git a/forge/src/degrade.rs b/forge/src/degrade.rs index 961e4bd87..a36108b61 100644 --- a/forge/src/degrade.rs +++ b/forge/src/degrade.rs @@ -82,7 +82,7 @@ pub enum L3Verdict { /// fully superseded: on this edge a lower rung produces the cert (L2 /// verified / L2 counterexample hard-fail / L1 recorded), or a subprocess /// failure propagates as an `Err` (REQ-8). The L0 timeout cert is not the - /// final word on the default path (that was the v0.1 STOP #10 removes). + /// final word on the default path (that was the v0.1 stop #10 removes). Timeout { /// The structured degrade reason carried onto the L2/L1 cert (REQ-4): the /// #11 `VerusTimeout` reason. @@ -93,9 +93,9 @@ pub enum L3Verdict { Counterexample(Certificate), /// the covenant `falsify` run hit a counterexample (REQ-4, the cert verdict /// [`crate::verdict::CertVerdict::CovenantRefuted`]) → hard fail, never a degrade — - /// the SAME never-degrades treatment as `Counterexample` (a refuted covenant is a + /// the same never-degrades treatment as `Counterexample` (a refuted covenant is a /// disproof of the item against its own declared meaning, not an inconclusive run). - /// Carries the non-certifying covenant-refuted cert. The `falsify` PRODUCER is 2b; + /// Carries the non-certifying covenant-refuted cert. The `falsify` producer is 2b; /// the foundation wires the ladder arm so the verdict routes hard the moment 2b /// produces it (and the `covenant_refuted_never_degrades` test pins the routing now). #[allow( @@ -505,7 +505,7 @@ mod tests { // REQ-4 / AC-3, the covenant anti-cheat (the `counterexample_never_degrades` // pattern, instrumented closures): a covenant refutation (`L3Verdict:: - // CovenantRefuted`) is a counterexample-class hard fail — the L2/L1 closures PANIC + // CovenantRefuted`) is a counterexample-class hard fail — the L2/L1 closures panic // if invoked (they must not be), and the returned cert is the non-certifying L0 // cert, never lowered-assurance, never L1/L2. A covenant `falsify` hit is a disproof // of the item against its own declared meaning; it must never hide behind a lowered diff --git a/forge/src/effect_wrappers.rs b/forge/src/effect_wrappers.rs index 26211fb8c..0f6fb6272 100644 --- a/forge/src/effect_wrappers.rs +++ b/forge/src/effect_wrappers.rs @@ -241,7 +241,7 @@ const WRAPPERS: &[Wrapper] = &[ Ok(bytes) => super::TString { data: bytes },\n \ Err(_) => super::TString { data: Vec::new() },\n }\n }\n", }, - // os::write_file (the editor's file-SAVE boundary, #125 — Ctrl-S) — write the + // os::write_file (the editor's file-save boundary, #125 — Ctrl-S) — write the // buffer `String`'s bytes to the editor's fixed demo file (THERMITE_EDITOR_FILE if // set, else `/tmp/thermite_editor.txt`). Returns a status u64 (0 = ok, 1 = I/O // error), the closed status arm, never a panic. The bytes (incl. the `\n` @@ -554,7 +554,7 @@ mod tests { #[test] fn write_file_wrapper_is_total_status_arm() { - // REQ-1 (the editor's file-SAVE boundary, #125 — Ctrl-S): `os::write_file` + // REQ-1 (the editor's file-save boundary, #125 — Ctrl-S): `os::write_file` // writes the buffer bytes (incl. `\n`) to the fixed demo path, returning a // status u64 (0 = ok, 1 = I/O error), the closed arm, never a panic. // Anchored to the design's pinned wrapper shape (R-CHAR-3). diff --git a/forge/src/engine.rs b/forge/src/engine.rs index c1ecf7600..2c62e79e4 100644 --- a/forge/src/engine.rs +++ b/forge/src/engine.rs @@ -29,7 +29,7 @@ //! type error `error[E0308]` the corpus pins at L0. A coarse "no parsed span → //! Unknown" rule would degrade that E0308 to L2 (and crash on the ADT L2 lowering), //! perturbing the oracle. The narrow signature keeps E0308 (and every witnessed -//! countermodel) at `Refuted` → L0, so it fires only on a genuine SMT-`unknown`, a +//! countermodel) at `Refuted` → L0, so it fires only on a SMT-`unknown`, a //! case the corpus does not contain, leaving every `conformance/*.cert.json` //! byte-identical (REQ-3.1's "the remap only changes behavior on inputs the corpus //! does not contain"). @@ -83,7 +83,7 @@ pub enum EngineName { LeanInteractive, /// The nlsat real-relaxation engine (`.design/stage1-forge-tier.md` REQ-8 / /// Q-NLSAT, increment 2f): a direct Z3 `nlsat`-tactic (QF_NRA) query over the - /// relax fragment. Today Z3 is reached only THROUGH Verus (a VC-gen solver call); + /// relax fragment. Today Z3 is reached only through Verus (a VC-gen solver call); /// this is the first real-arithmetic Z3 query as its own engine. A `Proven` /// nlsat discharge certifies at the kernel-grounded [`crate::manifest::Level::L4`] /// — its trust profile is `solver(nlsat) + spine-lemma(kernel)` (the real→integer @@ -126,7 +126,7 @@ impl EngineName { /// SOLVER trust base is recorded here regardless of the rung. It is strictly smaller /// than the Verus base (no VC-gen, no lowering theorem): the clause is a direct, ground /// QF_BV query. Kernel-grounding it (proof reconstruction) is REQ-7/8, which shrinks -/// this trust base at the SAME rung; until then the bit-vector discharge is +/// this trust base at the same rung; until then the bit-vector discharge is /// solver-trusted, named here for the auditor. #[must_use] pub fn bv_trust_profile() -> TrustProfile { @@ -139,58 +139,46 @@ pub fn bv_trust_profile() -> TrustProfile { } } -/// The named trust base a reconstruction-SUPPORTED [`EngineName::BitVector`] clause +/// The named trust base a reconstruction-supported [`EngineName::BitVector`] clause /// carries after REQ-8's default-on trust migration (`.design/stage3-bv-reconstruction.md` -/// REQ-8 / AC-9). Where the per-clause obligation is inside the renderable fragment -/// ([`crate::lean_smt_export::clause_reconstruction_supported`] — the arithmetic/comparison -/// QF_BV subset), its `trust:` migrates from the SOLVER base ([`bv_trust_profile`], `Z3 -/// QF_BV`) to THIS kernel-checked base, at the SAME caged rung [`crate::manifest::Level::L4`] -/// (rung and trust are orthogonal axes — REQ-8 shrinks the trust base, never the rung). +/// REQ-8 / AC-9). When +/// [`crate::lean_smt_export::clause_reconstruction_supported`] can render the clause, +/// its `trust:` migrates from the solver base ([`bv_trust_profile`], `Z3 QF_BV`) to +/// this kernel-checked base at the same caged rung [`crate::manifest::Level::L4`]. /// -/// The migration is honest about exactly what grounds the clause and what residual stays: +/// The migration records what grounds the clause and what residual remains: /// -/// 1. The clause obligation `(P_prod) ⟺ (P_ref)` is rendered over the bounded-integer -/// machine-model and reconstructed by the lean-smt `smt` tactic (cvc5) inside the Lean -/// kernel, `#print axioms ⊆ {propext, Classical.choice, Quot.sound}` (the AC-8 committed -/// `lean/Thermite/SmtExport.lean` proof) — NO solver in the load-bearing base. -/// 2. The bounded-integer model ⇔ the genuine `BitVec N` semantics is the kernel-checked -/// `Thermite.BvModel.frmInt_iff_frmBV` / `tv_equiv_faithful` metatheorem (axiom-clean, -/// Mathlib/Smt-free; in `lean-axiom-probe.sh`) — so the int-model reconstruction grounds -/// the genuine machine semantics, not a re-derivation. -/// 3. The named RESIDUAL (the pretty-printer-trust class, NOT a semantic one, #356): the -/// two string-emission legs — this exporter's pretty-printer and `bitvector.rs`'s SMT -/// renderer — both encode the same `Frm`; their agreement is inspection-tier -/// (`.design/verified/exporter-surface-correspondence.md`), the only residual REQ-8 -/// leaves on the renderable fragment. +/// 1. The clause obligation `(P_prod) ⟺ (P_ref)` is rendered directly over `BitVec N`. +/// Lean proves the normalization with order and commutativity lemmas, and +/// `#print axioms` remains within `{propext, Classical.choice, Quot.sound}`. +/// 2. The two string-emission paths — this exporter and `bitvector.rs`'s SMT-LIB +/// renderer — encode the same fixed-width operators. Their agreement is the +/// inspection-tier pretty-printer residual documented in +/// `.design/verified/exporter-surface-correspondence.md`. /// -/// Strictly smaller than the solver base for the renderable fragment: Z3 QF_BV is no longer -/// load-bearing (the kernel rechecks the clause), leaving only the kernel + the -/// pretty-printer residual. The bitwise/shift/rotate subset is NOT migrated (it keeps -/// [`bv_trust_profile`], named F-J in the audit). +/// This profile no longer needs the bounded-integer bridge or cvc5's incomplete +/// bit-vector proof reconstruction. #[must_use] pub fn bv_kernel_checked_trust_profile() -> TrustProfile { TrustProfile { items: vec![ - "Lean kernel (the (P_prod) ⟺ (P_ref) obligation reconstructed by lean-smt `smt`/cvc5 \ - over the bounded-integer machine-model; #print axioms ⊆ {propext, Classical.choice, \ - Quot.sound} — no solver in the base)" - .to_string(), - "spine-lemma frmInt_iff_frmBV / tv_equiv_faithful (bounded-integer model ⇔ BitVec N \ - machine semantics, kernel-checked, BvModel.lean)" + "Lean kernel, kernel-checked (the literal BitVec N (P_prod) ⟺ (P_ref) obligation \ + is proved by order and commutativity lemmas; #print axioms ⊆ {propext, \ + Classical.choice, Quot.sound})" .to_string(), "pretty-printer residual (the lean_smt_export + bitvector.rs string-emission legs \ - encode the same Frm; inspection-tier, #356)" + encode the same literal fixed-width operators; inspection-tier, #356)" .to_string(), ], } } -/// The stable marker substring every KERNEL-CHECKED / kernel-grounded per-clause trust +/// The stable marker substring used by every kernel-checked or kernel-grounded per-clause trust /// item carries (`.design/stage3-bv-reconstruction.md` REQ-8 / AC-9). Both the bv /// reconstruction profile's faithfulness lemma ([`bv_kernel_checked_trust_profile`] — /// `frmInt_iff_frmBV … kernel-checked`) and the nlsat relax spine lemmas /// (`r_relax_sound`/`rencode_sound … kernel-checked`) name themselves with it, so the -/// audit's residual-trust statement keys the kernel-checked-vs-solver split on ONE marker +/// audit's residual-trust statement keys the kernel-checked-vs-solver split on one marker /// rather than re-deriving the split from engine names (which would miscount the bv route, /// whose engine tag stays `bitvector` whether or not the clause migrated). pub const KERNEL_CHECKED_TRUST_MARKER: &str = "kernel-checked"; @@ -254,7 +242,7 @@ pub struct Evidence { /// The verdict an engine returns for a discharge (`.design/verified/ /// proof-backends.md` REQ-2(b)). The strict mapping discipline (REQ-3): a /// tactic/solver failure without a witnessing input is [`Verdict::Unknown`], not -/// [`Verdict::Refuted`]; refutation requires a genuine countermodel. +/// [`Verdict::Refuted`]; refutation requires a countermodel. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Verdict { /// The engine proved the obligation (for all inputs at a sound-for-all-inputs @@ -343,7 +331,7 @@ pub trait Engine { /// a NON-OPTIONAL parameter so the proof-search path cannot be entered without one /// (covenant-before-burn is a type-level seam, not a runtime convention). Today every /// program carries [`CovenantRecord::none`] (the `witness` surface syntax is REQ-3, - /// not yet present), so the record is inert here; the covenant LOGIC (inhabit/falsify, + /// not yet present), so the record is inert here; the covenant logic (inhabit/falsify, /// the before-burn enforcement, the `CovenantRefuted` hard fail) is increment 2b, /// which fills it in at this seam without re-touching any call site. fn discharge(&self, o: &Obligation, covenant: &CovenantRecord) -> Verdict; @@ -379,13 +367,13 @@ impl VerusEngine { /// - `Proved` → [`Verdict::Proven`] (with the discharged-count evidence + key). /// - `Timeout` → [`Verdict::Unknown`] (`VerusTimeout`) → degrade (unchanged). /// - `Counterexample` split by [`counterexample_is_incompleteness_unknown`]: - /// - the genuine SMT-`unknown` signature (span-less, no frontend `error[E…]`, + /// - the SMT-`unknown` signature (span-less, no frontend `error[E…]`, /// an explicit `unknown` signal) → [`Verdict::Unknown`] (`IncompleteUnknown`) /// → degrade (the REQ-3.1 delta: today this hard-fails; behind the interface /// it degrades, matching §6's degrade-on-incompleteness intent); /// - everything else (a witnessed countermodel with a parsed `--> span`, or a /// frontend type error `error[E…]` like the provenance E0308) → [`Verdict:: - /// Refuted`] (hard fail, byte-identical to today: a real bug or rejection + /// Refuted`] (hard fail, byte-identical to today: a bug or rejection /// never degrades). #[must_use] pub fn verdict_of(&self, outcome: &crate::check::VerusOutcome, key: CacheKey) -> Verdict { @@ -400,7 +388,7 @@ impl VerusEngine { } VerusOutcome::Counterexample { obligations } => { if counterexample_is_incompleteness_unknown(obligations) { - // The REQ-3.1 remap: only the genuine SMT-incompleteness + // The REQ-3.1 remap: only the SMT-incompleteness // `unknown` edge (a witness-less failure carrying the explicit // SMT-`unknown` signature, no frontend/type error) degrades. // Refutation requires a witnessing input; an incompleteness @@ -419,7 +407,7 @@ impl VerusEngine { // countermodel and a frontend rejection (a type error `error[E…]`, // e.g. the IFC un-typeable `careless_query` E0308 the provenance // corpus pins at L0). The remap is inert on the corpus: only the - // narrow genuine-`unknown` signature (which the corpus does not + // narrow solver-`unknown` signature (which the corpus does not // contain) is rerouted, so every `conformance/*.cert.json` is // unperturbed (the increment (i) cert-oracle AC). Verdict::Refuted(Counterexample { @@ -655,13 +643,13 @@ impl LeanEngine { match output { Ok(out) if out.status.success() => { // REQ-2 / AC-5 certify-time axiom gate, HOISTED onto the auto path: a - // clean lake exit is necessary but NOT sufficient for `Proven`. The + // clean lake exit is necessary but not sufficient for `Proven`. The // emitted source carries a `#print axioms ` probe - // (appended in `discharge`); the SAME gate the interactive replay runs + // (appended in `discharge`); the same gate the interactive replay runs // ([`certify_lean_axioms`]) checks the obligation theorem is sorry-free // and rests only on the allowlisted axioms. A surviving `sorry` or a - // smuggled axiom downgrades to `Unknown` (an honest skip — the cert's - // enumerable trusted base cannot be vouched for), NEVER `Proven`. + // smuggled axiom downgrades to `Unknown` (a skip — the cert's + // enumerable trusted base cannot be vouched for), never `Proven`. let probe_out = String::from_utf8_lossy(&out.stdout); match certify_lean_axioms(source, &probe_out, item) { Ok(()) => Verdict::Proven(Evidence { verified, key }), @@ -865,9 +853,9 @@ impl Engine for LeanEngine { } // 3. Append the obligation theorem's `#print axioms` probe so the auto path runs - // the SAME certify-time axiom gate the interactive replay runs (REQ-2 / AC-5: - // the gate is hoisted onto EVERY Lean discharge path, not just interactive - // replay). Without this, a clean lake exit was certified `Proven` with NO + // the same certify-time axiom gate the interactive replay runs (REQ-2 / AC-5: + // the gate is hoisted onto every Lean discharge path, not just interactive + // replay). Without this, a clean lake exit was certified `Proven` with no // axiom check on the auto tiers — a smuggled axiom / surviving `sorry` would // not be caught. The probe anchors on the obligation theorem's exact name. let thm_name = format!("thermite_obligation_{}", proof_thm_sanitize(&o.item)); @@ -940,16 +928,16 @@ impl Engine for LeanEngine { } impl LeanEngine { - /// Discharge a ready-made exported Lean SOURCE that was NOT minted from an + /// Discharge a ready-made exported Lean source that was not minted from an /// [`Obligation`] — the forge-tier `lemma` path (`.design/stage1-forge-tier.md` /// REQ-7, increment 2e). The lemma exporter ([`crate::lean_export::export_lemma`]) /// produces a self-contained file (preamble + `R_item` + the /// `thermite_obligation_` theorem proved by the author's frozen-battery - /// tactics); this appends the SAME certify-time axiom probe (`#print axioms + /// tactics); this appends the same certify-time axiom probe (`#print axioms /// thermite_obligation_`) the auto [`Engine::discharge`] path appends, writes - /// a scratch file, runs lake, and gates `Proven` on the axiom report via the SAME + /// a scratch file, runs lake, and gates `Proven` on the axiom report via the same /// [`certify_lean_axioms`] gate (a surviving `sorry` / smuggled axiom / lake failure - /// is an honest `Unknown`, never `Proven`). Lean-absent → `Unknown` (a skip). The + /// is an `Unknown`, never `Proven`). Lean-absent → `Unknown` (a skip). The /// evidence key binds the source + toolchain + spine content so a bump forces a /// miss. pub(crate) fn discharge_source(&self, source: &str, item: &str) -> Verdict { @@ -1008,7 +996,7 @@ impl LeanEngine { /// tier (a)/(b). A refusal (out-of-spine / not-pure-contract / incomplete registry /// / non-int result) or a tier-(c) interactive obligation is not admitted by the /// auto path — "untested against lean" (REQ-9), never a kill. This runs the export - /// (the same one `discharge` runs), so it is the genuine per-mutant admission gate. + /// (the same one `discharge` runs), so it is the per-mutant admission gate. #[must_use] pub fn admits_auto(&self, o: &Obligation) -> bool { matches!(self.export(o), Ok(e) if e.tier.is_auto()) @@ -1017,24 +1005,24 @@ impl LeanEngine { /// Run the REQ-6a arbitrary-result re-elaboration tautology check on an obligation /// (`.design/stage1-forge-tier.md` REQ-6 / AC-10, increment 2d — anti-Goodhart /// defense (a)). The L3 counterpart of `vacuity_solver.rs::build_tautology_harness`: - /// it exports the SAME obligation with `result` bound to a fresh universally- + /// it exports the same obligation with `result` bound to a fresh universally- /// quantified `(r : Int)` instead of the body denotation - /// ([`crate::lean_export::export_arbitrary_result_harness`]), then DRIVES THE + /// ([`crate::lean_export::export_arbitrary_result_harness`]), then DRIVES the /// EXISTING discharge path — the same `#print axioms` probe + [`Self::run_lake`] a /// normal obligation uses (no new elaborator, per the substrate note). /// /// The polarity mirrors the Verus harness exactly: /// - lake kernel-accepts the harness ([`Verdict::Proven`]) → the `ens` holds for an - /// ARBITRARY result, so the contract says nothing about what the body computes: a + /// arbitrary result, so the contract says nothing about what the body computes: a /// body-ignoring **tautology** → [`ArbitraryResultOutcome::Tautology`] (reject). /// - lake fails to elaborate (an elaboration/tactic failure, not a countermodel) → - /// the `ens` genuinely constrains the result → [`ArbitraryResultOutcome::Clean`]. + /// the `ens` constrains the result → [`ArbitraryResultOutcome::Clean`]. /// A true tautology the auto battery cannot close is a missed detection (the SAFE - /// completeness gap — never an unsound false reject), exactly as the Verus harness. + /// completeness gap — never an unsound false reject), as the Verus harness. /// - the harness is not exportable on the auto path (a refusal / a tier-(c) /// recursive obligation / lake absent / the axiom gate) → [`ArbitraryResultOutcome:: - /// Skipped`] — the check could not run, so it NEVER rejects (the item keeps its - /// real proof; the tautology gate is an additional layer, not a replacement). + /// Skipped`] — the check could not run, so it never rejects (the item keeps its + /// proof; the tautology gate is an additional layer, not a replacement). #[must_use] pub fn arbitrary_result_reelaboration(&self, o: &Obligation) -> ArbitraryResultOutcome { let item = match find_item(&self.program, &o.item) { @@ -1048,7 +1036,7 @@ impl LeanEngine { }; // Export the arbitrary-result harness (same registry / req / ens as the real // obligation; only `result` is the fresh `(r : Int)` binder). A refusal is an - // honest skip (not exportable on the auto path), never a reject. + // skip (not exportable on the auto path), never a reject. let harness = match crate::lean_export::export_arbitrary_result_harness(o, &self.program, item) { Ok(e) => e, @@ -1070,7 +1058,7 @@ impl LeanEngine { )); } // Append the obligation theorem's `#print axioms` probe (the same gate the real - // discharge runs) and drive lake exactly as a normal obligation. + // discharge runs) and drive lake as a normal obligation. let thm_name = format!("thermite_obligation_{}", proof_thm_sanitize(&o.item)); let probed = format!("{}\n\n#print axioms {thm_name}\n", harness.source); let pid = std::process::id(); @@ -1087,9 +1075,9 @@ impl LeanEngine { let verdict = self.run_lake(&scratch, &o.item, &probed, 1, key); let _ = std::fs::remove_file(&scratch); match verdict { - // The harness kernel-accepted for an ARBITRARY result → body-ignoring ens. + // The harness kernel-accepted for an arbitrary result → body-ignoring ens. Verdict::Proven(_) => ArbitraryResultOutcome::Tautology, - // A genuine elaboration/tactic failure (the auto battery could not close the + // An elaboration/tactic failure (the auto battery could not close the // arbitrary-result goal) → the ens constrains the result → clean. An env / // spawn / axiom-gate condition is a skip (the check could not run), never a // claim of clean (R-CODE-4: an undetermined run is not read as a verdict). @@ -1261,7 +1249,7 @@ impl LeanEngine { // The proof is fresh: replay the reconstructed file via lake and capture the // anchored `#print axioms ` (already appended by `reconstruct_replay`) for // the explicit sorry check + the trust-base axiom allowlist (lake exits 0 on a - // `sorry`, so the source/axioms scan is what distinguishes a genuine proof, + // `sorry`, so the source/axioms scan is what distinguishes a proof, // REQ-7(ii)). The probe target is the canonical declaration by construction. let probe = reconstructed; let pid = std::process::id(); @@ -1562,7 +1550,7 @@ pub fn verdict_ladder_action( } } -/// Does a `Counterexample` outcome carry the genuine SMT-incompleteness `unknown` +/// Does a `Counterexample` outcome carry the SMT-incompleteness `unknown` /// signature? (`.design/verified/proof-backends.md` REQ-3.1.) This is the narrow /// remap predicate: the REQ-3.1 fast-`unknown` is the case where the SMT solver /// returned `unknown` (the solver could not decide, an incompleteness event @@ -1573,27 +1561,27 @@ pub fn verdict_ladder_action( /// /// The shipped `classify_verus_outcome` lumps all three span-less failures into the /// `Counterexample` bucket. To keep the cert oracle byte-identical (the increment -/// (i) AC), the remap fires only on the genuine incompleteness signature and +/// (i) AC), the remap fires only on the incompleteness signature and /// defaults to `Refuted` (the shipped `Counterexample → HardFail`) for everything /// else. The signature: no obligation carries a witnessing `--> span` location /// (a real countermodel is witnessed and stays `Refuted`), and no diagnostic -/// carries a frontend error marker (`error[E`: a Rust/VIR type error is a genuine +/// carries a frontend error marker (`error[E`: a Rust/VIR type error is a /// rejection, not an SMT `unknown`, and stays `Refuted` → L0), and a diagnostic /// explicitly names the SMT `unknown` incompleteness verdict. This makes the remap /// inert on the corpus (which contains witnessed failures + E0308 type errors, not -/// genuine SMT `unknown`s), so every `conformance/*.cert.json` is unperturbed. +/// SMT `unknown`s), so every `conformance/*.cert.json` is unperturbed. /// Determinism: a pure function of the parsed obligations (R-CODE-5). #[must_use] pub fn counterexample_is_incompleteness_unknown( obligations: &[crate::manifest::ObligationResult], ) -> bool { - // A witnessed countermodel (any parsed `--> span`) is a genuine disproof → not + // A witnessed countermodel (any parsed `--> span`) is a disproof → not // remapped (stays `Refuted`). if obligations.iter().any(|o| o.location.is_some()) { return false; } // A frontend error (`error[E…]`, a type/VIR rejection like the IFC E0308) is a - // genuine rejection, not an SMT `unknown` → not remapped (stays `Refuted` → L0, + // rejection, not an SMT `unknown` → not remapped (stays `Refuted` → L0, // preserving the provenance corpus oracle). let has_frontend_error = obligations.iter().any(|o| { o.diagnostic @@ -1603,7 +1591,7 @@ pub fn counterexample_is_incompleteness_unknown( if has_frontend_error { return false; } - // The genuine incompleteness signature: a diagnostic explicitly naming the SMT + // The incompleteness signature: a diagnostic explicitly naming the SMT // `unknown` verdict (verus surfaces "unknown" when Z3 returns `unknown` without // a model). Only this narrow case degrades (REQ-3.1). A bare/empty diagnostic // is not remapped; without a positive `unknown` signal we keep the shipped @@ -1621,7 +1609,7 @@ pub fn counterexample_is_incompleteness_unknown( // (the cert field is `Option`, populated only when a non-default engine discharges), // so the default Verus path leaves it `None` and the corpus certs stay byte-identical. // Honest-min project aggregation is unchanged; this is per-obligation metadata -// orthogonal to `Level` (§5 "project aggregation stays honest-min"). +// orthogonal to `Level` (§5 "project aggregation stays minimum"). // ============================================================================ /// The per-obligation engine attribution (`.design/verified/proof-backends.md` @@ -1664,7 +1652,7 @@ pub fn attribution_for(engine: &dyn Engine) -> EngineAttribution { /// A soundness alarm (`.design/verified/proof-backends.md` REQ-5): one engine /// `Proven` and another `Refuted` (a witnessed countermodel) on the same obligation. -/// A genuine countermodel from one engine contradicting a proof from another means +/// A countermodel from one engine contradicting a proof from another means /// one engine (or the exporter/lowering, or `S` itself) is unsound; proceeding would /// launder unsoundness into a certificate, the failure §1's enumerable-base promise /// forbids. Carries both engine names + the obligation + the refuting counterexample @@ -1737,7 +1725,7 @@ pub fn check_disagreement( // ============================================================================ // REQ-7 — interactive proofs (`.design/verified/proof-backends.md` REQ-7(ii) / §4 -// "INTERACTIVE" / §6 tier (c), increment (iii), #247): for a tier-(c) item the engine +// "interactive" / §6 tier (c), increment (iii), #247): for a tier-(c) item the engine // emits the skeleton to `.lean-proofs/.lean` when absent; when present the // file is replayed (lake) with the obligation-hash staleness gate (the emitted header // carries the evidence_key; a mismatch = stale → Unknown("stale proof — re-derive"), @@ -1784,7 +1772,7 @@ pub fn interactive_proof_path(source_file: &std::path::Path, item: &str) -> Path /// `sorry` token in the source (the skeleton's placeholder an agent must fill), and /// (2) a `sorryAx` / `sorry` in the `#print axioms` output (a `sorry` that survived /// elaboration, the authoritative kernel signal). Either is an open hole → the proof -/// is not a genuine kernel proof and is never `Proven`. Determinism: a pure function +/// is not a kernel proof and is never `Proven`. Determinism: a pure function /// of the inspected strings (R-CODE-5). #[must_use] pub fn proof_has_sorry(source: &str, print_axioms_output: &str) -> bool { @@ -1793,7 +1781,7 @@ pub fn proof_has_sorry(source: &str, print_axioms_output: &str) -> bool { /// A textual `sorry` token in the proof source (a whole-word match so a substring /// like `sorryless` does not false-positive). The skeleton emits ` sorry -- -/// INTERACTIVE …`, so an unfilled skeleton trips this. +/// interactive …`, so an unfilled skeleton trips this. fn source_contains_sorry_token(source: &str) -> bool { source .split(|c: char| !c.is_alphanumeric() && c != '_') @@ -1802,7 +1790,7 @@ fn source_contains_sorry_token(source: &str) -> bool { /// A `sorryAx` / `sorry` axiom in a `#print axioms` output (the authoritative kernel /// signal that a `sorry` survived elaboration: lake exits 0 on a `sorry`, so the -/// axioms output is what distinguishes a genuine kernel proof from a `sorry`-carrying +/// axioms output is what distinguishes a kernel proof from a `sorry`-carrying /// one). fn axioms_contain_sorry(print_axioms_output: &str) -> bool { let lower = print_axioms_output.to_ascii_lowercase(); @@ -1899,18 +1887,18 @@ fn nonstandard_axiom(print_axioms_output: &str, item: &str) -> AxiomReport { } /// The shared certify-time axiom gate (REQ-2 / AC-5, `.design/stage1-forge-tier.md`): run -/// on EVERY Lean discharge path — the auto tiers (a)/(b) (via [`LeanEngine::run_lake`]) AND +/// on every Lean discharge path — the auto tiers (a)/(b) (via [`LeanEngine::run_lake`]) and /// the interactive replay (via `replay_interactive`). Given the emitted Lean `source` and /// the lake/lean output (which must contain the obligation theorem's `#print axioms` -/// report), returns `Ok(())` iff the obligation is sorry-free AND its axioms ⊆ the +/// report), returns `Ok(())` iff the obligation is sorry-free and its axioms ⊆ the /// allowlist; otherwise `Err(reason)` naming the surviving `sorry`, the smuggled axiom, or /// the missing report. Hoisting this onto the auto path closes the AC-5 hole: a clean lake /// exit was previously certified `Proven` with no axiom check on the auto tiers. The two -/// callers share THIS function so the gate's behavior cannot drift between paths. +/// callers share this function so the gate's behavior cannot drift between paths. fn certify_lean_axioms(source: &str, lake_output: &str, item: &str) -> Result<(), String> { // (1) `sorry` first (the dedicated message): a `sorry` survives a clean lake exit // (lake exits 0 on a `sorry`), so the source/`sorryAx`-axioms scan is what - // distinguishes a genuine kernel proof. NEVER `Proven` (REQ-7(ii)). + // distinguishes a kernel proof. This is never `Proven` (REQ-7(ii)). if proof_has_sorry(source, lake_output) { return Err(format!( "the exported obligation `{item}` carries a `sorry` (detected in the source \ @@ -2134,7 +2122,7 @@ fn declaration_sites(source: &str, thm_name: &str) -> Vec { /// fixed generator-emitted goal, so a proof term cannot vacate that goal. This belt is a /// defense layer against an `… in`-style top-level command form smuggled into the /// term (`open … in`, `set_option … in`, a `#…`-command): any of these as an exact token -/// (whitespace-independent, position-independent) → reject. A genuine term/tactic proof +/// (whitespace-independent, position-independent) → reject. A term/tactic proof /// never needs these; auxiliary lemmas inline as `have`/`let`/`suffices`. The `#` family /// is handled separately (any `#`-prefixed token). const PROOF_TERM_FORBIDDEN_COMMANDS: [&str; 16] = [ @@ -2219,17 +2207,17 @@ pub fn trust_profile_interactive() -> TrustProfile { // REQ-8 — the nlsat real-relaxation engine (`.design/stage1-forge-tier.md` REQ-8 / // Q-NLSAT / AC-12, increment 2f). The relax route: a relaxable polynomial contract // (the `relax` fragment) is handed to a direct Z3 `nlsat`-tactic (QF_NRA) query — the -// FIRST real-arithmetic Z3 query as its own engine (today Z3 is reached only THROUGH +// first real-arithmetic Z3 query as its own engine (today Z3 is reached only through // Verus). `unsat` over ℝ ⇒ (by the kernel-checked `r_relax_sound`) valid over ℤ ⇒ // certify L4 (kernel-grounded). `sat` ⇒ the integrality check (Q8: round into the -// radius-2 ℤⁿ box) splits a genuine integer `Counterexample` from a real-only +// radius-2 ℤⁿ box) splits an integer `Counterexample` from a real-only // `RealWitness` (true over ℤ, false over ℝ) — the latter escalates UP to the forge, // never down to a `Counterexample`. // ============================================================================ /// The outcome of an nlsat relax discharge (`.design/stage1-forge-tier.md` REQ-8 / /// AC-12). The richer-than-[`Verdict`] result the relax route returns: the -/// [`RealWitness`](NlsatOutcome::RealWitness) case carries a raw REAL point the 3-arm +/// [`RealWitness`](NlsatOutcome::RealWitness) case carries a raw real point the 3-arm /// engine `Verdict` cannot, so [`NlsatEngine::discharge_relax`] is the route's real /// entry point (the `Engine::discharge` trait impl maps it down for generic callers). #[derive(Debug, Clone, PartialEq, Eq)] @@ -2237,13 +2225,13 @@ pub enum NlsatOutcome { /// `unsat` over ℝ: no real counterexample → the relaxation `∀ x : ℝ, req → ⋀ ens` /// holds → by `r_relax_sound` the integer clause holds → certify at L4. Proved, - /// `sat` over ℝ AND an integer point in the radius-2 ℤⁿ box genuinely falsifies the + /// `sat` over ℝ and an integer point in the radius-2 ℤⁿ box falsifies the /// integer clause → a real integer `Counterexample`. Carries the integer witness. Counterexample { /// The integer falsifying point (variable → value, textual). integer_point: Vec<(String, String)>, }, - /// `sat` over ℝ but NO integer point in the box falsifies → the clause is true over + /// `sat` over ℝ but no integer point in the box falsifies → the clause is true over /// ℤ, false over ℝ → a `RealWitness` escalation (never a `Counterexample`). Carries /// the raw real point nlsat returned, handed to the forge as goal metadata. RealWitness { @@ -2251,7 +2239,7 @@ pub enum NlsatOutcome { point: crate::verdict::RealPoint, }, /// z3 returned `unknown`, z3 is absent, the item is not relaxable, or the query - /// failed to render — an honest skip (never a false `Proved`, never a + /// failed to render — a skip (never a false `Proved`, never a /// `Counterexample`). Carries the reason. Unknown(String), } @@ -2276,10 +2264,9 @@ impl NlsatEngine { NlsatEngine { program } } - /// Locate the `z3` binary (`.design/stage1-forge-tier.md` REQ-8 / Q-NLSAT). Z3 is - /// bundled alongside the `verus` distribution and resolved on `PATH` (the same way - /// `check::run_verus` reaches `verus`); a bare `z3` lookup suffices in the - /// verus-on-PATH environment. Deterministic given the environment (R-CODE-5). + /// Locate the `z3` binary (`.design/stage1-forge-tier.md` REQ-8 / Q-NLSAT). + /// The nlsat route requires `z3` itself on `PATH`; finding `verus` alone is not + /// sufficient. Deterministic given the environment (R-CODE-5). fn z3_binary() -> &'static str { "z3" } @@ -2306,7 +2293,7 @@ impl NlsatEngine { /// / AC-12.) The narrowed-fragment admission is the `relaxable` syntactic check /// ([`crate::relax::classify_fn`]) over the item's `fn`, not the static /// `admits_all_classes` flag (`false` for this engine). A non-`fn` item or a - /// non-relaxable contract is not admitted (an honest skip, never a verdict). + /// non-relaxable contract is not admitted (a skip, never a verdict). #[must_use] pub fn admits_relax(&self, item: &str) -> bool { match find_item(&self.program, item) { @@ -2316,7 +2303,7 @@ impl NlsatEngine { } /// Discharge a relaxable `fn`'s contract via the direct Z3 nlsat (QF_NRA) query - /// (`.design/stage1-forge-tier.md` REQ-8c / AC-12 — the relax route's real entry + /// (`.design/stage1-forge-tier.md` REQ-8c / AC-12 — the relax route's entry /// point). Builds the negated-contract query ([`crate::relax::negated_contract_query`]), /// runs the nlsat tactic, and: /// @@ -2326,7 +2313,7 @@ impl NlsatEngine { /// [`NlsatOutcome::Counterexample`]; none → [`NlsatOutcome::RealWitness`] carrying /// the raw real point; /// - `unknown` / z3 absent / not relaxable / render failure → - /// [`NlsatOutcome::Unknown`] (an honest skip, never a false verdict). + /// [`NlsatOutcome::Unknown`] (a skip, never a false verdict). #[must_use] pub fn discharge_relax(&self, f: &thermite_syntax::FnItem) -> NlsatOutcome { if !crate::relax::classify_fn(f).is_relaxable() { @@ -2371,7 +2358,7 @@ impl NlsatEngine { } /// The integrality check (`.design/stage1-forge-tier.md` REQ-8c / Q8): given the - /// `sat` real model, decide whether the counterexample is a genuine integer + /// `sat` real model, decide whether the counterexample is an integer /// `Counterexample` or a real-only `RealWitness`. Rounds each variable to the /// nearest integer and tests the radius-2 ℤⁿ box; an integer point that falsifies /// the integer clause is a `Counterexample`, otherwise the raw real point is a @@ -2401,7 +2388,7 @@ impl NlsatEngine { /// falsifies the integer clause (`.design/stage1-forge-tier.md` REQ-8c / Q8). Each /// variable's box center is its rounded real value (an unconstrained / unparseable /// variable centers at 0); the box is the Cartesian product of `center ± {0,1,2}`. - /// Returns the first integer falsifier (a genuine `Counterexample`), or `None` (the + /// Returns the first integer falsifier (a `Counterexample`), or `None` (the /// real countermodel is real-only → `RealWitness`). The box is small (5ⁿ over the /// few relax variables), well within the Q8 1s budget. fn integrality_box_falsifier( @@ -2448,7 +2435,7 @@ impl NlsatEngine { /// Run z3 over the SMT-LIB2 `input` (fed on stdin), returning `(result, model)`: /// the first result token (`sat`/`unsat`/`unknown`) and the raw model text. `Err` - /// on z3 absent / spawn failure / no result token (an honest skip reason, never a + /// on z3 absent / spawn failure / no result token (a skip reason, never a /// silent success — R-CODE-4). fn run_z3(input: &str) -> Result<(String, BTreeMap), String> { use std::io::Write as _; @@ -2498,7 +2485,7 @@ impl NlsatEngine { /// Parse z3's `(get-model)` output into a `variable → raw-value-text` map /// (`.design/stage1-forge-tier.md` REQ-8c). Extracts each `(define-fun NAME () Real - /// VALUE)` with balanced-paren value capture; the raw value text (a decimal, a + /// value)` with balanced-paren value capture; the raw value text (a decimal, a /// `(- d)`, or a `(/ a b)`) is kept verbatim for the `RealWitness` point and parsed /// to `f64` by [`real_to_f64`](NlsatEngine::real_to_f64) for the integrality /// rounding. @@ -2515,8 +2502,8 @@ impl NlsatEngine { match Self::matching_paren(model, open) { Some(close) => { let inner = &model[after..close]; - // The declared form is `NAME () Real VALUE`; z3 prints VALUE on the - // SAME or the NEXT line (multi-line models), so anchor on `) Real` + // The declared form is `NAME () Real value`; z3 prints value on the + // same or the NEXT line (multi-line models), so anchor on `) Real` // and take the remainder trimmed (a decimal `1.41?`, a `(- d)`, or a // `(/ a b)`). if let Some(rpos) = inner.find(") Real") { @@ -2594,7 +2581,7 @@ impl Engine for NlsatEngine { fn discharge(&self, o: &Obligation, covenant: &CovenantRecord) -> Verdict { // REQ-4 seam: the covenant record is threaded but inert on the relax route (the - // covenant LOGIC is 2b; the relax route is a pure-real discharge). + // covenant logic is 2b; the relax route is a pure-real discharge). let _ = covenant; // Resolve the obligation's `fn` to read its full contract (an `Obligation` does // not carry the `ens` clauses the relax encoding needs). @@ -2606,7 +2593,7 @@ impl Engine for NlsatEngine { }; // Map the rich relax outcome down to the 3-arm engine `Verdict` (the trait's // total type). `RealWitness` has no engine-`Verdict` image (it carries a real - // point) → `Unknown` here; `discharge_relax` is the route's real entry point + // point) → `Unknown` here; `discharge_relax` is the route's entry point // that preserves it. This keeps the trait usable for the disagreement check // (a `Proven` nlsat vs a `Refuted` other engine) without laundering a // `RealWitness` into a `Counterexample`. @@ -2699,11 +2686,11 @@ pub enum ArbitraryResultOutcome { /// The harness kernel-accepted for an arbitrary `result` → the `ens` is body-ignoring /// (a semantic tautology over the Lean discharge domain) → the discharge gate rejects. Tautology, - /// The harness failed to elaborate for an arbitrary `result` → the `ens` genuinely - /// constrains the result → clean (the contract is not a body-ignoring tautology). + /// The harness failed to elaborate for an arbitrary `result`, so the `ens` + /// constrains the result and the contract is not a body-ignoring tautology. Clean, /// The check could not run (export refusal / tier-(c) interactive / lake absent / - /// axiom-gate) — an honest skip that NEVER rejects. Carries the reason. + /// axiom-gate) — a skip that never rejects. Carries the reason. Skipped(String), } @@ -2717,7 +2704,7 @@ pub enum ArbitraryResultOutcome { #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum LeanMutantOutcome { /// The Lean engine proved the mutant — it survived (the contract is too weak, - /// unless then proven equivalent to the real body, the #101 exclusion the caller + /// unless then proven equivalent to the body, the #101 exclusion the caller /// applies). Survived, /// The mutant was attempted by the Lean engine and killed (`Refuted`, a witnessed @@ -2781,7 +2768,7 @@ impl LeanMutationTally { /// `proven_equivalent` is the shipped #101 equivalence-probe result for a survived /// mutant (a §0.1 meta-query, outside the Engine interface in v1, a direct verus /// query the caller threads): an equivalent survivor is dropped from both the - /// survivor set and the denominator (it is not a genuine survivor). + /// survivor set and the denominator (it is not a survivor). pub fn record(&mut self, outcome: LeanMutantOutcome, proven_equivalent: bool) { match outcome { LeanMutantOutcome::UntestedAgainstLean => self.untested += 1, @@ -2889,9 +2876,9 @@ mod tests { } } - // REQ-2 / AC-5 (the shared certify-time axiom gate, hoisted onto EVERY Lean discharge + // REQ-2 / AC-5 (the shared certify-time axiom gate, hoisted onto every Lean discharge // path including the auto tiers): `certify_lean_axioms` accepts a clean report, and - // REFUSES a fourth (Classical-adjacent) axiom by name, a surviving `sorry`, and a + // refuses a fourth (Classical-adjacent) axiom by name, a surviving `sorry`, and a // missing report — hermetically, over synthetic `#print axioms` output anchored on the // obligation theorem. This is the gate `run_lake` now runs on a clean lake exit, so a // smuggled axiom / `sorry` can no longer be certified `Proven` on the auto path. @@ -2948,11 +2935,11 @@ mod tests { } // REQ-2 / AC-5 ("Every ExportRefusal variant has at least one test"; the refusal - // inventory stays LOUD and COMPLETE): construct EACH of the seven structured refusal + // inventory stays LOUD and complete): construct each of the seven structured refusal // variants and assert its `Display` renders the variant's class marker. The behavioral // construction paths are covered elsewhere (undefined-callee → IncompleteRegistry, // while-body → LoopBody, optres-result → OptResResult, capture-unsafe / out-of-spine → - // OutOfFragment, open-hole → OpenHole); this pins the COMPLETE inventory so a variant + // OutOfFragment, open-hole → OpenHole); this pins the complete inventory so a variant // cannot be silently dropped (the foundation must PRESERVE every variant, REQ-2). #[test] fn export_refusal_inventory_is_complete() { @@ -3089,7 +3076,7 @@ mod tests { !counterexample_is_incompleteness_unknown(&e0308), "an E0308 type error is a genuine rejection, NOT an SMT `unknown` (corpus L0)" ); - // (3) The genuine SMT-`unknown` signature is remapped (degrade, REQ-3.1). + // (3) The SMT-`unknown` signature is remapped (degrade, REQ-3.1). let unknown = vec![ObligationResult::failed( "verus reported obligation failure", None, @@ -3244,7 +3231,7 @@ mod tests { } } - // REQ-8 / AC-12 (LIVE, z3-gated): the isqrt characterization — `r*r<=n ∧ + // REQ-8 / AC-12 (live, z3-gated): the isqrt characterization — `r*r<=n ∧ // n<(r+1)² ∧ 1<=r → r<=n` — is a real-valid universal polynomial implication, so // the nlsat relax route discharges it `Proved` (unsat over ℝ → integer-valid by // r_relax_sound → L4). z3-absent SKIPs (CI shards have no z3), mirroring the @@ -3272,9 +3259,9 @@ mod tests { ); } - // REQ-8 / AC-12 (LIVE, z3-gated): `∀ n. n*n ≠ 2` is true over ℤ but false over ℝ - // (n = √2). The relax route's integrality check finds NO integer falsifier in the - // radius-2 box → a `RealWitness` carrying the raw real point (√2), NEVER a + // REQ-8 / AC-12 (live, z3-gated): `∀ n. n*n ≠ 2` is true over ℤ but false over ℝ + // (n = √2). The relax route's integrality check finds no integer falsifier in the + // radius-2 box → a `RealWitness` carrying the raw real point (√2), never a // `Counterexample`. #[test] fn live_nlsat_n_squared_ne_two_is_real_witness() { @@ -3304,11 +3291,11 @@ mod tests { } } - // REQ-8 / REQ-10 / AC-14 (UNGATED, structural): the RealWitness PRODUCER, exercised - // WITHOUT z3. Feed `classify_sat` the real countermodel z3 would return for + // REQ-8 / REQ-10 / AC-14 (ungated, structural): the RealWitness producer, exercised + // without z3. Feed `classify_sat` the real countermodel z3 would return for // `∀ n. n*n ≠ 2` (n ≈ √2) directly: the integrality check rounds it into the radius-2 - // ℤⁿ box, finds NO integer falsifier of `n*n ≠ 2`, and classifies the point a - // `RealWitness` carrying the raw √2 — NEVER a `Counterexample`. This pins the producer + // ℤⁿ box, finds no integer falsifier of `n*n ≠ 2`, and classifies the point a + // `RealWitness` carrying the raw √2 — never a `Counterexample`. This pins the producer // logic hermetically (the AC-14 RealWitness-producer coverage that does not depend on // z3 being installed); the z3 end-to-end run is `live_nlsat_n_squared_ne_two_is_real_witness`. #[test] @@ -3338,8 +3325,8 @@ mod tests { } } - // REQ-8 (LIVE, z3-gated): a contract false over ℤ with an integer falsifier - // (`n+1 <= n`) yields a genuine integer `Counterexample` (not a RealWitness) — the + // REQ-8 (live, z3-gated): a contract false over ℤ with an integer falsifier + // (`n+1 <= n`) yields an integer `Counterexample` (not a RealWitness) — the // integrality check finds the integer witness in the box. #[test] fn live_nlsat_integer_counterexample_is_caught() { @@ -3362,8 +3349,8 @@ mod tests { } } - // REQ-8b (no z3 needed): a div-containing contract is NOT relaxable, so the route - // skips it (an honest `Unknown`, never a verdict) — the fragment gate. + // REQ-8b (no z3 needed): a div-containing contract is not relaxable, so the route + // skips it (an `Unknown`, never a verdict) — the fragment gate. #[test] fn nlsat_div_clause_is_not_relaxable() { let program = parse_program( @@ -3438,9 +3425,9 @@ mod tests { } // REQ-6 / AC-10 (increment 2d, anti-Goodhart defense (a)): the arbitrary-result - // re-elaboration REJECTS a body-ignoring `ens`. `ens x > 0` (given `req x > 0`) - // says nothing about `result` — it holds for an ARBITRARY result, so the harness - // (which binds `result` to a fresh `r : Int`) kernel-accepts → `Tautology`. (LIVE: + // re-elaboration rejects a body-ignoring `ens`. `ens x > 0` (given `req x > 0`) + // says nothing about `result` — it holds for an arbitrary result, so the harness + // (which binds `result` to a fresh `r : Int`) kernel-accepts → `Tautology`. (live: // needs the built Lean spine; skips if lake is absent, like the sibling live tests.) #[test] fn live_arbitrary_result_rejects_body_ignoring_ens() { @@ -3462,9 +3449,9 @@ mod tests { } // REQ-6 / AC-10 (increment 2d): the contrast — a body-CONSTRAINING `ens` is Clean. - // `ens result == x + 1` does NOT hold for an arbitrary result (only for `r = x+1`), - // so the arbitrary-result harness fails to elaborate → `Clean` (the gate does NOT - // reject a genuine, body-pinning contract). + // `ens result == x + 1` does not hold for an arbitrary result (only for `r = x+1`), + // so the arbitrary-result harness fails to elaborate → `Clean` (the gate does not + // reject a body-pinning contract). #[test] fn live_arbitrary_result_clean_for_body_constraining_ens() { if !lake_present() { @@ -4382,12 +4369,12 @@ mod tests { // REQ-6 / §1 / R-DEFER-9 (the #252 belt): the proof-term command scan. The proof term // is the only author-controlled text and is type-checked against the fixed generator // goal, so this is a defense layer against an `… in`-style command form smuggled into - // the term. A genuine term/tactic proof (with inline `have`/`let`/`suffices`) carries + // the term. A term/tactic proof (with inline `have`/`let`/`suffices`) carries // no command keyword; an `open … in` / `set_option … in` / `#…` form is caught // position-independently (exact-token). No lake needed (R-CHAR-3: a structural scan). #[test] fn proof_term_command_token_scans_position_independently() { - // Permitted: a genuine tactic/term proof, including inline `have`/`let`/`suffices` + // Permitted: a tactic/term proof, including inline `have`/`let`/`suffices` // auxiliaries and identifiers that merely contain a keyword (`openVal`, // `Nat.openInterval`) or are `.`-qualified projections. for ok in [ @@ -4438,7 +4425,7 @@ mod tests { // outside the proof term is dropped, never spliced; the indented-command poison (the // #252 divergence) and the #251 macro-poison both have nowhere to live, so the // reconstructed file carries only the canonical preamble + the proof term + the anchored - // probe. A genuine inline-`have` proof term still splices. No lake (a structural test). + // probe. An inline-`have` proof term still splices. No lake (a structural test). #[test] fn reconstruct_drops_author_helper_section() { let canonical = "import Thermite.Stabilize\n\n\ @@ -4500,7 +4487,7 @@ mod tests { ); } - // A genuine inline-have proof term still splices (no expressivity loss: a + // An inline-have proof term still splices (no expressivity loss: a // single-obligation proof inlines auxiliaries as `have`). let legit = "-- evidence_key: abc\n\ import Thermite.Stabilize\n\ @@ -5162,7 +5149,7 @@ mod tests { // self-contained theorem (the pure `∀ params, req → ens` proposition over the // denotation spine) proved by the author's frozen-battery tactics; `discharge_source` // runs lake + the certify-time axiom gate. A clean merge-flavored arithmetic lemma - // kernel-accepts and PROVES (needs the built Lean spine — skipped without it, like the + // kernel-accepts and proves (needs the built Lean spine — skipped without it, like the // other `live_*` engine tests; the CI lean job is authoritative). #[test] fn live_forge_lemma_discharges_proven() { diff --git a/forge/src/exec_tv.rs b/forge/src/exec_tv.rs index b96c27449..18be76a41 100644 --- a/forge/src/exec_tv.rs +++ b/forge/src/exec_tv.rs @@ -16,7 +16,7 @@ //! { }` (`thermite_tv::exec_equivalence_obligation`), and //! discharges it through `verus`. Verified ⟺ the exec lowering of that expr is //! faithful (it computes the bounded reference value for all inputs); a -//! `postcondition not satisfied` / type / parse error ⟺ a real exec-lowering +//! `postcondition not satisfied` / type / parse error ⟺ a exec-lowering //! infidelity (the off-corpus #122/#146/overflow/off-by-one classes). It is exposed //! as `forge exec-tv `, a separate opt-in deeper audit (like `forge tv`), not //! folded into `forge check`. @@ -840,7 +840,7 @@ fn discharge(program: &str, label: &str, seed: u64, rlimit: f64) -> ExecVerdict } /// Parse the `N verified, M errors` summary line from verus output (mirrors -/// `contract_tv`'s parser / the teeth-test). `None` if no summary line is present. +/// `contract_tv`'s parser and the negative test). `None` if no summary line is present. fn parse_results(output: &str) -> Option<(u32, u32)> { let line = output .lines() @@ -928,15 +928,15 @@ pub const EXEC_TV_DEFAULT_RLIMIT: f64 = DEFAULT_RLIMIT; /// AC-7). pub const EXEC_TV_GENERATED_DEFAULT_N: usize = 200; -// ---- the forge-level Divergent teeth (REQ-5; blocker #157) ----------------- +// ---- forge-level Divergent regression tests (REQ-5; blocker #157) ---------- // -// The obligation-layer teeth (`thermite-tv/tests/exec_teeth.rs` E1-E4) prove a -// wrong `P_production` -> a real verus error. They do not exercise the forge-level +// The obligation-layer tests (`thermite-tv/tests/exec_teeth.rs` E1-E4) prove a +// wrong `P_production` -> a verus error. They do not exercise the forge-level // step that maps that verus error to `ExecVerdict::Divergent`: `discharge`'s // four-way classification. Over the generated/corpus space the faithful lowerer // never produces a Divergent, so the Divergent arm had no direct test coverage. // -// This module is the end-to-end teeth for the forge classification: it builds a +// This module tests the forge classification end to end. It builds a // real exec obligation with a wrong production (the same E1/E3 infidelity shapes // the obligation layer pins), discharges it through the actual `discharge` fn, and // asserts the verdict. It covers both Divergent triggers (postcondition- @@ -946,7 +946,7 @@ pub const EXEC_TV_GENERATED_DEFAULT_N: usize = 200; // // Test-only: no production-logic change. `discharge` is a private sibling fn, // reachable here via `super::` (a child mod sees the parent's private items), so no -// visibility tweak is needed either. The teeth run a real wrong production -> a real +// visibility tweak is needed either. The tests run a wrong production through a // verus error -> the real `discharge` mapping, not a mocked verdict. Mirrors // `thermite-tv/tests/exec_teeth.rs`'s verus gate -- `discharge` spawns a // bare `verus`, so the test gates on the same PATH-resolvable binary and reports a @@ -958,7 +958,7 @@ mod divergent_teeth { /// `true` iff a bare `verus` is spawnable (the same resolution `discharge` /// uses -- `Command::new("verus")`, i.e. PATH). Skip otherwise so the - /// teeth do not pass when the discharge cannot reach a solver. + /// tests do not pass when the discharge cannot reach a solver. fn verus_on_path() -> bool { Command::new("verus").arg("--version").output().is_ok() } diff --git a/forge/src/forks.rs b/forge/src/forks.rs index 6f2a0a478..1212a5b0d 100644 --- a/forge/src/forks.rs +++ b/forge/src/forks.rs @@ -6,7 +6,7 @@ //! 1. **semantic forks** — `@bv`-tagged clauses are interpreted over a fixed-width //! machine-semantics fork, not the default unbounded integers. Lock 1 (REQ-3) already //! makes each tagged clause loud + greppable (its `bv_shadow` block, surfaced per -//! clause in `forge audit`/`forge review`). This section is the AGGREGATE over those +//! clause in `forge audit`/`forge review`). This section is the aggregate over those //! clauses: **bv-shadow density per module** — how much of each contract-bearing //! item's postcondition surface has committed to a machine-semantics fork. //! 2. **definition towers** — a forge-tier `lemma`'s `req ∪ ens` can unfold a tower of @@ -19,14 +19,14 @@ //! Q-BVSCOPE asked whether to ship the `@bv` tag full, `nowrap`-only, or lemma-only. The //! "measure bv-shadow density first" input was circular — no bv clause exists to measure //! until the tag ships. So the resolution (`.design/stage3-bv-reconstruction.md` Decision -//! Record) ships the full tag guarded by its three locks and makes THIS density report the +//! Record) ships the full tag guarded by its three locks and makes this density report the //! *post-ship* retreat trigger: rising shadow-flag density in contract-bearing code is the //! named **F-F tripwire** down the ladder (full → `nowrap`-only → lemma-only → drop). The //! tripwire fires when the project's bv-shadow density crosses //! [`FF_DENSITY_THRESHOLD_PERMILLE`] — a loud, informational warning that the program is //! becoming dominated by machine-semantics forks and the retreat ladder should be weighed. //! -//! ## A pure projection that GATES NOTHING +//! ## A pure projection that gates nothing //! //! Like `forge audit` itself (#274 "audit gates nothing") and the `--meaning` companion, //! this section is a deterministic projection of the settled cert collection + the parsed @@ -42,7 +42,7 @@ use thermite_syntax::{Expr, Item, Program}; use crate::manifest::{cert_certifies, Certificate}; /// The F-F retreat-trigger threshold (REQ-6 / AC-7): the project-wide bv-shadow density, -/// in PER-MILLE of the contract-bearing postcondition surface, at or above which the named +/// in per-MILLE of the contract-bearing postcondition surface, at or above which the named /// F-F tripwire fires. `500‰` (half) is the Schelling point — when a MAJORITY of the /// project's `ens` clauses have committed to a fixed-width machine-semantics fork, the /// program has drifted far enough toward the fork that the retreat ladder (full → @@ -106,7 +106,7 @@ pub struct LemmaTower { } /// The project-wide F-F density tripwire (REQ-6 / AC-7) — the post-ship retreat trigger. -/// Aggregates the bv-shadow density across ALL contract-bearing `ens` clauses and compares +/// Aggregates the bv-shadow density across all contract-bearing `ens` clauses and compares /// it to [`FF_DENSITY_THRESHOLD_PERMILLE`]. Informational: a tripped tripwire gates /// nothing (it changes no verdict and no exit code), it raises a named human warning. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] @@ -129,7 +129,7 @@ pub struct FfTripwire { impl SemanticForks { /// Build the section from the settled cert collection + the parsed program (REQ-6 / - /// AC-7), or `None` when there is nothing to report — no `@bv`-tagged clause AND no + /// AC-7), or `None` when there is nothing to report — no `@bv`-tagged clause and no /// burned lemma (the v1 / non-bv corpus, whose goldens stay byte-identical). A pure /// projection: density is a parse-level fact, the burned-lemma set is read from the /// certificates (the `review::burned_lemma_projection` predicate), and the tower depth @@ -263,7 +263,7 @@ fn burned_lemma_towers(certs: &[Certificate], program: &Program) -> Vec Some(l), _ => None, @@ -273,7 +273,7 @@ fn burned_lemma_towers(certs: &[Certificate], program: &Program) -> Vec = Vec::with_capacity(1 + lemma.ens.len()); roots.push(&lemma.req.expr); @@ -430,7 +430,7 @@ mod tests { assert_eq!(towers[0].definitions, 2); } - // AC-7: an uncertified lemma (no burn) is NOT surfaced as a tower — only a burned + // AC-7: an uncertified lemma (no burn) is not surfaced as a tower — only a burned // (certified, receipted) lemma is, mirroring `review::burned_lemma_projection`. #[test] fn uncertified_lemma_is_not_a_tower() { diff --git a/forge/src/goal_repl.rs b/forge/src/goal_repl.rs index 74a52189e..a7c3f9cb4 100644 --- a/forge/src/goal_repl.rs +++ b/forge/src/goal_repl.rs @@ -81,11 +81,11 @@ pub fn render_battery(file: &Path, item: Option<&str>) -> Result String { /// Render the proof view for a `proof for f` item (REQ-7): one block per obligation, /// each resolving its `ens#k` goal against `f`'s contract and binding `f`'s /// params + `req` as the hypotheses in scope. A `proof for` whose target `f` is not a -/// `fn` in the file (or whose clause selector resolves no clause) renders an honest +/// `fn` in the file (or whose clause selector resolves no clause) renders an /// "unresolved" note rather than a fabricated goal (R-CODE-2 — surface the gap). fn render_proof_for(p: &ProofItem, program: &Program) -> String { let mut out = String::new(); @@ -242,7 +242,7 @@ fn render_hypotheses(params: &[Param], req: &Clause) -> String { /// Render the open `?pN` proof holes as the `forge fill` operands (REQ-7 / AC-11), or /// — when the proof block is non-empty with no open hole — a "proof authored" -/// committed line. An empty hole-free proof block is an honest "no proof yet" note. +/// committed line. An empty hole-free proof block is an "no proof yet" note. fn render_proof_holes( out: &mut String, holes: &[thermite_syntax::Hole], @@ -275,10 +275,10 @@ fn clause_label(sel: &ClauseSelector) -> String { /// Resolve a [`ClauseSelector`] against a function's contract (REQ-7). `req` names the /// (single) precondition; `ens#k` names the `k`-th ensures clause 0-based in source -/// order (`ens#0` is the FIRST `ens` — the convention the forge-tier proof-obligation +/// order (`ens#0` is the first `ens` — the convention the forge-tier proof-obligation /// corpus already uses, `thermite-syntax/tests/forge_items.rs`: `ens#0 by { … } ens#1 /// by { … }`). Returns `None` for an out-of-range / unknown selector (rendered as an -/// honest "unresolved" goal rather than a fabricated one). +/// explicit "unresolved" goal rather than a fabricated one). fn resolve_clause<'c>(contract: &'c Contract, sel: &ClauseSelector) -> Option<&'c Clause> { match sel.keyword.as_str() { "req" => Some(&contract.req), @@ -373,9 +373,9 @@ pub fn edit_file(file: &Path, addr: &str, replacement: &str) -> Result.?N` (#193): splices into a `fn` body, re-renders the goal state. -/// - a PROOF hole `.proof.?pN` / `.proof..?pN` (stage-1 REQ-7, 2e): -/// splices into a forge-tier proof block, re-renders the PROOF VIEW + the re-check +/// - a body hole `.?N` (#193): splices into a `fn` body, re-renders the goal state. +/// - a proof hole `.proof.?pN` / `.proof..?pN` (stage-1 REQ-7, 2e): +/// splices into a forge-tier proof block, re-renders the proof view + the re-check /// verdict (the frozen battery refuses an unlisted tactic — REQ-5/2c — and the /// discharge produces the forge-tier cert with the burn receipt — REQ-7). /// @@ -478,7 +478,7 @@ fn render_proof_cert_status(cert: &Certificate) -> String { /// Render one item's goal state (REQ-2; §5.1). The `given` is the `req` clause /// text; the `want` is the `ens` clause texts; then each obligation as discharged -/// or failed-with-witness; a clean cert renders `ALL GOALS DISCHARGED` + the level +/// or failed-with-witness; a clean cert renders `all goals discharged` + the level /// + the battery line. fn render_goal_item(cert: &Certificate, program: &Program) -> String { let mut out = String::new(); @@ -687,15 +687,15 @@ pub(crate) fn open_hole_reason(f: &thermite_syntax::FnItem) -> Option { )) } -/// The shared open-PROOF-hole refusal text for a forge-tier item carrying any open +/// The shared open-proof-hole refusal text for a forge-tier item carrying any open /// `?pN` proof hole (`.design/stage1-forge-tier.md` REQ-3 / AC-7). Returns /// `Some(detail)` iff the item's proof block(s) carry any open proof hole — a /// `lemma`'s proof block, or any `proof for f` obligation's `by { … }` block (a /// `prop fn`/`witness` carries no proof block). Mirrors [`open_hole_reason`]: an -/// item with an open proof hole is incomplete and does NOT certify and does NOT +/// item with an open proof hole is incomplete and does not certify and does not /// build (the same never-ship-incomplete invariant), so `check.rs` and `build.rs` /// gate on this with one message. The covenant/proof-view consumers (2b/2e) own -/// the fill loop; this gate only refuses an OPEN one. Pure function of the item's +/// the fill loop; this gate only refuses an open one. Pure function of the item's /// proof holes (R-CODE-5). pub(crate) fn open_proof_hole_reason(forge: &thermite_syntax::ForgeItem) -> Option { use thermite_syntax::ForgeItem; @@ -758,7 +758,7 @@ fn span_of_address(program: &Program, addr: &str) -> Option { let entry_kind = address::resolve(program, addr).ok()?.kind; // A `?pN` proof hole lives on a forge-tier item (a `lemma`'s proof block or a - // `proof for f` obligation's block), NOT on a `fn`, so it dispatches before the + // `proof for f` obligation's block), not on a `fn`, so it dispatches before the // `fn` lookup below (stage1-forge-tier.md REQ-7, increment 2e). if entry_kind == AddrKind::ProofHole { return proof_hole_span(program, addr); @@ -1011,7 +1011,7 @@ mod tests { ); } - // REQ-2 / AC-2: a clean L3 cert renders ALL GOALS DISCHARGED + the level + the + // REQ-2 / AC-2: a clean L3 cert renders all goals discharged + the level + the // §7 battery line. #[test] fn goal_render_discharged() { @@ -1198,7 +1198,7 @@ mod tests { r.contains("x : u64") && r.contains("y : u64"), "f's params bound: {r}" ); - // `ens#1` is the SECOND ens clause (0-based), `result >= y`. + // `ens#1` is the second ens clause (0-based), `result >= y`. assert!( r.contains("\u{22a2} goal: result >= y"), "ens#1 resolves to the second ens clause: {r}" @@ -1231,7 +1231,7 @@ mod tests { } // REQ-7 / R-CODE-2: a `proof for` whose clause selector is out of range renders an - // honest "unresolved" goal, never a fabricated one or a panic. + // explicit "unresolved" goal, never a fabricated one or a panic. #[test] fn proof_view_proof_for_out_of_range_clause_is_unresolved() { let src = "fn f(n: u32) -> u32 req true ens result == n fx pure { n }\n\ diff --git a/forge/src/kani.rs b/forge/src/kani.rs index d5625510a..7f11c7c86 100644 --- a/forge/src/kani.rs +++ b/forge/src/kani.rs @@ -14,10 +14,10 @@ //! //! Grounded against `cargo kani 0.67.0`'s terse format: //! -//! - success → `Level::L2`: the line `VERIFICATION:- SUCCESSFUL` (and +//! - success → `Level::L2`: the line `verification:- successful` (and //! `** 0 of N failed`). The discharged obligation records the bound //! (`slice <= N, unwind K`) so a reader sees the L2 caveat (REQ-6). -//! - counterexample → a non-L2 reported cert: `VERIFICATION:- FAILED` plus +//! - counterexample → a non-L2 reported cert: `verification:- FAILED` plus //! `Failed Checks: ` and (where present) `File: "", line `. //! Each failed check becomes an `ObligationResult::failed(description, location, //! raw)`, the §5.1 counterexample witness. The `unwinding assertion loop 0` @@ -63,7 +63,7 @@ use crate::manifest::{Certificate, Level, ObligationResult, ObligationStatus}; /// wall-clock solver time (excluded from the cert oracle, REQ-9). #[derive(Debug, Clone)] pub struct L2Result { - /// `Level::L2` on `VERIFICATION:- SUCCESSFUL`; `Level::L0` on a reported + /// `Level::L2` on `verification:- successful`; `Level::L0` on a reported /// counterexample (a non-L2 result, not a false pass, REQ-5/§6). pub level: Level, /// The per-obligation witnesses: one discharged obligation recording the @@ -189,7 +189,7 @@ fn write_kani_crate(crate_dir: &Path, stem: &str, harness: &str) -> Result<(), F } /// Parse Kani's `--output-format terse` output into an [`L2Result`] (REQ-5). The -/// summary line `VERIFICATION:- SUCCESSFUL`/`FAILED` drives the level; the +/// summary line `verification:- successful`/`FAILED` drives the level; the /// `Failed Checks:` lines (+ the following `File: "", line `) become the /// per-obligation counterexample witnesses. No recognizable summary line → a /// `ForgeError::KaniOutput` (surfaced, not swallowed into a false pass, R-CODE-4). @@ -330,15 +330,15 @@ fn first_lines(text: &str, n: usize) -> String { /// the timeout-vs-counterexample split is #11's `SolverProfile`-presence /// discriminator; at L2 the discriminator is the shape of the kani failure: /// -/// - [`L2Verdict::Verified`]: `VERIFICATION:- SUCCESSFUL` (`L2Result` is +/// - [`L2Verdict::Verified`]: `verification:- successful` (`L2Result` is /// `Level::L2`) → the ladder certifies L2. -/// - [`L2Verdict::UnderBound`]: a `VERIFICATION:- FAILED` whose only failed +/// - [`L2Verdict::UnderBound`]: a `verification:- FAILED` whose only failed /// obligations are `unwinding assertion` (kani ran out of unwind / could not /// bound the loop, the L2 analog of a timeout, inconclusive). The ladder /// degrades to L1 (REQ-3). -/// - [`L2Verdict::Counterexample`]: a `VERIFICATION:- FAILED` carrying a real +/// - [`L2Verdict::Counterexample`]: a `verification:- FAILED` carrying a real /// property `assertion failed: ` witness (kani disproved the -/// contract, a real bug). A hard fail, not a degrade (REQ-2 anti-cheat). +/// contract, a bug). A hard fail, not a degrade (REQ-2 anti-cheat). /// /// Conservative (R-DEFER-9, the doc's OQ-2 ratified resolution): an ambiguous /// `FAILED` shape (any failed obligation that is not an unwinding assertion, or a @@ -352,7 +352,7 @@ pub enum L2Verdict { /// Bound exhausted (only `unwinding assertion` failures) → inconclusive, /// degrade to L1. UnderBound, - /// A real contract property was disproved (or the failure is ambiguous) → + /// A contract property was disproved (or the failure is ambiguous) → /// hard fail, never a degrade. Counterexample, } @@ -381,7 +381,7 @@ pub fn classify_l2_outcome(result: &L2Result) -> L2Verdict { return L2Verdict::Counterexample; } // Under-bound iff every failed obligation is an unwinding-assertion / resource - // failure (the bound ran out). A single real property failure → counterexample. + // failure (the bound ran out). A single property failure → counterexample. if failed.iter().all(|o| is_under_bound_failure(&o.name)) { L2Verdict::UnderBound } else { @@ -393,7 +393,7 @@ pub fn classify_l2_outcome(result: &L2Result) -> L2Verdict { /// exhaustion (the L2 analog of a timeout, issue #10 OQ-2), as opposed to a real /// property counterexample. The discriminator is kani's boilerplate /// `unwinding assertion` text (the grounded `unwinding assertion loop N` shape: -/// the loop unwind ran out), and that alone. A real property failure +/// the loop unwind ran out), and that alone. A property failure /// (`assertion failed: result == spec_sum(xs)`) is not under-bound. This /// holds even when the user's `ens` clause, which kani echoes verbatim into /// `Failed Checks:`, merely contains the substring `unwind` (a spec helper @@ -426,7 +426,7 @@ mod tests { const BOUND: &str = "slice <= 4, unwind 5"; - // REQ-5 / AC-1: a terse SUCCESSFUL summary → Level::L2 with the bound recorded + // REQ-5 / AC-1: a terse successful summary → Level::L2 with the bound recorded // (the grounded `sum` output, R-CHAR-3 — Kani's real format, not forge's). #[test] fn success_terse_is_l2() { @@ -586,7 +586,7 @@ mod tests { ); } - // #10 OQ-2 / REQ-2 anti-cheat: a real property `assertion failed: ` FAILED + // #10 OQ-2 / REQ-2 anti-cheat: a property `assertion failed: ` FAILED // is a Counterexample (hard fail), not an under-bound degrade; kani disproved // the contract (the grounded broken-`sum` shape). R-CHAR-3. #[test] @@ -607,7 +607,7 @@ mod tests { ); } - // #10 OQ-2 / REQ-2 anti-cheat (blocker #51): a real property counterexample whose + // #10 OQ-2 / REQ-2 anti-cheat (blocker #51): a property counterexample whose // `ens` clause text merely contains the substring `unwind` (an ordinary // identifier, here a spec helper `unwind_count`) is a Counterexample, not an // UnderBound degrade to L1. The under-bound discriminator is kani's boilerplate diff --git a/forge/src/lean_export.rs b/forge/src/lean_export.rs index b47c85db7..daff8f594 100644 --- a/forge/src/lean_export.rs +++ b/forge/src/lean_export.rs @@ -121,7 +121,7 @@ pub enum ExportRefusal { /// Carries the offending result type. NonIntResult(String), /// The item carries an open body hole (`?N`) — short-circuited L0 before any - /// engine (§8 OUT set); not exportable. + /// engine (§8 out set); not exportable. OpenHole(String), /// The loop-class structured refusal (§4.1.7 / REQ-10.6): a body containing a /// `loop`/`while`/`break`/`continue`/mid-body-`return`/non-scalar-mutation — @@ -311,7 +311,7 @@ fn encode_expr(e: &Expr, ctx: &EncodeCtx) -> Result { other => encode_expr(other, ctx), } } - // Out of S_C's frozen subset (a refusal — §4 / §8 OUT set): + // Out of S_C's frozen subset (a refusal — §4 / §8 out set): Expr::Field { name, .. } => Err(ExportRefusal::OutOfFragment(format!( "field access `.{name}` (S_C has no struct-field projection)" ))), @@ -338,8 +338,8 @@ fn encode_expr(e: &Expr, ctx: &EncodeCtx) -> Result { )), // A raw quantifier `forall`/`exists` (`.design/stage2-stratified-cage.md` // REQ-0): the v1 contract fragment `S_C` (the `Ast.lean` mirror this exporter - // targets) has NO raw binder constructor — stratified Lean encoding is REQ-5 - // (`Strat/RefEncode.lean`), a separate namespace. Refuse honestly so no + // targets) has no raw binder constructor — stratified Lean encoding is REQ-5 + // (`Strat/RefEncode.lean`), a separate namespace. Refuse so no // unproven binder is ever exported to the v1 spine and the Rust↔Lean // correspondence stays pinned. Expr::Quantifier { .. } => Err(ExportRefusal::OutOfFragment( @@ -385,7 +385,7 @@ fn encode_binary( /// Encode a cast target to a `Thermite.CastTy` (REQ-6 EXP; mirrors /// `RefEncode.lean`'s `cast_target`, #177). A cast to anything outside -/// `u64`/`u32`/`usize`/`nat`/`int` is OUT of S_C. +/// `u64`/`u32`/`usize`/`nat`/`int` is out of S_C. fn encode_cast_target(ty: &Type) -> Result<&'static str, ExportRefusal> { match ty { Type::Prim(PrimType::U64) => Ok("Thermite.CastTy.u64"), @@ -423,7 +423,7 @@ fn encode_index(base: &Expr, index: &IndexArg, ctx: &EncodeCtx) -> Result Option<&'static str> { /// Encode a free call `f(args)` — a combinator call (`Expr.comb`) or a named /// spec-fn call (`Expr.specCall`) (REQ-6 EXP; mirrors `RefEncode.lean`'s /// `encode_call` cases #179/#181). `old(x)` is the pre-state free name (→ `Expr.var -/// "old(x)"`). A qualified / non-`Path` callee is OUT. +/// "old(x)"`). A qualified / non-`Path` callee is out. fn encode_call(callee: &Expr, args: &[Expr], ctx: &EncodeCtx) -> Result { let name = match callee { Expr::Path(segs) if segs.len() == 1 => segs[0].clone(), @@ -574,7 +574,7 @@ fn encode_combinator( /// Encode a contract-position `match scrut { arms }` to `Expr.match_` (REQ-6 EXP; /// #180). Only the C7 built-in Option/Result 2-arm forms are in S_C; a user-ADT arm -/// / a guard / a wildcard is OUT. +/// / a guard / a wildcard is out. fn encode_match( scrutinee: &Expr, arms: &[thermite_syntax::MatchArm], @@ -623,7 +623,7 @@ fn encode_match( } /// Encode a built-in variant name to `Thermite.Variant` (REQ-6 EXP; #180). A user -/// variant is OUT of S_C. +/// variant is out of S_C. fn encode_variant(path: &[String]) -> Result<&'static str, ExportRefusal> { let last = path.last().map(String::as_str).unwrap_or(""); match last { @@ -1430,8 +1430,8 @@ fn emit_theorem( // body's stabilized value — the proof discharges `ens` for what the body // computes. // - Arbitrary (the re-elaboration tautology harness): `result` = a fresh - // `(r : Int)` theorem binder, the body DROPPED — the proof must discharge - // `ens` for an ARBITRARY result. If the auto battery still closes it, the + // `(r : Int)` theorem binder, the body dropped — the proof must discharge + // `ens` for an arbitrary result. If the auto battery still closes it, the // `ens` holds regardless of the body (a body-ignoring tautology). The // Lean counterpart of `build_tautology_harness`'s arbitrary `result` param. let (binder, result_value) = match result_mode { @@ -1568,7 +1568,7 @@ fn exec_result_of(ty: &Type) -> Result { match ty { Type::Prim(PrimType::Bool) => Ok(ExecResult::Bool), // `Option<_>`/`Result<_,_>` are dedicated `Type` nodes (C7) — the optres - // result position is OUT (#254: `ExecVal` has no optres variant). + // result position is out (#254: `ExecVal` has no optres variant). Type::Option(_) | Type::Result(_, _) => Err(ExportRefusal::OptResResult(format!("{ty:?}"))), other => Err(ExportRefusal::OutOfFragment(format!( "straight-line-body result type {other:?} is not an exec int/bool sort \ @@ -1677,7 +1677,7 @@ fn encode_exec_expr(e: &Expr, width: &str, ctx: &ExecCtx) -> Result encode_exec_index(base, index, ctx), Expr::Ref { expr, .. } => encode_exec_expr(expr, width, ctx), - // Out of `S_E`'s frozen subset (a refusal — §4.1 / §8 OUT): + // Out of `S_E`'s frozen subset (a refusal — §4.1 / §8 out): Expr::MethodCall { name, .. } => Err(ExportRefusal::OutOfFragment(format!( "method call `.{name}(..)` in exec-body position (S_E has no method calls)" ))), @@ -1734,7 +1734,7 @@ fn encode_exec_binary( /// Encode an exec cast target to a `Thermite.Exec.IntTy` (the bounded-wrap cast, /// `Exec.lean`'s `castVal`). The exec domain wraps only to bounded int sorts (never -/// `nat`/`int` — those are the unbounded `S_C` casts, OUT of `S_E`). +/// `nat`/`int` — those are the unbounded `S_C` casts, out of `S_E`). fn exec_cast_target(ty: &Type) -> Result<&'static str, ExportRefusal> { exec_int_ty(ty).ok_or_else(|| { ExportRefusal::OutOfFragment(format!( @@ -1827,7 +1827,7 @@ fn encode_exec_stmt(stmt: &Stmt, ctx: &mut ExecCtx) -> Result { // The v1 mutation form is `x = e` over a bare scalar `Path[x]` target; a - // non-scalar `xs[i] = e` / `m.field = e` is OUT (§4.1.7). + // non-scalar `xs[i] = e` / `m.field = e` is out (§4.1.7). let name = match target { Expr::Path(segs) if segs.len() == 1 => segs[0].clone(), other => { @@ -2031,7 +2031,7 @@ fn emit_body_theorems( spine's `inputState` exemplar — a body `assign` to a param is `none`). -/\n\ {state_of_def}\n\n\ {lemmas}\n\n\ - /-- {thm_name}: the HYPOTHESIZE CONTRACT obligation (§4.1.5). The result is\n \ + /-- {thm_name}: the HYPOTHESIZE contract obligation (§4.1.5). The result is\n \ bound THROUGH `bodyConverges` (uniqueness FREE — `bodyDenote` is a function);\n \ the OVERFLOW class is conjoined as `{thm_name}_overflow` (the soundness\n \ condition making the vacuous-on-overflow case harmless). -/\n\ @@ -2042,7 +2042,7 @@ fn emit_body_theorems( Thermite.denote 0 {req_term} {{ v with specs := R_item }} →\n \ Thermite.denote 0 {ens_term} {bind_result} := by\n\ {body_battery}\n\n\ - /-- {thm_name}_overflow: the conjoined OVERFLOW obligation (§4.1.5) — under\n \ + /-- {thm_name}_overflow: the conjoined overflow obligation (§4.1.5) — under\n \ the precondition every 2a obligation threaded through the body discharges\n \ (`bodyDenote … |>.isSome`). A Lean-only straight-line item certifies ONLY when\n \ BOTH this AND the CONTRACT theorem kernel-accept (the conjunction rule). -/\n\ @@ -2114,15 +2114,15 @@ pub fn export_item( /// How the obligation theorem binds `result` (REQ-6a, increment 2d — anti-Goodhart /// defense (a)). The shipped export binds `result` to the body's stabilized value; /// the arbitrary-result re-elaboration harness binds it to a fresh universally- -/// quantified binder, so the goal becomes "the `ens` holds for an ARBITRARY result". +/// quantified binder, so the goal becomes "the `ens` holds for an arbitrary result". #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum ResultMode { /// The shipped binding: `result` = `(Thermite.intVal 0 …)`, the body's /// stabilized value. The proof must discharge `ens` for the value the body computes. BodyDenotation, /// The arbitrary-result re-elaboration binding (REQ-6a): `result` = a fresh - /// `(r : Int)` theorem binder, the body denotation DROPPED. The proof must - /// discharge `ens` for an ARBITRARY result — if it still elaborates, the `ens` + /// `(r : Int)` theorem binder, the body denotation dropped. The proof must + /// discharge `ens` for an arbitrary result — if it still elaborates, the `ens` /// said nothing about what the body computes (a tautology, reject). The Lean /// counterpart of `vacuity_solver.rs::build_tautology_harness`'s arbitrary /// `result` proof-fn binder. @@ -2131,18 +2131,18 @@ pub(crate) enum ResultMode { /// Build the arbitrary-result re-elaboration tautology harness for an item's /// obligation (REQ-6a / AC-10, increment 2d — anti-Goodhart defense (a)). The L3 -/// counterpart of `vacuity_solver.rs::build_tautology_harness`: it reuses the EXACT -/// same exporter machinery as the real obligation (`export_item_with_mode`) — same +/// counterpart of `vacuity_solver.rs::build_tautology_harness`: it reuses the exact +/// same exporter machinery as the obligation (`export_item_with_mode`) — same /// preamble, same `R_item` registry, same byte-identical `req`/`ens` encoding, same -/// auto-tactic battery and tier — and changes ONLY the `result` binding, from the +/// auto-tactic battery and tier — and changes only the `result` binding, from the /// body's stabilized value to a fresh universally-quantified `(r : Int)`. Driving the /// existing discharge path (no new elaborator, per the substrate note), the produced -/// source is fed to lake exactly as a normal obligation: if it kernel-accepts, the +/// source is fed to lake as a normal obligation: if it kernel-accepts, the /// `ens` holds for an arbitrary result, so the contract is a body-ignoring tautology -/// (reject); if it fails, the `ens` genuinely constrains the result (clean — the safe +/// (reject); if it fails, the `ens` constrains the result (clean — the safe /// completeness direction, mirroring the Verus harness polarity). Returns the same /// `ExportRefusal` skips as `export_item` (a non-pure-contract / non-auto item is an -/// honest skip — the arbitrary-result harness is the pure-contract auto path only in +/// skip — the arbitrary-result harness is the pure-contract auto path only in /// increment 2d; the straight-line/while-body result-substitution is a residual). pub fn export_arbitrary_result_harness( obligation: &Obligation, @@ -2155,21 +2155,21 @@ pub fn export_arbitrary_result_harness( /// Export a forge-tier `lemma` to a self-contained Lean file for discharge /// (`.design/stage1-forge-tier.md` REQ-7, increment 2e). A lemma states /// `∀ params, req → (ens₁ ∧ … ∧ ensₙ)` — a pure proposition over its typed params, -/// with NO body to stabilize (unlike a fn-contract obligation). The emitted theorem -/// reuses the EXACT fn-contract tier-(a) framing MINUS the body/result binding: the +/// with no body to stabilize (unlike a fn-contract obligation). The emitted theorem +/// reuses the exact fn-contract tier-(a) framing MINUS the body/result binding: the /// params are free vars in `v : Thermite.Env`, `req`/`ens` are encoded by the same /// [`encode_expr`] and denoted by `Thermite.denote 0 … { v with specs := R_item }`, so /// the u64 overflow semantics are modeled FAITHFULLY (a naive ℕ/ℤ encoding would be /// unsound — it would prove a claim false under wrapping). The proof is the author's /// frozen-battery `tactics` (the proof block text). The theorem name is the canonical /// `thermite_obligation_` the certify-time axiom-gate probe anchors on -/// (the same `sanitize` the fn path uses), so a lemma discharge runs the SAME axiom +/// (the same `sanitize` the fn path uses), so a lemma discharge runs the same axiom /// gate ([`crate::engine::certify_lean_axioms`]) as every other Lean path. /// /// `called` is the full-expression-position spec-fn closure of `req ∪ ens` (computed by /// the caller with the same closure the fn path uses); `build_registry` populates /// `R_item` from it and hard-refuses an incomplete registry. An out-of-fragment -/// `req`/`ens` construct is an honest [`ExportRefusal`] (a skip, never a partial file). +/// `req`/`ens` construct is an [`ExportRefusal`] (a skip, never a partial file). pub fn export_lemma( l: &thermite_syntax::LemmaItem, called: &[String], @@ -2180,7 +2180,7 @@ pub fn export_lemma( let ctx = ctx_for_params(&l.params); // Encode req + each ens via the same machinery the fn-contract path uses. An - // out-of-fragment construct refuses HERE (before emitting), an honest skip. + // out-of-fragment construct refuses here (before emitting), a skip. let req_term = encode_expr(&l.req.expr, &ctx)?; let ens_terms = l .ens @@ -2224,7 +2224,7 @@ pub fn export_lemma( Ok(ExportedObligation { source, // The lemma's proof is the author's frozen-battery tactics (a reviewed step, not - // the auto battery), so the cert carries the INTERACTIVE trust profile; the tier + // the auto battery), so the cert carries the interactive trust profile; the tier // here is metadata (the lemma path runs lake directly via `discharge_source`, not // the tier-gated `discharge`). FuelFreeAuto marks the shallow denote goal. tier: ExportTier::FuelFreeAuto, @@ -2275,7 +2275,7 @@ fn export_item_with_mode( // The arbitrary-result harness (REQ-6a) is the pure-contract auto path // only in increment 2d: a straight-line/while-body item binds `result` // through `bodyDenote`/`stateOf`, whose result-substitution is a - // residual. An honest skip (OutOfFragment) rather than a body-denotation + // residual. A skip (OutOfFragment) rather than a body-denotation // export — the tautology check reports "untested" for these, never a // false clean (mirrors the mutation battery's untested-against-lean). if result_mode == ResultMode::Arbitrary { @@ -2738,7 +2738,7 @@ fn export_straight_line_body( /// statement is a `while` loop routes to the while-body exporter; everything else (a /// pure tail, a straight-line statement body, a `loop`-kind / non-last loop) goes to /// the straight-line exporter, which refuses any loop via `LoopBody`. The full v1 -/// recognition (the OUT classes) is in [`recognize_while_body`]; this is the cheap +/// recognition (the out classes) is in [`recognize_while_body`]; this is the cheap /// router so a `loop`-kind in last position is still recognized + refused with its /// named reason (rather than slipping to the straight-line exporter's generic refusal). fn block_has_trailing_while(block: &Block) -> bool { @@ -3554,9 +3554,9 @@ fn emit_while_theorems(ctx: WhileTheoremCtx<'_>) -> String { "{cellval_block}\n\n\ /-- The straight-line PREFIX block (§4.2.1 — a tail-less `Block`). -/\n\ def prefix_block : Thermite.Exec.Block := {prefix_block_term}\n\n\ - /-- The loop CONDITION (`while `). -/\n\ + /-- The loop condition (`while `). -/\n\ def loop_cond : Thermite.Exec.ExecExpr := {loop_cond_term}\n\n\ - /-- The straight-line SCALAR loop BODY (`Stmt::Loop.body`). -/\n\ + /-- The straight-line SCALAR loop body (`Stmt::Loop.body`). -/\n\ def loop_block : Thermite.Exec.Block := {loop_block_term}\n\n\ /-- The tail value `ExecExpr` (the result at the exit state). -/\n\ def tail_expr : Thermite.Exec.ExecExpr := {tail_term}\n\n\ @@ -3595,7 +3595,7 @@ fn emit_while_theorems(ctx: WhileTheoremCtx<'_>) -> String { (Thermite.Exec.blockThread loop_block st).isSome) := by\n\ {progress_proof}\n\n\ set_option maxHeartbeats 1000000 in\n\ - /-- {thm_name}_dec (TERMINATION, §4.2.4): strict bounded-below descent of\n \ + /-- {thm_name}_dec (termination, §4.2.4): strict bounded-below descent of\n \ `mu_item` (`loopDenote_exits_of_dec`'s `h_dec`, the #265 PRE-state shape). -/\n\ theorem {thm_name}_dec (v : Thermite.Env) :\n \ ∀ st st', Inv_item v st → Thermite.Exec.condBool loop_cond st = some true →\n \ @@ -3614,7 +3614,7 @@ fn emit_while_theorems(ctx: WhileTheoremCtx<'_>) -> String { (Thermite.Exec.bindResult ({{ v with specs := R_item }} : Thermite.Env) r) := by\n\ {exit_proof}\n\n\ set_option maxHeartbeats 1000000 in\n\ - /-- {thm_name} (the HYPOTHESIZE CONTRACT theorem, §4.2.4) — GENERATOR-PROVED via\n \ + /-- {thm_name} (the HYPOTHESIZE contract theorem, §4.2.4) — GENERATOR-proved via\n \ `Thermite.Exec.while_compose` (_entry + _pres) + _exit. -/\n\ theorem {thm_name} (v : Thermite.Env) :\n \ ({in_range_pred}) →\n \ @@ -4430,19 +4430,19 @@ mod tests { } // REQ-6a / AC-10 (increment 2d, anti-Goodhart defense (a)): the arbitrary-result - // re-elaboration harness reuses the EXACT exporter machinery and changes ONLY the + // re-elaboration harness reuses the exact exporter machinery and changes only the // `result` binding — a fresh `(r : Int)` theorem binder instead of the body's // stabilized value. A pure, no-lake structural test (the lake elaboration leg is the // live `engine::tests::live_arbitrary_result_*` tests): the harness theorem gains the // `(r : Int)` binder, binds `result` to `r`, drops the `intVal 0 ` denotation, - // and keeps the byte-identical `req`/`ens`/`R_item` of the real obligation. + // and keeps the byte-identical `req`/`ens`/`R_item` of the obligation. #[test] fn arbitrary_result_harness_binds_result_to_a_fresh_binder() { let p = parse_one("fn f(x: u32) -> u32 req x > 0 ens result == x + 1 fx pure { x + 1 }"); let o = fn_obl(&p, "f", vec![]); let item = find_item(&p, "f").expect("fn f present"); - // The real obligation binds `result` to the body's stabilized value. + // The obligation binds `result` to the body's stabilized value. let real = export_item(&o, &p, item).expect("real obligation exports"); assert!( real.source.contains("Thermite.intVal 0"), @@ -4468,7 +4468,7 @@ mod tests { "the harness DROPS the body denotation (result is arbitrary):\n{}", harness.source ); - // The contract is byte-identical to the real obligation: same `R_item`, same + // The contract is byte-identical to the obligation: same `R_item`, same // `ens` term (only the result binding differs). The `ens` `result == x + 1` // encodes the same way in both. assert_eq!( diff --git a/forge/src/lean_smt_export.rs b/forge/src/lean_smt_export.rs index 0b9afe9e3..0b382bf36 100644 --- a/forge/src/lean_smt_export.rs +++ b/forge/src/lean_smt_export.rs @@ -1,5 +1,4 @@ -//! `forge/src/lean_smt_export.rs` — the automated Rust→Lean obligation exporter -//! for the `smt`-tactic (cvc5-reconstruction) discharge path +//! Automated Rust→Lean export for per-clause translation-validation obligations //! (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8). //! //! This module closes the Tier-3 hand-translation gap that @@ -7,9 +6,11 @@ //! automated Rust→Lean exporter would close"). Given a per-clause equivalence //! obligation — the translation-validation shape `(P_production) ⟺ (P_reference)` //! that `thermite-tv/src/obligation.rs::equivalence_obligation` asserts — it renders -//! BOTH predicate ASTs into Lean `Prop`s over a typed env and emits a self-contained -//! Lean theorem discharged by the lean-smt `smt` tactic (pinned @ `7d1d8239`, -//! `lean/lakefile.toml`, vendored cvc5 over FFI), followed by a `#print axioms` probe. +//! both predicate ASTs into Lean `Prop`s over a typed environment and emits a +//! self-contained theorem followed by a `#print axioms` probe. QF_LIA uses the +//! lean-smt `smt` tactic (pinned @ `ee6d36b`, with cvc5 over FFI). QF_BV is rendered +//! as literal `BitVec N` expressions and its normalization equivalence is proved with +//! kernel-checked library lemmas. //! A theorem whose `#print axioms` stays within `{propext, Classical.choice, //! Quot.sound}` (no `Smt`-internal oracle, no `sorryAx`, no `Lean.ofReduceBool`) is //! a kernel-checked reconstruction — that axiom-cleanliness is what makes a fragment @@ -21,51 +22,22 @@ //! linear integer arithmetic over `Int`. This is the PoC-proven shape //! (`SmtDemo.lean`'s Tier-2/Tier-3 theorems), the contract sublanguage //! `gen_comparison`/`gen_int` space. Rendered directly over Lean `Int`. -//! - **QF_BV** ([`SmtFragment::Bv`]): the fixed-width machine-semantics clauses -//! [`crate::bitvector::BitVectorEngine`] produces (REQ-2). Rendered over the -//! **range-bounded integer machine-model** rather than Lean `BitVec N`. See the -//! decision note below. +//! - **QF_BV** ([`SmtFragment::Bv`]): the full fixed-width term fragment used by +//! [`crate::bitvector::BitVectorEngine`] (REQ-2), rendered directly over Lean +//! `BitVec N`. //! -//! ## Why QF_BV is rendered over a bounded-integer model, not `BitVec N` +//! ## Literal QF_BV reconstruction //! -//! `.design/verified/z3-demotion.md` records (and this increment empirically -//! reconfirmed at the pinned lean-smt rev) that lean-smt's QF_BV proof -//! reconstruction bit-blasts through `Smt/Reconstruct/BitVec/Bitblast.lean`, which -//! itself `uses 'sorry'`. Every `BitVec`-typed `by smt` goal therefore pulls -//! `sorryAx` into its `#print axioms` — it is NOT kernel-clean. (Even a pure -//! unsigned-comparison goal over `BitVec N` bit-blasts and routes through the hole.) +//! The exported theorem is an equivalence between the production predicate and +//! [`reference_normalize`]. That normalization uses only total-order duals, +//! `≠` expansion, and commutativity of addition and multiplication. Lean proves +//! those facts directly with `simp`, including when they occur below bitwise, shift, +//! division, or remainder operations. This covers the complete QF_BV term surface +//! without asking lean-smt to reconstruct cvc5's bit-blasting proof and without adding +//! `bv_decide`'s native-reflection axiom. //! -//! A `@bvN` clause's fixed-width semantics are faithfully captured over `Int` by the -//! standard machine-model: each `N`-bit variable `x` is an integer with -//! `0 ≤ x < 2^N`, each wrapping arithmetic operation `a ⊕ b` is `(a ⊕ b) % 2^N` -//! (Lean's `Int.emod` lands in `[0, 2^N)` for the positive modulus, matching -//! `bvadd`/`bvsub`/`bvmul`), and each unsigned comparison is the integer comparison -//! on the bounded operands. This goal is QF_LIA — exactly the fragment lean-smt -//! reconstructs kernel-clean (the proven path) — so a `@bv` clause obligation rendered -//! this way discharges within `{propext, Classical.choice, Quot.sound}`. The literal -//! `BitVec N` render (`crate::bitvector::render_bv_prop`, the SMT-LIB2 query -//! `--engine bv` runs) is the artifact lean-smt's literal QF_BV replay would consume, -//! and remains blocked on the upstream bit-blasting `sorry`. -//! -//! **Faithfulness of the integer model is kernel-proven** (`lean/Thermite/BvModel.lean`, -//! issue #356): the term/proposition fragment [`render_term`] / [`render_prop`] emit here -//! corresponds arm-for-arm to `Thermite.BvModel.{Tm, Frm}`, and the metatheorem -//! `frmInt_iff_frmBV` proves the bounded-integer denotation agrees with the genuine -//! `BitVec N` denotation (`#print axioms` ⊆ the standard set, Mathlib/Smt-free). So with -//! the exporter's `by smt` int-model `↔` reconstructed in the kernel AND that faithfulness -//! theorem, a `@bv` clause's truth is kernel-grounded end to end — discharging the REQ-8 -//! `render_bv_prop` faithfulness obligation in our own spine, with no solver in the trust -//! base for the renderable fragment and no dependency on lean-smt's literal bv -//! reconstruction. The Rust-emitter ⟷ Lean-AST correspondence is inspection-tier (as for -//! the whole exporter — `.design/verified/exporter-surface-correspondence.md`). -//! -//! ## Out of the renderable fragment -//! -//! Bitwise / shift operators (`&`/`|`/`^`/`<<`/`>>`) and integer division/remainder -//! are an honest [`SmtExportError::OutOfFragment`] skip — they are the bit-blasting -//! / division-by-zero territory the integer model does not faithfully and cleanly -//! capture (the `z3-demotion.md` bitwise wall). A skip is never a silent wrong -//! encoding; it names the offending construct. +//! The Rust-emitter ↔ Lean-syntax correspondence remains inspection-tier, as described +//! in `.design/verified/exporter-surface-correspondence.md`. use std::collections::BTreeSet; @@ -79,19 +51,17 @@ pub enum SmtFragment { /// QF_LIA scalar: comparisons + boolean connectives + linear arithmetic over /// `Int` (the `SmtDemo.lean` PoC shape). Lia, - /// QF_BV at a fixed width, rendered over the range-bounded integer machine-model - /// (`0 ≤ x < 2^N` per variable, wrapping arithmetic as `% 2^N`). + /// QF_BV at a fixed width, rendered directly over `BitVec N`. Bv(BvWidth), } /// An out-of-fragment refusal (`.design/stage3-bv-reconstruction.md` REQ-7). Mirrors -/// the honest-skip discipline of [`crate::bitvector::render_bv_prop`]: a construct +/// the skip discipline of [`crate::bitvector::render_bv_prop`]: a construct /// outside the renderable fragment is named, never silently mis-encoded. #[derive(Debug, Clone, PartialEq, Eq)] pub enum SmtExportError { - /// An `Expr` construct outside the renderable QF_LIA / QF_BV fragment (a bitwise - /// or shift operator, division/remainder, a multi-segment path, a method call, - /// an arithmetic operator at proposition position, …). Carries a human + /// An `Expr` construct outside the selected fragment, such as a multi-segment + /// path, method call, or arithmetic operator at proposition position. Carries a /// description of the offending construct. OutOfFragment(String), } @@ -110,13 +80,12 @@ impl std::fmt::Display for SmtExportError { /// (`.design/stage3-bv-reconstruction.md` REQ-7). The exported theorem is the TV /// shape `(P_production) ⟺ (P_reference)` over the obligation's free variables — the /// same logical content `thermite-tv`'s `equivalence_obligation` discharges through -/// Verus/Z3, here discharged by the kernel-checked `smt` tactic. +/// Verus/Z3, here discharged by a kernel-checked Lean proof. #[derive(Debug, Clone, PartialEq, Eq)] pub struct SmtEquivObligation { /// The item / clause name the theorem is named after (sanitized to a Lean ident). pub item: String, - /// The free variables, in binder order. All are rendered at the fragment's sort - /// (`Int` for both QF_LIA and the QF_BV integer machine-model). + /// The free variables, in binder order. QF_LIA uses `Int`; QF_BV uses `BitVec N`. pub vars: Vec, /// The production-lowered predicate (the artifact under test). pub prod: Expr, @@ -153,55 +122,73 @@ fn lean_ident(item: &str) -> String { out } -/// Render an arithmetic TERM to a Lean `Int` expression in the given fragment -/// (`.design/stage3-bv-reconstruction.md` REQ-7). In [`SmtFragment::Lia`] the -/// operators are the plain integer operators; in [`SmtFragment::Bv`] each wrapping -/// operation is reduced `% 2^N` to model fixed-width wraparound and a literal is -/// reduced into `[0, 2^N)`. +/// Render an arithmetic or bitwise term in the selected Lean fragment. fn render_term(e: &Expr, fragment: SmtFragment) -> Result { match e { Expr::IntLit { value, .. } => match fragment { SmtFragment::Lia => Ok(format!("({value} : Int)")), - SmtFragment::Bv(w) => Ok(format!("({} : Int)", value % modulus(w.bits()))), + SmtFragment::Bv(w) => Ok(format!("({}#{})", value % modulus(w.bits()), w.bits())), }, Expr::Path(segs) if segs.len() == 1 => Ok(segs[0].clone()), Expr::Binary { op, lhs, rhs } => { - let sym = match op { - BinOp::Add => "+", - BinOp::Sub => "-", - BinOp::Mul => "*", - other => { + let sym = match (fragment, op) { + (_, BinOp::Add) => "+", + (_, BinOp::Sub) => "-", + (_, BinOp::Mul) => "*", + (SmtFragment::Bv(_), BinOp::Div) => "/", + (SmtFragment::Bv(_), BinOp::Rem) => "%", + (SmtFragment::Bv(_), BinOp::Shl) => "<<<", + (SmtFragment::Bv(_), BinOp::Shr) => ">>>", + (SmtFragment::Bv(_), BinOp::BitAnd) => "&&&", + (SmtFragment::Bv(_), BinOp::BitOr) => "|||", + (SmtFragment::Bv(_), BinOp::BitXor) => "^^^", + (_, other) => { + let supported = match fragment { + SmtFragment::Lia => "`+`, `-`, or `*`", + SmtFragment::Bv(_) => { + "arithmetic, division, remainder, bitwise, or shift operators" + } + }; return Err(SmtExportError::OutOfFragment(format!( - "`{other:?}` is not a renderable arithmetic term operator (only \ - `+`/`-`/`*` lower to the integer model; `/`/`%`/`&`/`|`/`^`/`<<`/`>>` \ - are the bit-blasting / division residual)" - ))) + "`{other:?}` is not a term operator in this fragment; expected {supported}" + ))); } }; let l = render_term(lhs, fragment)?; let r = render_term(rhs, fragment)?; - match fragment { - SmtFragment::Lia => Ok(format!("({l} {sym} {r})")), - // Model fixed-width wraparound: the N-bit operation is the integer - // operation reduced modulo 2^N (Lean `Int.emod` lands in [0, 2^N)). - SmtFragment::Bv(w) => Ok(format!("(({l} {sym} {r}) % {})", modulus(w.bits()))), + if matches!((fragment, op), (SmtFragment::Bv(_), BinOp::Div)) { + let SmtFragment::Bv(w) = fragment else { + unreachable!("the branch fixed the fragment") + }; + // SMT-LIB defines bvudiv-by-zero as all ones, while Lean's BitVec + // division returns zero. Spell out the SMT case so the two renderers + // agree even before Thermite's nonzero-divisor obligation is applied. + let zero = format!("(0#{})", w.bits()); + Ok(format!( + "(if {r} = {zero} then (~~~{zero}) else ({l} / {r}))" + )) + } else { + Ok(format!("({l} {sym} {r})")) } } - // A cast leaves the integer value unchanged in the model (the spine view; the - // `crate::bitvector::render_bv_term` precedent renders the inner term). + Expr::Unary { + op: UnaryOp::Not, + expr, + } if matches!(fragment, SmtFragment::Bv(_)) => { + Ok(format!("(~~~{})", render_term(expr, fragment)?)) + } + // Casts in this fragment preserve the selected fixed-width representation. Expr::Cast { expr, .. } => render_term(expr, fragment), other => Err(SmtExportError::OutOfFragment(format!( "`{other:?}` is outside the renderable term fragment (only integer \ - literals, single-segment variables, `+`/`-`/`*`, and casts)" + literals, single-segment variables, fragment operators, and casts)" ))), } } -/// Render a PROPOSITION to a Lean `Prop` in the given fragment -/// (`.design/stage3-bv-reconstruction.md` REQ-7). Comparisons map to the integer -/// relations (faithful for the unsigned bit-vector relations on the bounded -/// machine-model operands), the connectives to `∧`/`∨`/`¬`. `Err` names the -/// out-of-fragment construct. +/// Render a proposition to a Lean `Prop` in the given fragment +/// (`.design/stage3-bv-reconstruction.md` REQ-7). `BitVec` comparisons are unsigned, +/// matching the scalar types and the SMT-LIB QF_BV renderer. fn render_prop(e: &Expr, fragment: SmtFragment) -> Result { match e { Expr::BoolLit(b) => Ok(if *b { "True" } else { "False" }.to_string()), @@ -257,47 +244,71 @@ pub fn render_goal(o: &SmtEquivObligation) -> Result { Ok(format!("{prod} ↔ {reference}")) } -/// Export one obligation as a self-contained Lean theorem discharged by `smt`, -/// followed by its `#print axioms` probe (`.design/stage3-bv-reconstruction.md` +/// Lemmas needed for the commutative rewrites performed by [`reference_normalize`]. +/// Order and comparison rewrites are already handled by `simp`. +fn bv_normalization_lemmas(e: &Expr) -> Vec<&'static str> { + fn walk(e: &Expr, add: &mut bool, mul: &mut bool) { + match e { + Expr::Binary { op, lhs, rhs } => { + *add |= *op == BinOp::Add; + *mul |= *op == BinOp::Mul; + walk(lhs, add, mul); + walk(rhs, add, mul); + } + Expr::Unary { expr, .. } | Expr::Cast { expr, .. } => walk(expr, add, mul), + _ => {} + } + } + + let mut add = false; + let mut mul = false; + walk(e, &mut add, &mut mul); + let mut lemmas = Vec::new(); + if add { + lemmas.push("BitVec.add_comm"); + } + if mul { + lemmas.push("BitVec.mul_comm"); + } + lemmas +} + +/// Export one obligation as a self-contained Lean theorem followed by its +/// `#print axioms` probe (`.design/stage3-bv-reconstruction.md` /// REQ-7 / AC-8). The theorem is named `thermite_smt_`. /// /// - [`SmtFragment::Lia`]: `theorem T (a b … : Int) : (P_prod) ↔ (P_ref) := by smt`. -/// - [`SmtFragment::Bv`]: each variable additionally carries its machine-domain -/// range hypotheses `0 ≤ x` / `x < 2^N`, passed to `smt` so the bounded-integer -/// model is sound. `theorem T (a … : Int) (h0lo : 0 ≤ a) (h0hi : a < 2^N) … : -/// (P_prod) ↔ (P_ref) := by smt [h0lo, h0hi, …]`. +/// - [`SmtFragment::Bv`]: `theorem T (a b … : BitVec N) : +/// (P_prod) ↔ (P_ref) := by simp [BitVec.add_comm, BitVec.mul_comm]`. pub fn export_theorem(o: &SmtEquivObligation) -> Result { let name = format!("thermite_smt_{}", lean_ident(&o.item)); let goal = render_goal(o)?; - let var_binder = if o.vars.is_empty() { - String::new() - } else { - format!(" ({} : Int)", o.vars.join(" ")) - }; - match o.fragment { - SmtFragment::Lia => Ok(format!( - "theorem {name}{var_binder} :\n {goal} := by smt\n#print axioms {name}\n" - )), + SmtFragment::Lia => { + let binder = if o.vars.is_empty() { + String::new() + } else { + format!(" ({} : Int)", o.vars.join(" ")) + }; + Ok(format!( + "theorem {name}{binder} :\n {goal} := by smt\n#print axioms {name}\n" + )) + } SmtFragment::Bv(w) => { - let m = modulus(w.bits()); - let mut hyp_binders = String::new(); - let mut hyp_names: Vec = Vec::new(); - for (i, v) in o.vars.iter().enumerate() { - let lo = format!("h{i}lo"); - let hi = format!("h{i}hi"); - hyp_binders.push_str(&format!(" ({lo} : 0 ≤ {v}) ({hi} : {v} < {m})")); - hyp_names.push(lo); - hyp_names.push(hi); - } - let tactic = if hyp_names.is_empty() { - "by smt".to_string() + let binder = if o.vars.is_empty() { + String::new() + } else { + format!(" ({} : BitVec {})", o.vars.join(" "), w.bits()) + }; + let lemmas = bv_normalization_lemmas(&o.prod); + let tactic = if lemmas.is_empty() { + "by\n simp".to_string() } else { - format!("by smt [{}]", hyp_names.join(", ")) + format!("by\n simp [{}]", lemmas.join(", ")) }; Ok(format!( - "theorem {name}{var_binder}{hyp_binders} :\n {goal} := {tactic}\n#print axioms {name}\n" + "theorem {name}{binder} :\n {goal} := {tactic}\n#print axioms {name}\n" )) } } @@ -313,18 +324,14 @@ const FILE_HEADER: &str = "\ (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8). DO NOT EDIT BY HAND. Each theorem is a per-clause translation-validation obligation `(P_prod) ⟺ (P_ref)` - emitted by the automated Rust→Lean exporter — the step `Thermite.SmtDemo` performed - by hand. It is discharged by the lean-smt `smt` tactic (cvc5 reconstruction, pinned - @ 7d1d8239) and KERNEL-CHECKED; the `#print axioms` after each must report a subset - of {propext, Classical.choice, Quot.sound} (no Smt oracle, no sorryAx, no - Lean.ofReduceBool) for the fragment to count as reconstruction-supported. - - QF_BV clauses are rendered over the range-bounded integer machine-model (bv var -> - Int with 0 ≤ x < 2^N, wraparound op -> `% 2^N`, unsigned cmp -> Int cmp): lean-smt's - literal BitVec reconstruction bit-blasts through an upstream `sorry` - (.design/verified/z3-demotion.md), so the integer model is the reconstruction- - supported QF_BV encoding. Regenerate via the `golden_file_matches_exporter` test - with THERMITE_REGEN_SMT_EXPORT=1. + emitted by the automated Rust→Lean exporter. QF_LIA uses lean-smt/cvc5. QF_BV is + rendered directly as `BitVec N` and proved from kernel-checked normalization lemmas. + The `#print axioms` after each theorem must report a subset of + {propext, Classical.choice, Quot.sound}. + + The literal QF_BV renderer covers wrapping arithmetic, unsigned comparisons, + bitwise operations, shifts, unsigned division, and remainder. Regenerate via the + `golden_file_matches_exporter` test with THERMITE_REGEN_SMT_EXPORT=1. -/ import Smt @@ -377,9 +384,8 @@ fn not_expr(e: Expr) -> Expr { /// `thermite-tv/src/ref_encode.rs` plays for the Verus obligation: the second, /// independent rendering whose agreement with production the translation-validation /// obligation `(P_prod) ⟺ (P_ref)` checks. Each rewrite is equivalence-preserving -/// over BOTH the QF_LIA `Int` model and the QF_BV bounded-integer machine-model -/// (the unsigned comparison flips hold for a total order; `+`/`*` commute for both -/// `Int` and the wraparound `% 2^N` operations): +/// over QF_LIA `Int` and QF_BV `BitVec N`: the comparison flips hold for their total +/// orders, while addition and multiplication commute in both representations. /// /// - `a ≤ b` → `¬ (b < a)`, `a < b` → `¬ (b ≤ a)` (the comparison-faithfulness flip); /// - `a ≥ b` → `b ≤ a`, `a > b` → `b < a`; @@ -388,7 +394,7 @@ fn not_expr(e: Expr) -> Expr { /// - `=`/`∧`/`∨`/`¬`/`-`/… recurse into normalized children, operator kept. /// /// Deterministic, total (R-CODE-5): a leaf or an unhandled construct is returned -/// unchanged, so a clause the renderer later refuses is refused honestly downstream, +/// unchanged, so a clause the renderer later refuses is refused downstream, /// not mangled here. #[must_use] pub fn reference_normalize(e: &Expr) -> Expr { @@ -410,6 +416,10 @@ pub fn reference_normalize(e: &Expr) -> Expr { op: UnaryOp::Not, expr, } => not_expr(reference_normalize(expr)), + Expr::Cast { expr, ty } => Expr::Cast { + expr: Box::new(reference_normalize(expr)), + ty: ty.clone(), + }, other => other.clone(), } } @@ -437,25 +447,17 @@ pub fn free_vars(e: &Expr) -> Vec { acc.into_iter().collect() } -/// Is a per-clause predicate inside the RECONSTRUCTION-SUPPORTED fragment +/// Is a per-clause predicate inside the RECONSTRUCTION-supported fragment /// (`.design/stage3-bv-reconstruction.md` REQ-8 / AC-9 — the fragment-support check /// REQ-8's trust migration keys on)? `true` iff the exporter renders this clause's -/// `(P_prod) ⟺ (P_ref)` translation-validation goal WITHOUT [`SmtExportError`] in the -/// given fragment — i.e. exactly the QF_LIA scalar + arithmetic/comparison QF_BV subset -/// (`+`/`-`/`*`, unsigned `=`/`≠`/`<`/`≤`/`>`/`≥`, logical connectives). The -/// bitwise/shift/rotate QF_BV subset (`^`/`&`/`|`/`<<`/`>>`, rotate) and -/// division/remainder are the [`SmtExportError::OutOfFragment`] refusal — the -/// bit-blasting wall (`.design/verified/z3-demotion.md`) — and read `false` here, so -/// those clauses stay solver-trusted (the F-J residual the audit names). +/// `(P_prod) ⟺ (P_ref)` translation-validation goal without [`SmtExportError`] in the +/// given fragment. QF_LIA covers its scalar arithmetic subset. QF_BV covers the full +/// fixed-width term surface: wrapping arithmetic, bitwise operators, shifts, unsigned +/// division and remainder, comparisons, and logical connectives. /// /// Renderability IS the reconstruction-support signal: a clause this returns `true` for -/// is rendered over the bounded-integer machine-model that lean-smt reconstructs -/// kernel-clean (`#print axioms ⊆ {propext, Classical.choice, Quot.sound}`, the AC-8 -/// committed `lean/Thermite/SmtExport.lean` proof + the kernel-checked -/// `BvModel.frmInt_iff_frmBV` faithfulness metatheorem) — so the renderable fragment's -/// axiom-cleanliness is discharged by REQ-7 once, statically, not re-run per clause. -/// Total + deterministic (R-CODE-5): a leaf the renderer would refuse is refused here -/// too, never a silent claim of support. +/// has an axiom-clean proof shape in the committed `lean/Thermite/SmtExport.lean`. +/// Total + deterministic (R-CODE-5): a leaf the renderer would refuse is refused here. #[must_use] pub fn clause_reconstruction_supported(prod: &Expr, fragment: SmtFragment) -> bool { render_goal(&obligation_for_predicate("clause", prod, fragment)).is_ok() @@ -482,8 +484,9 @@ pub fn obligation_for_predicate( /// The canonical reconstruction-supported obligation set /// (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8) — the batch the committed /// `lean/Thermite/SmtExport.lean` is generated from. One QF_LIA scalar clause and two -/// QF_BV `@bv` clauses (a comparison subfragment clause at bv64, a modular-arithmetic -/// clause at bv8), each paired with its [`reference_normalize`] reference. The `@bv` +/// QF_BV `@bv` clauses covering comparison, wrapping arithmetic, and the complete +/// bitwise/shift/division term surface, each paired with its [`reference_normalize`] +/// reference. The `@bv` /// fragments are assigned explicitly (not parsed from a `@bvN` tag) so this set is /// available in the default build, where the `bv` parse feature is off (REQ-1's /// structural lock). @@ -495,11 +498,39 @@ pub fn reconstruction_demo_obligations() -> Vec { let bv_cmp = bin(BinOp::Le, var("a"), var("b")); // QF_BV modular arithmetic (bv8): `a + b == c`. let bv_arith = bin(BinOp::Eq, bin(BinOp::Add, var("a"), var("b")), var("c")); + // QF_BV full term surface (bv8). The nested multiply is commuted in the + // independent reference encoding, so this is not a reflexivity-only fixture. + let bv_full = bin( + BinOp::Ne, + bin( + BinOp::Rem, + bin( + BinOp::Div, + bin( + BinOp::Shr, + bin( + BinOp::Shl, + bin( + BinOp::BitOr, + bin(BinOp::BitAnd, not_expr(var("a")), var("b")), + bin(BinOp::BitXor, var("a"), var("b")), + ), + var("c"), + ), + var("b"), + ), + var("c"), + ), + var("b"), + ), + bin(BinOp::Add, bin(BinOp::Mul, var("a"), var("b")), var("c")), + ); vec![ obligation_for_predicate("lia_arith_cmp", &lia, SmtFragment::Lia), obligation_for_predicate("bv64_le_not_lt", &bv_cmp, SmtFragment::Bv(BvWidth::W64)), obligation_for_predicate("bv8_add_comm", &bv_arith, SmtFragment::Bv(BvWidth::W8)), + obligation_for_predicate("bv8_full_terms", &bv_full, SmtFragment::Bv(BvWidth::W8)), ] } @@ -507,7 +538,7 @@ pub fn reconstruction_demo_obligations() -> Vec { /// parsed program (`.design/stage3-bv-reconstruction.md` REQ-7 — the file-driven /// exporter). A clause carrying a `@bvN` tag (only present in a `bv`-feature build) /// exports in [`SmtFragment::Bv`]; an untagged clause exports in [`SmtFragment::Lia`]. -/// A clause outside the renderable fragment is an honest skip, named in the returned +/// A clause outside the renderable fragment is a skip, named in the returned /// skip list (never a silent drop). Deterministic in source order (R-CODE-5). #[must_use] pub fn obligations_for_program(program: &Program) -> (Vec, Vec) { @@ -557,7 +588,7 @@ mod tests { } /// Extract the parsed `ens` predicate `Expr` of a fn `name` from `src`. The - /// demo obligations are built from REAL parsed Thermite predicates (not + /// demo obligations are built from real parsed Thermite predicates (not /// hand-built ASTs), so the exporter is exercised on the same `thermite-syntax` /// nodes the production obligation carries. fn ens_expr(src: &str, name: &str) -> Expr { @@ -598,27 +629,25 @@ mod tests { assert_eq!(reference_normalize(&var("x")), var("x")); } - // REQ-8 / AC-9: the per-clause fragment-support check keys on renderability — the - // arithmetic/comparison subset is reconstruction-supported (migrates trust), the - // bitwise/shift/rotate subset is refused (stays solver-trusted). + // REQ-8 / AC-9: the per-clause support check covers the complete QF_BV term + // surface, while QF_LIA retains its smaller scalar fragment. #[test] fn clause_reconstruction_supported_keys_on_the_renderable_fragment() { - // The mix64 split: `a + b == b + a` (arith) is supported; `a ^ b ^ b == a` (xor) is not. let add = ens_expr( "fn p(a: u64, b: u64) -> u64 req true ens a + b == b + a fx pure { a }", "p", ); assert!( clause_reconstruction_supported(&add, SmtFragment::Bv(BvWidth::W64)), - "wraparound-add commutativity is the reconstruction-supported QF_BV subset" + "wrapping arithmetic is reconstruction-supported" ); let xor = ens_expr( "fn p(a: u64, b: u64) -> u64 req true ens a ^ b ^ b == a fx pure { a }", "p", ); assert!( - !clause_reconstruction_supported(&xor, SmtFragment::Bv(BvWidth::W64)), - "xor is the bitwise subset the exporter refuses — stays solver-trusted (F-J)" + clause_reconstruction_supported(&xor, SmtFragment::Bv(BvWidth::W64)), + "bitwise terms are reconstructed literally" ); // A QF_LIA scalar comparison is supported too. let lia = ens_expr( @@ -626,19 +655,19 @@ mod tests { "p", ); assert!(clause_reconstruction_supported(&lia, SmtFragment::Lia)); - // A shift clause (the rotl1 lemma shape) is refused. + // Shift terms use a BitVec shift amount, matching SMT-LIB `bvshl`. let shift = ens_expr( "fn p(a: u64, b: u64) -> u64 req true ens (a << b) == a fx pure { a }", "p", ); - assert!(!clause_reconstruction_supported( + assert!(clause_reconstruction_supported( &shift, SmtFragment::Bv(BvWidth::W64) )); } // REQ-7: the file-driven exporter mints a QF_LIA obligation per renderable `ens` - // clause and honestly names a non-renderable (bitwise) clause in the skip list. + // clause and names a non-renderable (bitwise) clause in the skip list. #[test] fn program_export_skips_out_of_fragment_clauses() { let p = parse_one( @@ -684,69 +713,94 @@ mod tests { ); } - // REQ-7: the QF_BV integer machine-model reduces literals and wraps arithmetic - // `% 2^N` while comparisons stay integer comparisons on the bounded operands. + // REQ-7: QF_BV terms render directly as Lean `BitVec N` expressions. #[test] - fn bv_renders_modular_arithmetic() { + fn bv_renders_literal_bitvec_arithmetic() { let p = ens_expr( "fn p(a: u64, b: u64, c: u64) -> u64 req true ens a + b == c fx pure { a }", "p", ); assert_eq!( render_prop(&p, SmtFragment::Bv(BvWidth::W8)).unwrap(), - "(((a + b) % 256) = c)" + "((a + b) = c)" ); - // A literal is reduced into [0, 2^N): `300` at width 8 is `300 % 256 = 44`. + // Numeric syntax carries the width and reduces the value modulo 2^N. let lit = ens_expr( "fn p(a: u64) -> u64 req true ens a == 300 fx pure { a }", "p", ); assert_eq!( render_prop(&lit, SmtFragment::Bv(BvWidth::W8)).unwrap(), - "(a = (44 : Int))" + "(a = (44#8))" ); } - // REQ-7: bitwise / shift / division operators are an honest OutOfFragment skip - // (the bit-blasting / division residual), never a silent wrong encoding. + // REQ-7: the literal renderer covers every QF_BV term operator used by the + // production SMT-LIB renderer. #[test] - fn bitwise_and_division_are_out_of_fragment() { - for (src, clause) in [ + fn bv_renders_bitwise_shift_division_and_remainder() { + for (src, lean_op) in [ ( "fn p(a: u64, b: u64) -> u64 req true ens (a & b) == a fx pure { a }", - "&", + "&&&", ), ( "fn p(a: u64, b: u64) -> u64 req true ens (a | b) == a fx pure { a }", - "|", + "|||", ), ( "fn p(a: u64, b: u64) -> u64 req true ens (a ^ b) == a fx pure { a }", - "^", + "^^^", ), ( "fn p(a: u64, b: u64) -> u64 req true ens (a / b) == a fx pure { a }", "/", ), + ( + "fn p(a: u64, b: u64) -> u64 req true ens (a % b) == a fx pure { a }", + "%", + ), ( "fn p(a: u64, b: u64) -> u64 req true ens (a << b) == a fx pure { a }", - "<<", + "<<<", + ), + ( + "fn p(a: u64, b: u64) -> u64 req true ens (a >> b) == a fx pure { a }", + ">>>", ), ] { let p = ens_expr(src, "p"); + let rendered = render_prop(&p, SmtFragment::Bv(BvWidth::W64)) + .expect("the complete QF_BV term surface renders"); assert!( - matches!( - render_prop(&p, SmtFragment::Bv(BvWidth::W64)), - Err(SmtExportError::OutOfFragment(_)) - ), - "the `{clause}` operator must be an OutOfFragment skip" + rendered.contains(lean_op), + "expected Lean operator `{lean_op}` in {rendered}" ); } + + let not = ens_expr( + "fn p(a: u64) -> u64 req true ens !a == a fx pure { a }", + "p", + ); + assert_eq!( + render_prop(¬, SmtFragment::Bv(BvWidth::W8)).unwrap(), + "((~~~a) = a)" + ); + + let div = ens_expr( + "fn p(a: u64, b: u64) -> u64 req true ens (a / b) == a fx pure { a }", + "p", + ); + assert_eq!( + render_prop(&div, SmtFragment::Bv(BvWidth::W8)).unwrap(), + "((if b = (0#8) then (~~~(0#8)) else (a / b)) = a)", + "Lean and SMT-LIB use different bvudiv-by-zero defaults, so the zero case \ + must be explicit" + ); } - // REQ-7 / AC-8: the QF_LIA theorem shape is the SmtDemo `by smt` form with a - // `#print axioms` probe; the QF_BV theorem additionally carries the machine-domain - // range hypotheses, passed to `smt`. + // REQ-7 / AC-8: QF_LIA uses `smt`; QF_BV uses literal `BitVec N` binders and + // kernel-checked normalization lemmas. #[test] fn theorem_shapes_are_well_formed() { let obs = reconstruction_demo_obligations(); @@ -757,9 +811,12 @@ mod tests { assert!(lia.contains("#print axioms thermite_smt_lia_arith_cmp")); let bv = export_theorem(&obs[1]).unwrap(); - assert!(bv.contains("(h0lo : 0 ≤ a) (h0hi : a < 18446744073709551616)")); - assert!(bv.contains(":= by smt [h0lo, h0hi, h1lo, h1hi]")); + assert!(bv.contains("(a b : BitVec 64)")); + assert!(bv.contains(":= by\n simp\n")); assert!(bv.contains("#print axioms thermite_smt_bv64_le_not_lt")); + + let full = export_theorem(&obs[3]).unwrap(); + assert!(full.contains("simp [BitVec.add_comm, BitVec.mul_comm]")); } // REQ-7: the committed `lean/Thermite/SmtExport.lean` IS the exporter's automated @@ -874,8 +931,8 @@ mod tests { checked += 1; } assert_eq!( - checked, 3, - "all three demo obligations must be axiom-checked" + checked, 4, + "all four demo obligations must be axiom-checked" ); } } diff --git a/forge/src/lemma_library.rs b/forge/src/lemma_library.rs index 03cb2d90b..e1b9dabdf 100644 --- a/forge/src/lemma_library.rs +++ b/forge/src/lemma_library.rs @@ -1,36 +1,36 @@ //! `forge/src/lemma_library.rs` — the Stage-1 forge-tier **lemma library mechanics** -//! (`.design/stage1-forge-tier.md` REQ-9 / AC-13, increment 3; the LAST stage-1 -//! feature increment). This is the LOGIC the surface (2a's `lemma` item), the proof +//! (`.design/stage1-forge-tier.md` REQ-9 / AC-13, increment 3; the last stage-1 +//! feature increment). This is the logic the surface (2a's `lemma` item), the proof //! discharge (2e's `discharge_forge_lemma`), and the frozen battery (2c's //! `scan_citations`) set up for. REQ-9 trails usage — it is last in dependency order //! because it reasons about ALREADY-discharged lemma certificates. //! -//! Three mechanics, all per-project (Q1 — there is NO cross-project sharing; that is +//! Three mechanics, all per-project (Q1 — there is no cross-project sharing; that is //! deferred, see the spec's Out of Scope): //! //! 1. **Per-project lemma namespace** (Q1). A project's top-level `lemma` items form a -//! namespace ([`LemmaLibrary`]) keyed by name. Only a TOP-LEVEL [`LemmaItem`] enters +//! namespace ([`LemmaLibrary`]) keyed by name. Only a top-level [`LemmaItem`] enters //! the namespace — a proof-local `have`/`let` binding never does, so the namespace is -//! the ONLY cross-function citation surface (Q6: "no cross-function sharing except via +//! the only cross-function citation surface (Q6: "no cross-function sharing except via //! a lemma"). A `proof for f` block's several `ens#k` obligations share that one //! function's local context; a name they establish locally is invisible to `proof for //! g` (it is not in the namespace) — enforced structurally by building the namespace //! from top-level lemmas only. //! //! 2. **Certified-only citation resolution** (AC-13). A proof citing a project lemma -//! (`simp [melems_cons]`) resolves ONLY if that lemma carries a certificate. A -//! citation to a project lemma that did NOT certify is REFUSED — named — never a +//! (`simp [melems_cons]`) resolves only if that lemma carries a certificate. A +//! citation to a project lemma that did not certify is REFUSED — named — never a //! silent pass ([`enforce_citations`] → [`UncertifiedCitation`]). This is the //! proof-tier analogue of the frozen-battery refusal: the frozen battery refuses a //! citation outside the closed spine simp set; REQ-9 refuses a citation to an -//! uncertified PROJECT lemma. The two compose — a simp citation resolves against the +//! uncertified project lemma. The two compose — a simp citation resolves against the //! frozen battery OR a certified project lemma; anything else is refused. //! //! 3. **Dedup-on-burn by statement hash with citation rewrite** (Q1 / AC-13). Two -//! lemmas with the SAME statement (same params + `req` + `ens`) under DIFFERENT names -//! are ONE proven fact: only the first (the canonical) is stored, and a citation to -//! the duplicate is REWRITTEN to the canonical ([`LemmaLibrary::rewrite_citations`]). -//! The statement hash ([`statement_hash`]) excludes the lemma NAME and the PROOF, so +//! lemmas with the same statement (same params + `req` + `ens`) under different names +//! are one proven fact: only the first (the canonical) is stored, and a citation to +//! the duplicate is rewritten to the canonical ([`LemmaLibrary::rewrite_citations`]). +//! The statement hash ([`statement_hash`]) excludes the lemma NAME and the proof, so //! re-proving the same statement under a new name dedups to the existing lemma rather //! than storing a copy. //! @@ -49,9 +49,9 @@ use crate::manifest::{Certificate, Level}; /// cache's `DOMAIN` discipline, `cache.rs`). const DOMAIN: &[u8] = b"thermite.forge.lemma-library.v1"; -/// The content hash of a lemma's STATEMENT (REQ-9 / Q1) — a lowercase-hex sha256 over +/// The content hash of a lemma's statement (REQ-9 / Q1) — a lowercase-hex sha256 over /// the lemma's params + `req` + every `ens`, domain-tagged and length-prefixed (the -/// `cache::field` discipline). The lemma NAME and the PROOF are EXCLUDED: two lemmas +/// `cache::field` discipline). The lemma NAME and the proof are excluded: two lemmas /// stating the same proposition under different names (or proved by different tactics) /// share a statement hash — that is the dedup key (AC-13: "two identical lemmas under /// different names → one stored"). Pure and deterministic (R-CODE-5): the params are @@ -176,7 +176,7 @@ pub enum CitationResolution { /// The canonical stored lemma for this statement hash (the dedup target). canonical: String, }, - /// A known project lemma that did NOT certify — the citation is REFUSED, named + /// A known project lemma that did not certify — the citation is REFUSED, named /// (AC-13: "citing an uncertified lemma fails with the lemma named"). Uncertified, /// Neither a frozen lemma nor a project lemma — left to the frozen battery's @@ -191,7 +191,7 @@ pub enum CitationResolution { #[derive(Debug, Clone, Default)] pub struct LemmaLibrary { /// Every top-level project lemma, by name (Q1 namespace). A proof-local `have`/`let` - /// binding is NOT here — only a top-level `lemma` enters, so this is the sole + /// binding is not here — only a top-level `lemma` enters, so this is the sole /// cross-function citation surface (Q6). by_name: BTreeMap, /// statement-hash → the canonical (first certified, source order) lemma name. The @@ -203,7 +203,7 @@ impl LemmaLibrary { /// Build the per-project lemma namespace (REQ-9 / Q1) from the parsed `program` and /// the settled cert collection `certs`. A top-level `lemma` is `certified` iff `certs` /// carries an L3, non-rejected cert under its name (the discharge produced a proof). - /// The canonical for a statement hash is the FIRST certified lemma in SOURCE order + /// The canonical for a statement hash is the first certified lemma in source order /// (`program.items` is source-ordered), so dedup is deterministic. #[must_use] pub fn build(program: &Program, certs: &[Certificate]) -> Self { @@ -340,7 +340,7 @@ impl LemmaLibrary { } /// A REQ-9 certified-only citation refusal (AC-13): a proof cites a project lemma that -/// lacks a certificate. A HARD error, named — the proof-tier analogue of the frozen +/// lacks a certificate. A hard error, named — the proof-tier analogue of the frozen /// battery's [`crate::battery::BatteryViolation`] and the covenant refusal. #[derive(Debug, Clone, PartialEq, Eq)] pub struct UncertifiedCitation { @@ -373,12 +373,12 @@ impl UncertifiedCitation { } /// The REQ-9 certified-only citation gate (AC-13): scan a proof block's `simp [ … ]` -/// citations and refuse the FIRST one that names an UNCERTIFIED project lemma, naming it. +/// citations and refuse the first one that names an UNCERTIFIED project lemma, naming it. /// A citation to a frozen battery lemma or a certified project lemma resolves; an -/// `Unknown` citation is NOT this gate's concern (the frozen-battery gate refuses it as +/// `Unknown` citation is not this gate's concern (the frozen-battery gate refuses it as /// an unlisted simp lemma). `item` names the citing item for the refusal message. /// -/// This is the forge/Lean-path gate `check_file_with_engine` runs AFTER the lemma +/// This is the forge/Lean-path gate `check_file_with_engine` runs after the lemma /// discharge pass (so certification status is settled), beside the frozen battery's /// elaboration gate. pub fn enforce_citations( @@ -402,7 +402,7 @@ pub fn enforce_citations( Ok(()) } -/// The set of top-level project lemma NAMES in `program` (REQ-9 / Q1). Collected from +/// The set of top-level project lemma names in `program` (REQ-9 / Q1). Collected from /// the parsed program (no certs needed), so the frozen-battery elaboration gate /// ([`crate::battery::enforce_forge_item_with_lemmas`]) can DEFER a project-lemma /// citation (not refuse it as an unlisted simp lemma) — the certified-only resolution @@ -442,7 +442,7 @@ mod tests { .graduate_triage_clean() } - // Two lemmas with the SAME statement (params + req + ens) under DIFFERENT names hash + // Two lemmas with the same statement (params + req + ens) under different names hash // EQUAL; a different statement hashes differently (REQ-9 / Q1: the dedup key excludes // the name + the proof). #[test] @@ -474,14 +474,14 @@ mod tests { } // AC-13: citing an UNCERTIFIED project lemma is refused, naming the lemma; a citation - // to a CERTIFIED project lemma (or a frozen spine lemma) resolves. + // to a certified project lemma (or a frozen spine lemma) resolves. #[test] fn uncertified_citation_is_refused_with_the_lemma_named() { let prog = parse_ok( "lemma melems_cons(n: u32) req n > 0 ens n >= 1 proof { omega }\n\ lemma user(n: u32) req n > 0 ens n >= 1 proof { simp [melems_cons]; omega }", ); - // `melems_cons` did NOT certify (absent from the cert collection). + // `melems_cons` did not certify (absent from the cert collection). let lib = LemmaLibrary::build(&prog, &[]); assert!(lib.is_project_lemma("melems_cons")); assert!(!lib.is_certified("melems_cons")); @@ -494,7 +494,7 @@ mod tests { } Ok(()) => panic!("expected an uncertified-citation refusal naming `melems_cons`"), } - // Once `melems_cons` certifies, the SAME citation resolves (no refusal). + // Once `melems_cons` certifies, the same citation resolves (no refusal). let lib = LemmaLibrary::build(&prog, &[certified("melems_cons")]); assert!(lib.is_certified("melems_cons")); assert!(enforce_citations(&lib, "user", "simp [melems_cons]; omega").is_ok()); @@ -526,7 +526,7 @@ mod tests { } // AC-13: burning a statement-hash duplicate rewrites the citation to the canonical - // lemma instead of storing a copy — TWO identical lemmas under DIFFERENT names → ONE + // lemma instead of storing a copy — TWO identical lemmas under different names → one // stored, the citation to the duplicate rewritten to the first (canonical). #[test] fn dedup_on_burn_rewrites_citation_to_canonical() { @@ -535,18 +535,18 @@ mod tests { lemma melems_cons_dup(n: u32) req n > 0 ens n >= 1 proof { omega }\n\ lemma user(n: u32) req n > 0 ens n >= 1 proof { simp [melems_cons_dup]; omega }", ); - // BOTH duplicates certify; the FIRST in source order (`melems_cons`) is canonical. + // both duplicates certify; the first in source order (`melems_cons`) is canonical. let lib = LemmaLibrary::build( &prog, &[certified("melems_cons"), certified("melems_cons_dup")], ); - // Only ONE is stored (the canonical) — the duplicate dedups away. + // Only one is stored (the canonical) — the duplicate dedups away. assert_eq!( lib.stored_lemmas(), vec!["melems_cons".to_string()], "two identical-statement lemmas store ONE canonical copy" ); - // A citation to the duplicate is REWRITTEN to the canonical. + // A citation to the duplicate is rewritten to the canonical. assert_eq!( lib.canonical_name("melems_cons_dup"), Some("melems_cons"), @@ -557,15 +557,15 @@ mod tests { vec!["melems_cons".to_string()], "the burned citation is rewritten to the canonical, not stored as a copy" ); - // Citing both the canonical AND the duplicate collapses to one (dedup in order). + // Citing both the canonical and the duplicate collapses to one (dedup in order). assert_eq!( lib.rewrite_citations(&["melems_cons".to_string(), "melems_cons_dup".to_string()]), vec!["melems_cons".to_string()] ); } - // Q6: only a TOP-LEVEL lemma enters the namespace — a name a `proof for f` establishes - // locally is NOT a project lemma, so `proof for g` cannot cite it (no cross-function + // Q6: only a top-level lemma enters the namespace — a name a `proof for f` establishes + // locally is not a project lemma, so `proof for g` cannot cite it (no cross-function // sharing except via a lemma). The namespace is built from `Item::Forge(Lemma)` only. #[test] fn only_top_level_lemmas_are_in_the_namespace() { @@ -577,7 +577,7 @@ mod tests { let lib = LemmaLibrary::build(&prog, &[certified("shared")]); // A top-level lemma IS in the namespace (the cross-function shareable surface). assert!(lib.is_project_lemma("shared")); - // A `proof for f`-local name is NOT — it never entered the namespace, so a citation + // A `proof for f`-local name is not — it never entered the namespace, so a citation // to it from anywhere resolves `Unknown` (refused by the frozen battery), never as a // shareable project lemma. This is Q6 enforced structurally. assert!(!lib.is_project_lemma("local_fact")); diff --git a/forge/src/manifest.rs b/forge/src/manifest.rs index 01f15df39..f080839d4 100644 --- a/forge/src/manifest.rs +++ b/forge/src/manifest.rs @@ -191,11 +191,11 @@ fn scope_is_end_to_end(scope: &Option) -> bool { /// trust `solver(nlsat) + spine-lemma(kernel)`, the ℝ→ℤ bridge sound by the /// kernel-checked `r_relax_sound` + `rencode_sound` (`lean/Thermite/Relax.lean`); /// - the **`@bv` machine-width** discharge (Stage-3 REQ-2, `--engine bv`): trust -/// `solver(Z3 QF_BV)`, kernel-grounded by REQ-7/8 reconstruction at the SAME rung. +/// `solver(Z3 QF_BV)`, kernel-grounded by REQ-7/8 reconstruction at the same rung. /// /// (The general Verus/Z3 cage still certifies at L3 in code pending its own promotion /// — a follow-up; the RFC ladder eventually places the whole decidable cage at L4.) -/// Adding L4 is additive: the v1 conformance corpus stays at L3 (the L4 routes are NEW +/// Adding L4 is additive: the v1 conformance corpus stays at L3 (the L4 routes are new /// engine routes reached only via `--engine nlsat`/`--engine bv`, never the default /// Verus path), so the v1 `oracle_subset` is byte-identical. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] @@ -272,7 +272,7 @@ pub struct ObligationResult { #[serde(default, skip_serializing_if = "Option::is_none")] pub verdict: Option, /// (Schema v2, REQ-3 / AC-4 — `.design/stage3-bv-reconstruction.md`) Lock 1, the - /// SHADOW FLAG (RFC §9 shape): present on every `@bv`-tagged clause's obligation, + /// shadow FLAG (RFC §9 shape): present on every `@bv`-tagged clause's obligation, /// absent on every untagged / v1 clause. The machine-semantics fork is then /// impossible to hide — `grep bv_shadow` over the certificates ≡ exactly the set of /// tagged clauses, the same "`grep slag` is the complete inventory" discipline the @@ -281,12 +281,12 @@ pub struct ObligationResult { /// certs — which omit it — deserialize unchanged and re-serialize byte-identically. /// Oracle-INCLUDED via [`Certificate::oracle_subset`] (Q-ORACLE: deterministic + /// verdict-relevant → included), unlike the provenance-only `engine`/`trust`: a - /// semantic fork changes what the clause MEANS, so the oracle pins it. + /// semantic fork changes what the clause means, so the oracle pins it. #[serde(default, skip_serializing_if = "Option::is_none")] pub bv_shadow: Option, } -/// Lock 1, the bv SHADOW FLAG (`.design/stage3-bv-reconstruction.md` REQ-3 / AC-4 — the +/// Lock 1, the bv shadow FLAG (`.design/stage3-bv-reconstruction.md` REQ-3 / AC-4 — the /// RFC §9 shape). Every `@bv`-tagged clause's certificate carries this block, so a /// fixed-width machine-semantics clause is loud and greppable at every layer `#[slag]` /// is (the certificate JSON, `forge review`, `forge audit`). The first of the three @@ -560,7 +560,7 @@ pub struct Certificate { /// The §7 step-5 strengthening suggestions surfaced for this item (issue #14 /// additive field; `.design/forge/strengthening-probes.md` REQ-4). Each /// [`Suggestion`] is an adoptable stronger-`ens` clause that verifies against - /// the real body and is strictly stronger than the current `ens` (it would + /// the body and is strictly stronger than the current `ens` (it would /// kill a #12 survivor / adds an equality the `ens` lacks). Advisory: a probe /// only adds these; it does not change the verdict (`level`/`reject`/the oracle /// subset). `#[serde(default, skip_serializing_if = Vec::is_empty)]` so the @@ -631,7 +631,7 @@ pub struct Certificate { /// `Level` is unchanged — L3 still means "proven for all inputs"; the trust base is /// the auditor-visible refinement): excluded from `oracle_subset` (OQ-2 decided /// diagnostic-only so the golden stays stable; the project-min aggregate is - /// unchanged — REQ-4 "honest-min aggregation unchanged"). + /// unchanged — REQ-4 "minimum aggregation unchanged"). #[serde(default, skip_serializing_if = "Option::is_none")] pub engine_attribution: Option, /// The covenant evidence block (`.design/stage1-forge-tier.md` REQ-4, increment @@ -665,12 +665,12 @@ pub struct Certificate { #[serde(default, skip_serializing_if = "Option::is_none")] pub meaning_audit: Option, /// The L3 burn receipt (`.design/stage1-forge-tier.md` REQ-7, increment 2e; RFC-1 - /// §9; Q-BURN). `Some` only on a forge-tier item whose proof closed a goal (the + /// §9; Q-burn). `Some` only on a forge-tier item whose proof closed a goal (the /// proof-view discharge path): the committed-proof lexer-token count, the optional - /// authoring spend, and the lemmas the proof cited. Per Q-ORACLE / Q-BURN the burn + /// authoring spend, and the lemmas the proof cited. Per Q-ORACLE / Q-burn the burn /// receipt is verdict-IRRELEVANT (re-authoring a proof legitimately changes the /// token count and authoring spend without changing what was proven), so it is - /// EXCLUDED from [`Certificate::oracle_subset`] — exactly like `solver_time_ms`. + /// excluded from [`Certificate::oracle_subset`] — like `solver_time_ms`. /// `None` for every v1 item (no forge-tier burn), and `#[serde(default, /// skip_serializing_if = "Option::is_none")]` so the 7 frozen v1 golden certs /// (which omit it) serialize BYTE-IDENTICALLY (R-SPEC-2, additive only), mirroring @@ -680,7 +680,7 @@ pub struct Certificate { } impl Certificate { - /// Assemble a #5 certificate from the real pipeline data (REQ-2). `check.rs` + /// Assemble a #5 certificate from the pipeline data (REQ-2). `check.rs` /// derives `level`/`obligations` from verus and `effects` from the item's /// `fx` row; the forward-declared and reserved fields take their #5 /// values here. @@ -1002,7 +1002,7 @@ impl Certificate { } /// Build a non-certified certificate for a weak-contract reject (#12; - /// `.design/forge/mutation-scoring.md` REQ-5/REQ-6). The item's real body + /// `.design/forge/mutation-scoring.md` REQ-5/REQ-6). The item's body /// proved L3, but its frozen mutant set scored below the floor — the contract /// under-constrains the body (mutants survive). Like [`Certificate::rejected`] /// (`Level::L0`, the structured `reject` cause, one failed obligation naming @@ -1094,7 +1094,7 @@ impl Certificate { /// goal (`.design/stage1-forge-tier.md` REQ-7, increment 2e; RFC-1 §9). Set on the /// proof-view discharge path: the committed-proof token count + cited lemmas (and /// the optional authoring spend) join the cert as auditable burn evidence. Per - /// Q-BURN the receipt is oracle-EXCLUDED (re-authoring a proof changes it without + /// Q-burn the receipt is oracle-excluded (re-authoring a proof changes it without /// changing the claim), so only this field changes — the verdict and the /// `oracle_subset` are untouched. A v1 item never calls this, so its `burn` stays /// `None` and its cert is byte-stable. @@ -1107,7 +1107,7 @@ impl Certificate { /// Build a non-certified certificate for an over-budget definition tower /// (`.design/stage1-forge-tier.md` REQ-6c, increment 2d; AC-10). The forge-tier /// item's contract unfolds a definition tower deeper or wider than the Q2 default - /// budget (depth 4 / 40 definitions) — a Goodhart move (hiding the real claim + /// budget (depth 4 / 40 definitions) — a Goodhart move (hiding the claim /// behind an unreadable tower), refused at certify time. Like /// [`Certificate::rejected`] (`Level::L0`, the structured `DefinitionTowerBudget` /// cause, one failed obligation naming it), but it ALSO pins the unfolded-tower @@ -1216,17 +1216,17 @@ impl Certificate { /// cert, so the v1 oracle stays byte-identical; a forge-tier item's unfolded-tower /// hash is oracle-visible (REQ-6c: the certified meaning cannot drift silently). /// - /// The REQ-7 `burn` receipt (increment 2e) is deliberately ABSENT from this tuple - /// (Q-BURN): re-authoring a proof legitimately changes its committed-token count + + /// The REQ-7 `burn` receipt (increment 2e) is ABSENT from this tuple + /// (Q-burn): re-authoring a proof legitimately changes its committed-token count + /// authoring spend without changing what was proven, so it is oracle-excluded like /// `solver_time_ms` — a forge-tier cert and the same cert with its `burn` stripped /// compare oracle-equal. /// /// `bv_shadows` is the Lock 1 shadow flag (stage-3 REQ-3 / AC-4, Q-ORACLE: - /// deterministic + verdict-relevant → included): the PRESENT `bv_shadow` blocks, in + /// deterministic + verdict-relevant → included): the present `bv_shadow` blocks, in /// obligation source order. It is FILTERED to the tagged clauses (not one slot per /// obligation) so a v1 / untagged cert — whatever its obligation count, including a - /// hand-authored golden with no `obligations` key — contributes an EMPTY vec and stays + /// hand-authored golden with no `obligations` key — contributes an empty vec and stays /// oracle-byte-identical (the obligation count was never an oracle field and must not /// become one). A tagged clause's machine-semantics fork is then oracle-visible (a /// semantic fork CHANGES what the clause means — it cannot drift silently, unlike the @@ -1533,15 +1533,15 @@ mod tests { // REQ-1 / AC-4 (schema v2 additive — `.design/stage1-forge-tier.md`): the per-clause // `engine`/`trust`/`verdict` fields are additive (`#[serde(default, - // skip_serializing_if)]`), so the v1 oracle subset is unperturbed for ALL SEVEN + // skip_serializing_if)]`), so the v1 oracle subset is unperturbed for all seven // conformance goldens. The goldens are HAND-AUTHORED stable-subset oracles (R-CHAR-3 // — item/level/contract_quality/effects/slag, not full forge-emitted certs), so the // byte-identity claim is verified two ways: (1) NONE of the seven golden FILES carries // any schema-v2 key (`engine`/`trust`/`verdict`) anywhere — they are pristine v1 // oracles, untouched by this increment; (2) a v1-shape cert (a Verus-corpus discharged - // clause, no per-clause block) SERIALIZES with NO schema-v2 keys (the `skip_serializing_if` + // clause, no per-clause block) SERIALIZES with no schema-v2 keys (the `skip_serializing_if` // omits the absent fields), so forge's v1 output still matches those goldens byte-for-byte. - // A populated block (the forge-tier Lean path) DOES serialize the keys — the field works. + // A populated block (the forge-tier Lean path) does serialize the keys — the field works. #[test] fn schema_v2_additive_leaves_all_seven_goldens_byte_identical() { // Recursively assert no JSON key named `engine`/`trust`/`verdict` appears. @@ -1580,7 +1580,7 @@ mod tests { ); } - // (2) A v1-shape clause (no per-clause block) serializes with NO schema-v2 keys. + // (2) A v1-shape clause (no per-clause block) serializes with no schema-v2 keys. let v1 = Certificate::new( "sum", Level::L3, @@ -1597,7 +1597,7 @@ mod tests { (skip_serializing_if), so it stays byte-identical to the v1 goldens: {v1_json}" ); - // (3) A populated forge-tier clause DOES serialize the keys (the field is live). + // (3) A populated forge-tier clause does serialize the keys (the field is live). let forge_tier = Certificate::new( "isqrt", Level::L3, @@ -1626,7 +1626,7 @@ mod tests { // for the `nowrap` spelling. #[test] fn bv_shadow_is_additive_and_serializes_the_rfc9_shape() { - // A v1-shape clause (no shadow) must NOT serialize a `bv_shadow` key — so the v1 + // A v1-shape clause (no shadow) must not serialize a `bv_shadow` key — so the v1 // goldens stay byte-identical (the `engine`/`trust`/`verdict` discipline). let v1 = Certificate::new( "sum", @@ -1704,7 +1704,7 @@ mod tests { } // Stage-3 REQ-3 / AC-4: the shadow flag is ORACLE-INCLUDED (Q-ORACLE: deterministic + - // verdict-relevant → included) — two certs differing ONLY in an obligation's + // verdict-relevant → included) — two certs differing only in an obligation's // `bv_shadow` compare oracle-UNEQUAL, unlike the provenance-only engine/trust (which // are oracle-excluded). A semantic fork cannot drift silently. #[test] @@ -1729,7 +1729,7 @@ mod tests { "the oracle must DISTINGUISH a tagged clause from an untagged one (the fork is \ verdict-relevant — REQ-3 / AC-4)" ); - // But engine/trust attribution differences alone stay oracle-EXCLUDED (provenance). + // But engine/trust attribution differences alone stay oracle-excluded (provenance). let mut attributed = bare.clone(); attributed.obligations[0] = ObligationResult::discharged("mix64::ens#0") .with_clause_attribution( diff --git a/forge/src/meaning.rs b/forge/src/meaning.rs index a3b265c65..a81955a2c 100644 --- a/forge/src/meaning.rs +++ b/forge/src/meaning.rs @@ -7,7 +7,7 @@ //! A forge-tier contract gets its *meaning* by unfolding the `spec fn`s its //! `req`/`ens` clauses reference, which unfold the `spec fn`s *they* reference, and //! so on — a tower of definitions. A contract can be made to "say" something while -//! hiding the real claim behind a deep stack of definitions an auditor cannot read +//! hiding the claim behind a deep stack of definitions an auditor cannot read //! through (the Goodhart move: optimize "the contract proves" by making the contract //! unreadable). REQ-6c bounds that tower: the **Q2 default budget** is a //! [`TOWER_DEPTH_BUDGET`] of `4` (the longest distinct-definition unfolding chain) @@ -15,10 +15,10 @@ //! tower deeper or wider than the budget does not certify — a refusal at certify //! time, never a silent pass. //! -//! ## Where the gate lives (and where it does NOT) +//! ## Where the gate lives (and where it does not) //! //! The gate is a **certify-time gate on the discharge path** (`check.rs`, the -//! forge/Lean `--engine` route), NOT in `forge audit`. `forge audit`'s "gates +//! forge/Lean `--engine` route), not in `forge audit`. `forge audit`'s "gates //! nothing" projection invariant is shipped (#274, `.design/forge/audit-manifest.md` //! REQ-10): it re-derives no verdict and changes no exit code. `forge audit //! --meaning` is the **read-only companion**: it prints the unfolded tower and @@ -40,13 +40,13 @@ use serde::{Deserialize, Serialize}; use sha2::{Digest, Sha256}; use thermite_syntax::{Expr, FnItem, Item, Program, SpecFnItem}; -/// The Q2 default definition-tower DEPTH budget (REQ-6c): the maximum length of the +/// The Q2 default definition-tower depth budget (REQ-6c): the maximum length of the /// longest distinct-definition unfolding chain rooted at the contract. A tower whose /// deepest chain exceeds this does not certify (a certify-time refusal). `4` is the /// Q2 default in `.design/stage1-forge-tier.md` REQ-6. pub const TOWER_DEPTH_BUDGET: usize = 4; -/// The Q2 default definition-tower SIZE budget (REQ-6c): the maximum number of +/// The Q2 default definition-tower size budget (REQ-6c): the maximum number of /// distinct definitions reachable from the contract. A tower with more distinct /// definitions than this does not certify. `40` is the Q2 default. pub const TOWER_DEFINITION_BUDGET: usize = 40; @@ -289,7 +289,7 @@ impl DefinitionTower { pub fn build_tower(program: &Program, src: &str, f: &FnItem) -> DefinitionTower { let spec_decls = spec_decls_of(program); - // The roots: the spec fns the CONTRACT (`req ∪ ens`) directly references — the + // The roots: the spec fns the contract (`req ∪ ens`) directly references — the // meaning surface. (The body's own calls are implementation, not the claim.) let mut roots: BTreeSet = BTreeSet::new(); crate::check::collect_expr_spec_fn_calls(&f.contract.req.expr, &spec_decls, &mut roots); @@ -323,7 +323,7 @@ pub fn build_tower(program: &Program, src: &str, f: &FnItem) -> DefinitionTower } } -/// The definition-tower DEPTH + distinct-definition count rooted at an arbitrary set of +/// The definition-tower depth + distinct-definition count rooted at an arbitrary set of /// contract clause exprs (stage-3 REQ-6) — the depth-only projection the "semantic forks /// and definition towers" section ([`crate::forks`]) uses for a burned `lemma`. A /// [`thermite_syntax::LemmaItem`] carries `req ∪ ens` but no `FnItem`/body, so @@ -579,7 +579,7 @@ fn f(x: u32) -> u32 req true ens a(x) fx pure { x }"; assert!(tower.within_budget()); } - /// A definition referenced only by the body (not the contract) is NOT in the + /// A definition referenced only by the body (not the contract) is not in the /// tower: the tower is the meaning of the contract, not the implementation. Here /// `contract_dep` roots the tower via `ens`; `body_dep` is called only from the /// body, so it is absent (and a `spec fn` it would have pulled in does not count). diff --git a/forge/src/metrics.rs b/forge/src/metrics.rs index 34b52454c..2a1178b5d 100644 --- a/forge/src/metrics.rs +++ b/forge/src/metrics.rs @@ -8,9 +8,9 @@ //! (`manifest::ObligationResult`) and the translation-validation (TV) phase verdicts //! (`contract_tv::TvCounts`). It aggregates three §6 metrics: //! -//! 1. **cage-vs-forge share BY ROUTING REASON** — every discharged clause was routed +//! 1. **cage-vs-forge share BY routing reason** — every discharged clause was routed //! either to the Verus/Z3 *cage* (the default push-button path) or escalated UP to -//! the *forge* (the nlsat relax route or the Lean lemma route). The routing REASON +//! the *forge* (the nlsat relax route or the Lean lemma route). The routing reason //! is a total, deterministic projection of the per-clause `engine` tag //! ([`RoutingReason::from_engine`]): `verus`/absent ⇒ in-cage, `nlsat` ⇒ relaxable, //! `lean-*` ⇒ lemma. No new certificate field is introduced — `engine` IS the @@ -26,8 +26,8 @@ //! ## Gates nothing (#274, `.design/forge/audit-manifest.md` REQ-10) //! //! The dashboard is printed by `forge audit --metrics` as an informational section -//! AFTER the manifest. It changes no exit code and alters no verdict — exactly like -//! the `--meaning` companion. Its output is NOT part of the certificate +//! after the manifest. It changes no exit code and alters no verdict — like +//! the `--meaning` companion. Its output is not part of the certificate //! `oracle_subset`, so it never perturbs a golden. //! //! ## REQ status @@ -57,7 +57,7 @@ pub const VERDICT_KINDS: [&str; 7] = [ ]; /// The per-clause routing reason — WHY a clause was discharged where it was (REQ-7). -/// A total, deterministic projection of the schema-v2 per-clause `engine` tag, NOT a +/// A total, deterministic projection of the schema-v2 per-clause `engine` tag, not a /// new certificate field (the cert schema is untouched, so goldens stay byte-identical). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum RoutingReason { @@ -85,7 +85,7 @@ pub enum Share { } impl RoutingReason { - /// Project the per-clause `engine` tag to its routing reason (REQ-7) — a TOTAL map + /// Project the per-clause `engine` tag to its routing reason (REQ-7) — a total map /// over the closed engine-tag set ([`crate::engine::EngineName::tag`]): /// `nlsat` ⇒ [`RoutingReason::Relaxable`], any `lean*` tag ⇒ [`RoutingReason::Lemma`], /// and `verus` / `None` (the v1 Verus corpus clause, which carries no per-clause @@ -122,7 +122,7 @@ impl RoutingReason { } } -/// The cage-vs-forge routing share BY REASON (REQ-7) — the per-reason clause counts. +/// The cage-vs-forge routing share BY reason (REQ-7) — the per-reason clause counts. /// `in_cage` is the whole cage share; `relaxable` + `lemma` is the whole forge share. #[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] pub struct RoutingShare { @@ -214,9 +214,9 @@ fn verdict_index(kind: &str) -> Option { /// The TV phase split (REQ-7) — the contract-TV outcome split mapped to the §6 /// taxonomy. The mapping over [`crate::contract_tv::ClauseVerdict`]: /// - `faithful` ← `Faithful` (the lowering verified faithful — the baseline); -/// - `syntactic` ← `Skipped` (a clause outside the framed sublanguage — a SYNTACTIC +/// - `syntactic` ← `Skipped` (a clause outside the framed sublanguage — a syntactic /// coverage gap, reported not-checked rather than a false faithful); -/// - `semantic` ← `Divergent` (verus found a counterexample — a SEMANTIC lowering +/// - `semantic` ← `Divergent` (verus found a counterexample — a semantic lowering /// infidelity); /// - `timeout` ← `Unverifiable` (a Verus/Z3 rlimit exhaustion or verus-absent run — /// surfaced, never fabricated into a Divergent). @@ -379,7 +379,7 @@ mod tests { use crate::manifest::{Certificate, Level, ObligationResult}; use crate::verdict::{CertVerdict, CovenantCounterexample, RealPoint}; - /// REQ-7: the routing-reason projection is a TOTAL map over the closed engine-tag + /// REQ-7: the routing-reason projection is a total map over the closed engine-tag /// set — `nlsat` ⇒ relaxable, every `lean*` tag ⇒ lemma, `verus`/absent ⇒ in-cage, /// and the cage-vs-forge `share()` splits relaxable+lemma to the forge. #[test] diff --git a/forge/src/mutation.rs b/forge/src/mutation.rs index d41d02cb3..4cd8e8dc2 100644 --- a/forge/src/mutation.rs +++ b/forge/src/mutation.rs @@ -1,13 +1,13 @@ //! `forge/src/mutation.rs` — §7 step 4 of the vacuity battery: mutation scoring //! (`thermite-design.md` §7 line 224, "operator flips, off-by-ones, early //! returns, branch swaps — fixed deterministic mutator set"). Given a `fn` whose -//! real body already verifies L3, this module generates a frozen, deterministic +//! body already verifies L3, this module generates a frozen, deterministic //! set of mutants of that body (the contract untouched), re-lowers + re-verifies //! each against the same contract through the existing verus driver + proof //! cache, and scores the kill ratio (`killed / scored`). A mutant verus //! rejects is killed (the contract caught the wrong body — good); a mutant //! verus proves is a survivor (the contract cannot tell the mutant from the -//! real body — too weak). A configurable floor (default 60%, §7) gates +//! body — too weak). A configurable floor (default 60%, §7) gates //! certification: below the floor the item does not certify and the surviving //! mutants are the precise strengthening prompt. //! @@ -120,12 +120,12 @@ pub struct MutationScore { pub killed: usize, /// Mutants that lowered + ran and were not proved equivalent (the kill-ratio /// denominator). Excludes un-lowerable mutants (OQ-5) and mutants Verus proved - /// observably equivalent to the real body under the precondition + /// observably equivalent to the body under the precondition /// (`.design/forge/equivalent-mutants.md` REQ-2/REQ-4, #101 — a true /// equivalent mutant is not contract weakness, so it drops from the /// denominator rather than depressing the ratio). pub scored: usize, - /// The count of survivors Verus proved observably equivalent to the real body + /// The count of survivors Verus proved observably equivalent to the body /// under `req` (`.design/forge/equivalent-mutants.md` REQ-2/REQ-4, #101). A /// proved-equivalent mutant is excluded from both the survivor set and /// `scored`; this field records how many were so excluded (a transparency @@ -205,7 +205,7 @@ impl MutationScore { /// /// `adt_deps` carries the program's ADT items (the same `&[Item]` every /// production caller already threads into `check::item_subprogram`) so the -/// F-STRUCT-ZERO family (REQ-10/REQ-11) can resolve a `Type::Named` struct +/// F-STRUCT-zero family (REQ-10/REQ-11) can resolve a `Type::Named` struct /// return's field list — the early-return zero ladder needs the `StructItem` /// definitions. A def-free `fn` (no struct return) passes `&[]` and the family /// is inert. The Lean-path caller threads its full program's items here too. @@ -283,7 +283,7 @@ pub fn generate(f: &FnItem, _seed: u64, adt_deps: &[Item]) -> Vec { fn mutant_with_body(f: &FnItem, body: Block, desc: String) -> Mutant { let mut item = f.clone(); // A mutant is always a bodied in-language fn (its source `f` proved L3, so it - // had a real body); the field is `Option` since #16, so wrap in `Some`. + // had a body); the field is `Option` since #16, so wrap in `Some`. item.body = Some(body); Mutant { item, desc } } @@ -559,7 +559,7 @@ fn zero_value_for(ret: &Type) -> Option { } } -/// The F-STRUCT-ZERO early-return value for a `Type::Named(name)` struct return +/// The F-STRUCT-zero early-return value for a `Type::Named(name)` struct return /// (REQ-10/REQ-11): the field-zero struct literal `name { field: , … }`, /// resolved against the threaded `adt_deps`. Each field's zero comes from the /// same synthesis ladder the early-return family owns (`zero_value_with_defs`: @@ -578,7 +578,7 @@ fn zero_value_for(ret: &Type) -> Option { /// /// Type-invariant interaction (REQ-10): a struct `inv` is contract — if the /// field-zero literal violates it, Verus fails the construction obligation and -/// the mutant is killed (the honest polarity). For the corpus structs the zeros +/// the mutant is killed (the polarity). For the corpus structs the zeros /// satisfy the `inv` (`Account { balance: 0 }`: `0 <= 1_000_000`; /// `Buffer { text: , cursor: 0 }`: `0 <= 0 && 0 <= 1_000_000`), so the /// mutant is scored against the `ens`. @@ -599,7 +599,7 @@ fn struct_zero_value(name: &str, adt_deps: &[Item]) -> Option<(Expr, String)> { } /// Resolve a `struct name` definition among the threaded ADT items (REQ-11). An -/// `Item::Enum` of the same name resolves to `None` here (F-STRUCT-ZERO is a +/// `Item::Enum` of the same name resolves to `None` here (F-STRUCT-zero is a /// struct-only family — an enum has no canonical variant, the OQ-5 drop). fn find_struct<'a>(name: &str, adt_deps: &'a [Item]) -> Option<&'a StructItem> { adt_deps.iter().find_map(|i| match i { @@ -1213,7 +1213,7 @@ impl Applier<'_> { Expr::Path(p) => Expr::Path(p.clone()), // A string literal (`.design/basis/07-strings.md` REQ-1) is a leaf with // no mutation site (text, not an off-by-one target) — the rewriter - // rebuilds it by identity, exactly as for `BoolLit`/`Path`. + // rebuilds it by identity, as for `BoolLit`/`Path`. Expr::StrLit(s) => Expr::StrLit(s.clone()), } } @@ -1249,7 +1249,7 @@ mod tests { .expect("fixture has a fn") } - /// Parse a program and return all its items so the F-STRUCT-ZERO family + /// Parse a program and return all its items so the F-STRUCT-zero family /// (REQ-10/REQ-11) can be exercised with the struct defs threaded as /// `adt_deps` (the same items a production caller weaves). Pair with `parse_fn` /// (or a name filter) to pull the fn under test. @@ -1388,7 +1388,7 @@ mod tests { ); } - // REQ-10 (F-STRUCT-ZERO): a named-struct return synthesizes the field-zero + // REQ-10 (F-STRUCT-zero): a named-struct return synthesizes the field-zero // struct literal early-return mutant, resolved against the threaded defs. The // corpus `Account { balance: u64 }` -> `Account { }`. AC-8 also // checks the F-IDENT `return a`. Expected trace: REQ-10's table (R-CHAR-3). @@ -1421,7 +1421,7 @@ mod tests { } // REQ-10 / AC-10 (the OQ-5 drop): a struct return with a zero-less field (a - // `Box`-typed field) generates no F-STRUCT-ZERO mutant — never an error. + // `Box`-typed field) generates no F-STRUCT-zero mutant — never an error. #[test] fn struct_zero_drops_when_a_field_has_no_zero() { let items = parse_items( @@ -1440,7 +1440,7 @@ mod tests { ); } - // REQ-10: an enum-named return gets no F-STRUCT-ZERO mutant (no canonical + // REQ-10: an enum-named return gets no F-STRUCT-zero mutant (no canonical // variant — the OQ-5 drop), but the F-IDENT identity is still generated. #[test] fn struct_zero_drops_for_enum_named_return() { @@ -1671,7 +1671,7 @@ mod tests { // cross-multiply; the f64↔integer agreement is this test's job (OQ-E). // // OQ-E (the f64 boundary subtlety): f64 `0.60` is not exactly 3/5, so a ratio - // exactly on the boundary (e.g. 12/20 == 0.60) could in principle diverge by a + // on the boundary (e.g. 12/20 == 0.60) could in principle diverge by a // rounding ULP between the f64 `>=` and the integer cross-multiply. The grid is // run here (not assumed); if any cell diverges it is reported, not masked // (R-DEFER-9). The empirical expectation (from the cross-multiply being the diff --git a/forge/src/obligation.rs b/forge/src/obligation.rs index 99f688932..7de6bbe12 100644 --- a/forge/src/obligation.rs +++ b/forge/src/obligation.rs @@ -42,27 +42,27 @@ //! | REQ-FORGE-OBLIGATION-REGISTRY-TERMINATION | shipped | `forge/src/obligation.rs` | Registry-termination class and full-position closure | | //! //! -//! The auxiliary OVERFLOW / TERMINATION classes and the multi-class minting of one +//! The auxiliary overflow / termination classes and the multi-class minting of one //! item's full obligation set are part of the per-class rendering increment (ii) -//! still grows; increment (i) reifies the class enum (AC-1) and mints the CONTRACT + -//! REGISTRY-TERMINATION classes the §0 pipeline discharges today, which +//! still grows; increment (i) reifies the class enum (AC-1) and mints the contract + +//! REGISTRY-termination classes the §0 pipeline discharges today, which //! is what the Verus engine's `discharge` keys on. use thermite_syntax::{Block, Expr, FnItem, SpecFnItem, Type}; /// The backend-neutral obligation class (`.design/verified/proof-backends.md` /// REQ-1 / AC-1). The variant set is the union of the `thermite-tv/src/obligation.rs` -/// emitters (CONTRACT / EXEC / BODY / LOOP-{entry,preservation,exit}), the §6/§7 -/// in-item auxiliaries Verus discharges (OVERFLOW / TERMINATION), and REQ-1.2's +/// emitters (contract / exec / body / LOOP-{entry,preservation,exit}), the §6/§7 +/// in-item auxiliaries Verus discharges (overflow / termination), and REQ-1.2's /// [`ObligationClass::RegistryTermination`]. The three §0.1 meta/battery query /// classes (vacuity / equivalence / strengthen) are not here — they /// stay direct verus invocations outside the Engine interface in v1 (the role /// discriminator carries that future seam; see [`ObligationRole`]). #[derive(Debug, Clone, Copy, PartialEq, Eq)] // AC-1 requires the class enum's variants to be the full union of the -// `obligation.rs` emitters + the §6/§7 in-item auxiliaries + REGISTRY-TERMINATION. -// Increment (i) mints CONTRACT + REGISTRY-TERMINATION on the live path; the EXEC / -// BODY / LOOP-* / OVERFLOW / TERMINATION variants are forward-declared per AC-1 and +// `obligation.rs` emitters + the §6/§7 in-item auxiliaries + REGISTRY-termination. +// Increment (i) mints contract + REGISTRY-termination on the live path; the exec / +// body / LOOP-* / overflow / termination variants are forward-declared per AC-1 and // minted as the per-class rendering grows (increment (ii)+). Each carries a stable // `tag()`. The forward-declared variants are not yet constructed in production. #[allow( @@ -76,10 +76,10 @@ pub enum ObligationClass { /// `equivalence_obligation` content (the canonical `result == spec_sum(xs)` /// shape). This is the class the Verus engine's per-item L3 discharge proves. Contract, - /// An EXEC value obligation (`exec_equivalence_obligation`): the production + /// An exec value obligation (`exec_equivalence_obligation`): the production /// exec expr's bounded value equals the reference exec value. Exec, - /// A straight-line BODY state obligation (`body_equivalence_obligation`). + /// A straight-line body state obligation (`body_equivalence_obligation`). Body, /// A loop ENTRY obligation (`loop_entry_obligation`): `inv` holds on entry. LoopEntry, @@ -89,13 +89,13 @@ pub enum ObligationClass { /// A loop EXIT obligation (`loop_exit_obligation`): the claimed after-loop /// characterization follows from `inv ∧ ¬cond`. LoopExit, - /// An OVERFLOW / bounds obligation (the bounded `S_E`: `execDenote = none` + /// An overflow / bounds obligation (the bounded `S_E`: `execDenote = none` /// exactly at overflow) Verus discharges inside an item. Overflow, - /// A TERMINATION obligation: the item's own `dec` measure is a valid + /// A termination obligation: the item's own `dec` measure is a valid /// well-founded descent (the source `dec` → the well-founded fixpoint). Termination, - /// The REGISTRY-TERMINATION class (REQ-1.2, the #215 fix): for an item with a + /// The REGISTRY-termination class (REQ-1.2, the #215 fix): for an item with a /// non-empty called-spec-fn set, every reached spec-fn carries a per-spec-fn /// obligation that its `dec` measure is a valid well-founded descent. The /// parser guarantees dec presence; this class is dec validity, and it is not @@ -130,7 +130,7 @@ impl ObligationClass { /// reified as `Obligation`s in v1 (they keep their own direct verus calls, OQ-5). /// The role field is the seam those inverted/advisory roles will key on, so the /// REQ-3 discharge discipline (Unknown→degrade, Refuted→hard-fail) can be scoped -/// to `Certification` from day one without dragging the OUT-list in. +/// to `Certification` from day one without dragging the out-list in. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ObligationRole { /// An item-correctness CERTIFICATION obligation — REQ-3's discharge discipline @@ -157,8 +157,8 @@ impl ObligationRole { #[derive(Debug, Clone, PartialEq, Eq)] pub enum AstSlice { /// A single expression node (a contract clause, an exec expr, a loop measure). - /// Forward-declared for the EXEC / LOOP-measure classes (increment (ii)+); the - /// CONTRACT obligation increment (i) mints uses the `Block` body slice. + /// Forward-declared for the exec / LOOP-measure classes (increment (ii)+); the + /// contract obligation increment (i) mints uses the `Block` body slice. #[allow( dead_code, reason = "proof-backends §1: the single-Expr slice serves the EXEC/loop-measure \ @@ -238,7 +238,7 @@ pub struct Obligation { } impl Obligation { - /// Mint the per-item CONTRACT certification obligation for a checked exec + /// Mint the per-item contract certification obligation for a checked exec /// `fn` (`.design/verified/proof-backends.md` §1 — "the (T1)-style equality the /// spine already proves the reference encoder satisfies, lifted to the per-item /// obligation"). The `ast_slice` is the fn's body (the production side the @@ -280,7 +280,7 @@ impl Obligation { } } - /// Mint the per-item CONTRACT certification obligation for a checked `spec fn` + /// Mint the per-item contract certification obligation for a checked `spec fn` /// (`.design/verified/proof-backends.md` §1). A spec fn is a pure /// contract-free definition; its certification obligation is stated over its /// `body` with its params in env. The `called_spec_fns` closure (REQ-1.2/#226, @@ -309,9 +309,9 @@ impl Obligation { } } - /// Mint the per-item REGISTRY-TERMINATION certification obligation (REQ-1.2): + /// Mint the per-item REGISTRY-termination certification obligation (REQ-1.2): /// an item with a non-empty `called_spec_fns` set carries this class - /// item-wide, conjoined with its CONTRACT class. The `ast_slice` is the item's + /// item-wide, conjoined with its contract class. The `ast_slice` is the item's /// own body (the descent measures live in `env.spec_defs`'s reached spec-fns + /// the item's own `dec`); the env's `spec_defs` is the full closure so the /// engine that discharges it (Verus's dec-check today; a Lean well-foundedness @@ -360,7 +360,7 @@ mod tests { .expect("fn present") } - // REQ-1: the CONTRACT obligation reifies the fn's body + params + req as + // REQ-1: the contract obligation reifies the fn's body + params + req as // neutral content (no Verus strings) — the params carry their Thermite types, // the req is an AST node. Expected from the design's §1 artifact shape (R-CHAR-3). #[test] @@ -389,7 +389,7 @@ mod tests { } // REQ-1.2: an item with a non-empty called-spec-fn set gets a - // REGISTRY-TERMINATION obligation; an empty set yields None. Expected from the + // REGISTRY-termination obligation; an empty set yields None. Expected from the // design's REQ-1.2 class-assignment condition (R-CHAR-3). #[test] fn registry_termination_minted_iff_called_spec_fns_nonempty() { diff --git a/forge/src/profile.rs b/forge/src/profile.rs index 480027d77..2126ac0af 100644 --- a/forge/src/profile.rs +++ b/forge/src/profile.rs @@ -16,7 +16,7 @@ //! [`suggested_move`]) is deterministic given a `SolverProfile`; the input //! profile (the Z3 instantiation counts) is not. //! -//! ## Grounded profiler format (real verus 0.2026.05.24, Z3 4.12.5) +//! ## Grounded profiler format (verus 0.2026.05.24, Z3 4.12.5) //! //! Captured by running `~/.local/bin/verus --profile-all --verify-root` on a //! transitivity / connectivity quantifier set (the checked-in fixture @@ -309,7 +309,7 @@ fn reconstruct_trigger(source: Option<&str>, caret: Option<&str>) -> String { fallback_forall_body(source) } -/// Fallback trigger text: the body of a `forall|binders| BODY` source line +/// Fallback trigger text: the body of a `forall|binders| body` source line /// (everything after the closing `|` of the binder list), trimmed of trailing /// punctuation. Used when the caret annotation is unavailable. fn fallback_forall_body(source: &str) -> String { diff --git a/forge/src/relax.rs b/forge/src/relax.rs index e7c9cb8b5..b1abd1fed 100644 --- a/forge/src/relax.rs +++ b/forge/src/relax.rs @@ -25,7 +25,7 @@ //! kernel-checked `r_relax_sound` the integer clause holds → certify at **L4**. //! - `sat` → a real countermodel → the **integrality check** ([`eval_contract_negation_over_ints`]): //! round the real point into a radius-2 ℤⁿ box and test whether any integer point -//! genuinely falsifies the integer clause. An integer falsifier is a real +//! falsifies the integer clause. An integer falsifier is a real //! `Counterexample`; if none does, the countermodel is real-only (true over ℤ, //! false over ℝ) → a `RealWitness` escalation (never a `Counterexample`). //! @@ -40,7 +40,7 @@ use thermite_syntax::{BinOp, Clause, Expr, FnItem, PrimType, Type, UnaryOp}; /// AC-12). [`Relaxable`](RelaxVerdict::Relaxable) means the whole contract is in the /// relax fragment (the nlsat route may attempt it); [`NotRelaxable`](RelaxVerdict::NotRelaxable) /// names the first construct that put it out of fragment (a div/mod/shift/cast atom, -/// a non-integer parameter, a non-polynomial call), so the route's skip is honest and +/// a non-integer parameter, a non-polynomial call), so the route's skip is explicit and /// the auditor sees *why* (R-CODE-4 — never a bare boolean). #[derive(Debug, Clone, PartialEq, Eq)] pub enum RelaxVerdict { @@ -129,7 +129,7 @@ pub fn classify_clause(c: &Clause) -> RelaxVerdict { } } -/// Is `e` a relaxable PROPOSITION (a boolean-valued clause expression)? A +/// Is `e` a relaxable proposition (a boolean-valued clause expression)? A /// comparison of polynomial terms, a boolean connective of relaxable propositions, /// or a boolean literal. Anything else (notably a div/mod/shift/cast/bitwise atom, /// reached through a term) is out of fragment, with the reason named. @@ -252,7 +252,7 @@ fn expr_kind(e: &Expr) -> &'static str { /// The integer-scalar variables of a relaxable `fn`, in a deterministic order /// (parameters in signature order, then `result`). The relax encoding declares each /// as a `Real`; the integrality check assigns each an integer. Pre: `f` is relaxable -/// (`classify_fn(f).is_relaxable()`), so every name here is a genuine integer +/// (`classify_fn(f).is_relaxable()`), so every name here is an integer /// variable. #[must_use] pub fn integer_vars(f: &FnItem) -> Vec { @@ -289,7 +289,7 @@ pub fn render_term_smt(e: &Expr) -> Option { } } -/// Render a relaxable PROPOSITION to an SMT-LIB(QF_NRA) `Bool` expression. Pre: +/// Render a relaxable proposition to an SMT-LIB(QF_NRA) `Bool` expression. Pre: /// `classify_prop` accepted `e`. `≠` renders as `(not (= …))`; the comparisons map to /// their SMT relations; the connectives to `and`/`or`/`not`. #[must_use] @@ -360,7 +360,7 @@ pub fn negated_contract_query(f: &FnItem) -> Option { // Domain guard: every relax variable is an UNSIGNED integer scalar (u32/u64/ // usize), so the faithful real relaxation restricts each to the non-negative // reals. This keeps the route sound (ℤ≥0 ⊆ ℝ≥0, so real-validity-on-the-guarded- - // domain still implies integer-validity per r_relax_sound) AND avoids a spurious + // domain still implies integer-validity per r_relax_sound) and avoids a spurious // countermodel at a negative real the unsigned domain never reaches. for v in integer_vars(f) { s.push_str(&format!("(assert (>= {v} 0.0))\n")); @@ -411,7 +411,7 @@ pub fn eval_term_int(e: &Expr, assign: &BTreeMap) -> Option } } -/// Evaluate a relaxable PROPOSITION over an integer assignment. Returns `None` if any +/// Evaluate a relaxable proposition over an integer assignment. Returns `None` if any /// sub-term is inconclusive (unbound / overflow). The comparisons and connectives /// fold over [`eval_term_int`] / themselves. #[must_use] @@ -446,7 +446,7 @@ pub fn eval_prop_int(e: &Expr, assign: &BTreeMap) -> Option /// The integrality check (`.design/stage1-forge-tier.md` REQ-8c / Q8): does the /// contract negation `req ∧ ¬(⋀ ens)` hold at the integer point `assign`? `true` -/// means `assign` is a genuine **integer counterexample** to the contract (a real +/// means `assign` is a **integer counterexample** to the contract (a real /// `Counterexample`); `false` means the contract holds there. `None` is inconclusive /// (unbound / overflow). Used to test the radius-2 ℤⁿ box rounded from a real /// countermodel: if no integer point in the box returns `true`, the real countermodel @@ -490,7 +490,7 @@ mod tests { .expect("fn present") } - // AC-12: `relaxable` ADMITS the isqrt postconditions — a polynomial contract + // AC-12: `relaxable` admits the isqrt postconditions — a polynomial contract // (`result*result <= n` ∧ `n < (result+1)*(result+1)`), integer-scalar params + // result, no div/mod/shifts/casts. #[test] @@ -512,7 +512,7 @@ mod tests { } } - // AC-12: `relaxable` REJECTS a div-containing clause (`%`, `<<`, casts likewise). + // AC-12: `relaxable` rejects a div-containing clause (`%`, `<<`, casts likewise). #[test] fn relaxable_rejects_div_mod_shift_cast() { let div = @@ -577,7 +577,7 @@ mod tests { } // The integer evaluator + the integrality check: at the real point √2 the - // negation `n*n = 2` holds over ℝ, but NO nearby integer (the radius-2 box + // negation `n*n = 2` holds over ℝ, but no nearby integer (the radius-2 box // {-1,0,1,2,3} around 1.41) satisfies `n*n = 2` → real-only (a RealWitness, not a // Counterexample). #[test] @@ -596,7 +596,7 @@ mod tests { } } - // A genuine integer counterexample IS caught: `ens result == n + 1` with the body + // An integer counterexample IS caught: `ens result == n + 1` with the body // returning n is false at, e.g., result=n → the negation holds over ℤ. #[test] fn integrality_check_catches_integer_counterexample() { diff --git a/forge/src/repair.rs b/forge/src/repair.rs index 8e01008e7..2236a1c6e 100644 --- a/forge/src/repair.rs +++ b/forge/src/repair.rs @@ -101,7 +101,7 @@ pub enum RepairVerdict { /// not in the repair set). #[derive(Debug, Clone)] pub enum SubL3Status { - /// A genuine inconclusiveness: a verus timeout (`VerusTimeout` reject, or a + /// A inconclusiveness: a verus timeout (`VerusTimeout` reject, or a /// `lowered_assurance` cert carrying a `VerusTimeout` degrade). This is the /// sole status [`escalate`] retries (REQ-2). Carries the #11 prompt material /// off the timed-out cert for the still-sub-L3 report. @@ -139,7 +139,7 @@ pub enum SubL3Status { /// REQ-6 / AC-1): `Level::L3` (proved), or a certified `L1`/`L2` with no reject /// (a `#[slag]`/boundary/explicit/degraded rung; repair drives the L3 verus /// budget, and a non-degraded certified lower rung was a chosen level). -/// - `Some(Timeout)` — a genuine inconclusiveness the ladder retries: a +/// - `Some(Timeout)` — a inconclusiveness the ladder retries: a /// `VerusTimeout` reject, or a `lowered_assurance` cert whose `degrade_reason` /// is a `VerusTimeout` (the #10 down-ladder's record of the very timeout repair /// re-attempts). This is the only status [`escalate`] runs for. @@ -430,7 +430,7 @@ where cause: cause.clone(), detail: detail.clone(), }), - // A genuine timeout (inconclusiveness) → the only status that escalates. + // A timeout (inconclusiveness) → the only status that escalates. SubL3Status::Timeout { .. } => escalate(status, verify), } } diff --git a/forge/src/review.rs b/forge/src/review.rs index 1154e6787..e3ed792e4 100644 --- a/forge/src/review.rs +++ b/forge/src/review.rs @@ -232,7 +232,7 @@ pub struct ReviewArtifact { /// source order (REQ-2; R-DEFER-9). pub battery_failing: Vec, /// The BURNED forge-tier lemmas (`.design/stage1-forge-tier.md` REQ-9, increment 3): - /// each certified `lemma` surfaced with its burn receipt — exactly as a certified item + /// each certified `lemma` surfaced with its burn receipt — as a certified item /// surfaces, so a reviewer sees the project's proven lemma library alongside the /// reviewed fns. In source order. Omitted (empty) on the default Verus path / the v1 /// corpus (which discharge no forge lemma), and `#[serde(default, @@ -240,7 +240,7 @@ pub struct ReviewArtifact { /// only, mirroring the cert layer's additive discipline). #[serde(default, skip_serializing_if = "Vec::is_empty")] pub burned_lemmas: Vec, - /// The `@bv`-tagged clauses' SHADOW FLAGS (`.design/stage3-bv-reconstruction.md` + /// The `@bv`-tagged clauses' shadow flags (`.design/stage3-bv-reconstruction.md` /// REQ-3 / AC-4 — Lock 1): every machine-semantics clause surfaced for review, so a /// reviewer sees the project's semantic forks alongside the contracts. One entry per /// tagged clause (read from each obligation's `bv_shadow`), in cert/obligation order. @@ -251,7 +251,7 @@ pub struct ReviewArtifact { #[serde(default, skip_serializing_if = "Vec::is_empty")] pub bv_shadows: Vec, /// The "semantic forks and definition towers" section - /// (`.design/stage3-bv-reconstruction.md` REQ-6 / AC-7): the AGGREGATE legibility + /// (`.design/stage3-bv-reconstruction.md` REQ-6 / AC-7): the aggregate legibility /// surface a reviewer reads alongside the per-clause `bv_shadows` + `burned_lemmas` /// above — bv-shadow density per module, every burned lemma's definition-tower depth, /// and the post-ship **F-F density tripwire**. A pure projection @@ -286,7 +286,7 @@ pub struct BvShadowClause { pub struct BurnedLemma { /// The lemma name (the certified item). pub item: String, - /// The committed-proof lexer-token count from the burn receipt (REQ-7 / Q-BURN). + /// The committed-proof lexer-token count from the burn receipt (REQ-7 / Q-burn). pub proof_tokens: usize, /// The lemmas this lemma's proof cited (post-dedup-rewrite, REQ-9) — empty if it cited /// none. Omitted when empty (mirrors the burn receipt's own field discipline). @@ -345,7 +345,7 @@ pub fn review_file( ) -> Result { let path = path.as_ref(); - // Parse the file once for the contract surface (REQ-1) AND to decide the route + // Parse the file once for the contract surface (REQ-1) and to decide the route // below. A re-parse of a file `check_file` re-validates (deterministic, R-CODE-5), // never a re-verification — the `audit` precedent. let src = std::fs::read_to_string(path).map_err(|e| ForgeError::Io { @@ -489,7 +489,7 @@ fn project_artifact( /// Project a certified forge-tier `lemma` cert into a [`BurnedLemma`] (REQ-9, increment 3), /// or `None` if `cert` is not a burned lemma. A burned lemma is a `cert` whose item is a -/// top-level `lemma` in `program`, that CERTIFIED ([`is_intent_reviewable`] — L3, no reject), +/// top-level `lemma` in `program`, that certified ([`is_intent_reviewable`] — L3, no reject), /// and that carries a burn receipt (the proof closed a goal). The projection reads the /// receipt's auditable figures verbatim — pure, never fabricated (R-CODE-5). fn burned_lemma_projection(program: &Program, cert: &Certificate) -> Option { @@ -1057,9 +1057,9 @@ mod tests { assert_eq!(artifact.intent_reviewable[0].item, "sum"); } - // REQ-9 (increment 3): a CERTIFIED forge-tier `lemma` carrying a burn receipt surfaces + // REQ-9 (increment 3): a certified forge-tier `lemma` carrying a burn receipt surfaces // in the review artifact's `burned_lemmas` partition — "like any certified item" — with - // its proof-token count + cited lemmas; it is NOT mis-filed as a battery-failing fn (a + // its proof-token count + cited lemmas; it is not mis-filed as a battery-failing fn (a // lemma has no fn contract). A v1 program (no lemma) carries an empty partition. #[test] fn burned_lemma_surfaces_in_review() { @@ -1106,7 +1106,7 @@ mod tests { ); } - // REQ-9: an UNcertified lemma (a `reject` cert) does NOT surface as a burned lemma — only + // REQ-9: an UNcertified lemma (a `reject` cert) does not surface as a burned lemma — only // a certified item does (the "like any certified item" rule). #[test] fn uncertified_lemma_does_not_surface_as_burned() { diff --git a/forge/src/sandbox.rs b/forge/src/sandbox.rs index 6d9a64904..3e9f7fd57 100644 --- a/forge/src/sandbox.rs +++ b/forge/src/sandbox.rs @@ -404,7 +404,7 @@ pub fn emit_sandbox_prelude(transitive_fx: &BTreeSet) -> String { format!( r##" // ---- thermite #57 runtime effect sandbox (seccomp-bpf, fx-derived) ---------- -// Installed as the FIRST statements of `main`, BEFORE the entry call, so the entry +// Installed as the first statements of `main`, before the entry call, so the entry // (and any boundary/slag body it reaches) runs UNDER the filter. A syscall off the // fx-derived allowlist -> SECCOMP_RET_KILL_PROCESS -> SIGSYS -> process killed. // Raw `extern "C"` prctl resolved against the std binary's linked libc (no libc @@ -483,9 +483,9 @@ pub fn emit_sandbox_prelude(transitive_fx: &BTreeSet) -> String { pub fn emit_probe() -> String { format!( r##" -// ---- thermite #57 sandbox self-test probe (--sandbox-self-test ONLY) -------- -// A raw openat AFTER the filter install: under a pure filter it is non-allowlisted -// -> SIGSYS -> the process is killed BEFORE the entry call (exit 159); under a +// ---- thermite #57 sandbox self-test probe (--sandbox-self-test only) -------- +// A raw openat after the filter install: under a pure filter it is non-allowlisted +// -> SIGSYS -> the process is killed before the entry call (exit 159); under a // read(_) filter openat is allowlisted -> the probe returns and the entry runs. {{ #[cfg(target_arch = "x86_64")] diff --git a/forge/src/seven_verdicts.rs b/forge/src/seven_verdicts.rs index 002ba8eae..a2d3444e1 100644 --- a/forge/src/seven_verdicts.rs +++ b/forge/src/seven_verdicts.rs @@ -9,11 +9,11 @@ //! Why the production boundary (not always the full binary): the relax route's //! `Proved`/`Counterexample`/`RealWitness` are produced by [`crate::engine::NlsatEngine`] //! (z3-gated — they run in CI where z3 is present); `CovenantRefuted` is produced by the -//! verus-FREE covenant engine (ungated); `Stuck`/`KernelBudget`/`Timeout` are produced -//! UPSTREAM by [`crate::verdict::cert_verdict_for_lean`] from the lean transcript (a LIVE +//! verus-free covenant engine (ungated); `Stuck`/`KernelBudget`/`Timeout` are produced +//! upstream by [`crate::verdict::cert_verdict_for_lean`] from the lean transcript (a live //! kernel-budget exhaustion is inherently non-deterministic, so the hermetic exercise is //! the deterministic transcript→verdict→cert production the live path funnels through — -//! `KernelBudget` additionally drives the real cert builder [`crate::check:: +//! `KernelBudget` additionally drives the cert builder [`crate::check:: //! lean_unverifiable_cert`]). Together: every one of the seven verdicts has a named, //! hermetic, CI-run test. The closure-instrumented never-degrades tests //! (`crate::degrade`) are unchanged. @@ -61,7 +61,7 @@ fn verdict_proved() { ); } -/// VERDICT 2/7 — **Counterexample**: a relaxable clause with a genuine INTEGER falsifier +/// VERDICT 2/7 — **Counterexample**: a relaxable clause with a INTEGER falsifier /// (the real relaxation is `sat` and an integer point in the radius-2 box falsifies it) is /// a `Counterexample` carrying the integer witness — never escalated. z3-gated. #[test] @@ -85,8 +85,8 @@ fn verdict_counterexample() { /// VERDICT 3/7 — **RealWitness**: a clause true over ℤ but false over ℝ (`∀ n, n·n ≠ 2`) /// yields a `RealWitness` carrying the raw real point (√2), escalated UP to the forge — -/// NEVER a `Counterexample`. This is the relax PRODUCER path the AC-14 audit flagged; it is -/// z3-gated, so it runs in the CI lean job (z3 ships with verus). The matching UNGATED +/// never a `Counterexample`. This is the relax producer path the AC-14 audit flagged; it is +/// z3-gated, so it runs in the CI lean job (z3 ships with verus). The matching ungated /// structural producer test lives in `engine.rs` /// (`classify_sat_real_only_model_is_real_witness`). #[test] @@ -112,7 +112,7 @@ fn verdict_real_witness() { } /// VERDICT 4/7 — **CovenantRefuted**: a planted-bug `fn` whose body violates its `ens` on a -/// `req`-satisfying input is refuted by the verus-FREE covenant `falsify` run (a hard fail, +/// `req`-satisfying input is refuted by the verus-free covenant `falsify` run (a hard fail, /// never degraded). Ungated — the covenant engine is pure executable evaluation, no prover. #[test] fn verdict_covenant_refuted() { @@ -142,7 +142,7 @@ fn verdict_covenant_refuted() { /// VERDICT 5/7 — **Stuck**: a lean proof that ELABORATED but left a residual goal /// ("unsolved goals") is `Stuck` (the frozen-battery residual + missing-bridge hint), -/// produced UPSTREAM by [`cert_verdict_for_lean`] — never silently `Proved` and never the +/// produced upstream by [`cert_verdict_for_lean`] — never silently `Proved` and never the /// solver `Timeout` the 3-arm engine map would assign. Ungated (a transcript→verdict /// producer test; the live discharge that emits such a transcript is exercised by /// `battery_conformance.rs`). @@ -162,10 +162,10 @@ fn verdict_stuck() { /// VERDICT 6/7 — **KernelBudget**: a lean elaboration/kernel-budget exhaustion (the /// textually-distinct `(deterministic) timeout … maximum number of heartbeats` signal) is -/// `KernelBudget`, produced UPSTREAM — NEVER mis-mapped to the solver `Timeout`. This is the -/// AC-14 e2e gap: a LIVE budget exhaustion is non-deterministic, so the hermetic exercise +/// `KernelBudget`, produced upstream — never mis-mapped to the solver `Timeout`. This is the +/// AC-14 e2e gap: a live budget exhaustion is non-deterministic, so the hermetic exercise /// drives the deterministic production path the live discharge funnels through — both the -/// verdict producer ([`cert_verdict_for_lean`]) AND the real cert builder +/// verdict producer ([`cert_verdict_for_lean`]) and the cert builder /// ([`crate::check::lean_unverifiable_cert`]), asserting the produced certificate is /// classified `KernelBudget`. #[test] @@ -182,7 +182,7 @@ fn verdict_kernel_budget() { "KernelBudget", "a heartbeat-timeout transcript is KernelBudget" ); - // (b) the real cert builder produces a certificate that honestly attributes the + // (b) the cert builder produces a certificate that attributes the // budget exhaustion (end-to-end through the production cert path). let base = Certificate::new( "kb_item", @@ -207,7 +207,7 @@ fn verdict_kernel_budget() { } /// VERDICT 7/7 — **Timeout**: a solver resource-limit (rlimit) exhaustion is the -/// `Verdict::Unknown` image under the TOTAL engine map — a `Timeout`, the only non-kernel, +/// `Verdict::Unknown` image under the total engine map — a `Timeout`, the only non-kernel, /// non-residual incompleteness. Ungated (the engine-map producer; the live forced-timeout /// run is exercised by `profile_conformance.rs`). #[test] diff --git a/forge/src/strat_faithful.rs b/forge/src/strat_faithful.rs index a2da2d6fd..1311e1ce0 100644 --- a/forge/src/strat_faithful.rs +++ b/forge/src/strat_faithful.rs @@ -7,20 +7,20 @@ //! with the independent stratified reference encoder //! (`thermite_tv::strat_ref_encode`), validates the production lowering against it //! through the two phases (syntactic normalizer → thin semantic fallback), reports the -//! phase split, and assigns each CERTIFIED clause its `trust:` profile under the G2 gate +//! phase split, and assigns each certified clause its `trust:` profile under the G2 gate //! (`thermite_tv::strat_two_phase::G2_FLIPPED`). //! //! The flip is the central deliverable: during the rollout window a certified stratified //! clause carries `trust: solver(z3) + ref_encode(strat, UNPROVEN — stage 2 in //! progress)`; the one-line `G2_FLIPPED` change switches it to the proven form //! `ref_encode(strat)` once `make audit`'s [1′][4′][8][9] are green (REQ-9). A WITHHELD -//! (timeout) or DIVERGENT clause is NOT given the profile — it keeps the conservative +//! (timeout) or DIVERGENT clause is not given the profile — it keeps the conservative //! cage trust. //! //! The real Z3 discharge of the rarely-hit semantic phase is wired by the audit //! integration (REQ-9, check [9]); absent a wired solver this orchestrator is //! conservative — a syntactic MISS WITHHOLDS (never a false pass), so the generated -//! faithful stream (every clause a syntactic hit) certifies while any genuine miss is +//! faithful stream (every clause a syntactic hit) certifies while any miss is //! surfaced as withheld for the solver pass to adjudicate. use thermite_tv::strat_two_phase::{ @@ -41,7 +41,7 @@ pub const STRAT_FAITHFUL_DEFAULT_SEED: u64 = 0x5354_5246_4149_5448; // "STRFAITH pub struct StratFaithfulReport { /// The two-phase split (syntactic / semantic / timeout-withheld / divergent). pub split: PhaseSplit, - /// The `trust:` profile every CERTIFIED clause carries under the compiled-in gate + /// The `trust:` profile every certified clause carries under the compiled-in gate /// (`G2_FLIPPED`). Empty if no clause certified. pub trust_profile: Vec, /// Whether the compiled-in gate is the proven (post-G2) form. @@ -72,7 +72,7 @@ pub fn run_generated(seed: u64, n: usize) -> StratFaithfulReport { let reference = strat_ref_encode(phi); // The production lowering of an admitted clause is faithful to the reference // by T1-S/T2-S; here both come from the independent encoder, so a correct - // clause is a syntactic hit. (The real production string is supplied by + // clause is a syntactic hit. (The production string is supplied by // `thermite_lower` in the audit wiring, REQ-9.) StratClause { label: format!("gen:{i}"), @@ -84,11 +84,11 @@ pub fn run_generated(seed: u64, n: usize) -> StratFaithfulReport { .collect(); // Conservative semantic oracle: a syntactic miss has no wired solver yet, so WITHHOLD - // (honest — never a false pass). REQ-9 replaces this with the finite-bound Z3 query. + // ( never a false pass). REQ-9 replaces this with the finite-bound Z3 query. let report = run_two_phase(&clauses, |_obligation| SemanticOutcome::Timeout); - // The sweep vouches for audit check [9] DIRECTLY (its own two-phase verdict); the other - // three gating checks ([1′][4′][8]) are the G2 DECLARATION's responsibility (`G2_FLIPPED` + // The sweep vouches for audit check [9] directly (its own two-phase verdict); the other + // three gating checks ([1′][4′][8]) are the G2 declaration's responsibility (`G2_FLIPPED` // is only set because `make audit` saw them green — `forge g2-gate` mechanically enforces // it). So the emitted `trust:` profile routes through the gate with [9] = this sweep's // pass, downgrading to the conservative `UNPROVEN` form automatically if a clause diverged @@ -168,7 +168,7 @@ mod tests { fn the_trust_profile_is_the_proven_scoped_form_at_g2() { // REQ-9 reached G2: a PASSING sweep vouches for check [9], and the declaration // (`G2_FLIPPED`) carries [1′][4′][8], so the gated profile reads the proven - // (honestly scoped) reference-encoder string — no UNPROVEN. The mechanical block (a + // scoped reference-encoder string — no UNPROVEN. The mechanical block (a // red check downgrading the label) is covered by the gate-toggle tests in // `thermite_tv::strat_two_phase` (AC-9). let r = run_generated(STRAT_FAITHFUL_DEFAULT_SEED, 8); diff --git a/forge/src/strat_tv.rs b/forge/src/strat_tv.rs index 0b5a6ada0..d491853a2 100644 --- a/forge/src/strat_tv.rs +++ b/forge/src/strat_tv.rs @@ -5,14 +5,14 @@ //! classifier `Thermite.Strat.Cls.admitted` (`lean/Thermite/Strat/Fragment.lean`, REQ-3). //! This module is the differential that holds the two byte-equal: it draws a //! deterministic, well-sorted formula stream from the SplitMix64 generator -//! (`thermite_tv::gen::gen_strat_formulas`), classifies each with BOTH the Rust -//! classifier AND the Lean kernel `admitted` (via `lake env lean --run +//! (`thermite_tv::gen::gen_strat_formulas`), classifies each with both the Rust +//! classifier and the Lean kernel `admitted` (via `lake env lean --run //! Thermite/Strat/Cls/Wire.lean`, fed the shared S-expression wire format on stdin), and //! compares verdict-for-verdict. //! -//! - A **disagreement** (Rust admit ≠ Lean admit, both definite) is a real classifier +//! - A **disagreement** (Rust admit ≠ Lean admit, both definite) is a classifier //! infidelity → surfaced as a verification-failure `ExitCode` (the hard CI failure the -//! audit check [8] gate raises), NOT a `ForgeError`. Mirrors `contract_tv`'s divergent +//! audit check [8] gate raises), not a `ForgeError`. Mirrors `contract_tv`'s divergent //! handling. //! - The **unknown-on-admitted tripwire**: a formula the Rust classifier could not vouch //! for ([`thermite_spec::Verdict::Unknown`], or a Lean `parse-error`) while the kernel @@ -21,7 +21,7 @@ //! Rust classifier is total, so the count is structurally 0. //! - A harness/environment failure (lake un-spawnable, the Lean driver exits non-zero, //! the verdict-line count desyncs) is a [`ForgeError::StratDifferential`]; lake-absent -//! is an honest `Skipped` (not run), never a false pass. +//! is an `Skipped` (not run), never a false pass. //! //! ## REQ status //! @@ -48,7 +48,7 @@ pub const STRAT_TV_DEFAULT_N: usize = 200; pub const STRAT_TV_DEFAULT_SEED: u64 = 0x5354_5241_5430_3034; // "STRAT004" /// One verdict disagreement between the Rust classifier and the Lean kernel `admitted` — -/// a real classifier infidelity (the hard-failure finding). +/// a classifier infidelity (the hard-failure finding). #[derive(Debug, Clone, PartialEq, Eq)] pub struct Disagreement { /// The formula's 0-based index in the generated stream (reproducible from the seed). @@ -79,7 +79,7 @@ pub struct StratTvReport { } impl StratTvReport { - /// The battery passes iff there is no disagreement AND no tripwire (AC-4). A failing + /// The battery passes iff there is no disagreement and no tripwire (AC-4). A failing /// battery maps to a verification-failure `ExitCode` at the CLI. #[must_use] pub fn passed(&self) -> bool { @@ -87,7 +87,7 @@ impl StratTvReport { } } -/// The outcome of a differential run: it either ran end-to-end, or was honestly skipped +/// The outcome of a differential run: it either ran end-to-end, or was skipped /// because `lake` is absent (the local-without-Lean case — never a false pass). #[derive(Debug, Clone)] pub enum StratTvOutcome { @@ -106,7 +106,7 @@ fn lean_root() -> PathBuf { /// Locate the `lake` binary (mirrors `engine::LeanEngine::lake_binary`): the /// elan-managed `~/.elan/bin/lake` if present (so a non-login shell still finds it), else -/// the bare `lake` on PATH. `None` if neither is available — the honest `Skipped` case. +/// the bare `lake` on PATH. `None` if neither is available — the `Skipped` case. fn lake_binary() -> Option { if let Some(home) = std::env::var_os("HOME") { let elan = PathBuf::from(home).join(".elan/bin/lake"); @@ -166,7 +166,7 @@ fn compare(formulas: &[Frm], rust: &[Verdict], lean_lines: &[String]) -> StratTv if rust_admitted { report.tripwire_unknown_on_admitted += 1; } else { - // Rust rejected AND the kernel could not parse: still a + // Rust rejected and the kernel could not parse: still a // harness anomaly, but not the dangerous admit case — count it // as a disagreement so it is never silently dropped. report.disagreements.push(Disagreement { @@ -207,10 +207,10 @@ fn compare(formulas: &[Frm], rust: &[Verdict], lean_lines: &[String]) -> StratTv /// wire encodings to `lake env lean --run Thermite/Strat/Cls/Wire.lean` on stdin, reads /// the kernel verdicts, and compares. /// -/// Returns [`StratTvOutcome::Skipped`] (never an error) if `lake` is absent — the honest +/// Returns [`StratTvOutcome::Skipped`] (never an error) if `lake` is absent — the /// not-run case. A [`ForgeError::StratDifferential`] is a harness/environment failure /// (spawn failure, non-zero Lean exit, verdict-line desync), surfaced not swallowed -/// (R-CODE-4). A verdict DISAGREEMENT is NOT an error — it lands in the returned report's +/// (R-CODE-4). A verdict DISAGREEMENT is not an error — it lands in the returned report's /// `disagreements` for the CLI to surface as a verification-failure exit. pub fn run_generated(seed: u64, n: usize) -> Result { let Some(lake) = lake_binary() else { diff --git a/forge/src/strengthen.rs b/forge/src/strengthen.rs index abf901622..ac1ce6b7a 100644 --- a/forge/src/strengthen.rs +++ b/forge/src/strengthen.rs @@ -1,10 +1,10 @@ //! `forge/src/strengthen.rs` — §7 step 5 of the vacuity battery: strengthening //! probes (`thermite-design.md` §7 "template-based tightenings of `ens` … if a //! strictly stronger contract proves with no body change, Forge suggests it"). -//! Given a `fn` whose real body already proved L3 but whose contract is weak +//! Given a `fn` whose body already proved L3 but whose contract is weak //! (#12 mutation scoring found one or more survivors: behavior the `ens` does //! not pin), this module generates a frozen, deterministic, bounded set of -//! candidate stronger `ens` clauses, verifies each against the real body by +//! candidate stronger `ens` clauses, verifies each against the body by //! reusing the existing verus driver (`check::run_verus`, threaded as a verify //! closure), and surfaces the candidates that (a) verify against the body and (b) //! are strictly stronger than the current `ens` as adoptable [`Suggestion`]s. @@ -85,7 +85,7 @@ pub struct CandidateClause { /// One adoptable strengthening suggestion surfaced on a certificate (REQ-4). It /// is the §7 step-5 "consider strengthening `ens` with `` — it holds for /// your body and would kill survivor ``" prompt, made concrete: the `clause` -/// verifies against the real body (so it is adoptable with no body change) and is +/// verifies against the body (so it is adoptable with no body change) and is /// strictly stronger than the current `ens` (so it narrows the allowed outputs). #[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] pub struct Suggestion { @@ -333,7 +333,7 @@ pub fn generate_candidates( /// Build a copy of `f` whose `ens` is replaced by the single candidate clause /// (REQ-2). The body, `req`, and `fx` are unchanged: only the postcondition is -/// the candidate, so the verify step proves the candidate against the real body +/// the candidate, so the verify step proves the candidate against the body /// (the §7 step-5 "proves with no body change"). The candidate replaces the `ens` /// (rather than conjoining) so the verify run is a clean test of the candidate /// alone; a candidate strictly stronger than the current `ens` that proves on its @@ -433,7 +433,7 @@ fn is_slice_param(ty: &Type) -> bool { matches!(ty, Type::Ref { inner, .. } if matches!(inner.as_ref(), Type::Slice(_))) } -/// `true` iff `f`'s parameter types match `params` in order AND `f`'s return type +/// `true` iff `f`'s parameter types match `params` in order and `f`'s return type /// matches `ret` (the family-1 spec-fn-equality signature check). A `spec fn` /// whose signature matches `f`'s can be applied to `f`'s parameters to pin the /// result (`result == s()`). @@ -453,7 +453,7 @@ fn spec_fn_signature_matches(f: &FnItem, params: &[thermite_syntax::Param], ret: /// Run the strengthening probe over `f` (REQ-2/REQ-3/REQ-4). For each candidate /// (in the deterministic family order): /// -/// 1. Verify the candidate against the real body via `verify_body` (the threaded +/// 1. Verify the candidate against the body via `verify_body` (the threaded /// `check::run_verus` of the `item_subprogram` shape, content-addressed via the /// #8 cache). A candidate that does not verify is discarded (no unadoptable /// suggestion, R-DEFER-1). A `ForgeError` propagates (R-CODE-4). @@ -481,7 +481,7 @@ pub fn probe( let mut suggestions = Vec::new(); for candidate in &candidates { - // REQ-2: the candidate must verify against the real body to be adoptable. + // REQ-2: the candidate must verify against the body to be adoptable. let woven = candidate_fn(f, candidate); if !verify_body(&woven)? { continue; diff --git a/forge/src/tv_signal.rs b/forge/src/tv_signal.rs index 171c49450..81e0d1f89 100644 --- a/forge/src/tv_signal.rs +++ b/forge/src/tv_signal.rs @@ -65,19 +65,19 @@ pub(crate) fn is_rlimit_signal(output: &str) -> bool { /// signal above (`.design/stage1-forge-tier.md` REQ-1b / AC-2, Q-KBSIGNAL). This is the /// second discriminator in the same one-shared-helper pattern: a budget-exhausted Lean /// discharge is the cert verdict `KernelBudget` (the forge tier's Q4 30s/clause -/// elaboration budget), NOT a solver `Timeout` and NOT a meaning mismatch / `Stuck`. +/// elaboration budget), not a solver `Timeout` and not a meaning mismatch / `Stuck`. /// /// ## Q-KBSIGNAL probe result (recorded in the increment commit) /// /// The probe asked whether Lean emits a textually-distinct budget signal vs the Z3 -/// rlimit text. It DOES — `lake env lean` was driven to each budget edge and emits: +/// rlimit text. It does — `lake env lean` was driven to each budget edge and emits: /// - `(deterministic) timeout at , maximum number of heartbeats (N) has been reached` /// — the elaboration HEARTBEAT budget (`set_option maxHeartbeats`); /// - `maximum recursion depth has been reached` — the elaboration/kernel RECURSION /// budget (`set_option maxRecDepth`). /// /// So the probe selected the DISCRIMINATOR path (AC-2's "a distinct signal exists" -/// branch), NOT the wall-clock-wrapper fallback. The phrase set is mutually exclusive +/// branch), not the wall-clock-wrapper fallback. The phrase set is mutually exclusive /// with [`is_rlimit_signal`]'s (neither carries `rlimit`/`resource limit`; the rlimit /// phrases carry no `timeout`/`heartbeats`/`recursion depth`), proven by the /// `kernel_budget_and_rlimit_cannot_be_confused` negative test. @@ -88,7 +88,7 @@ pub(crate) fn is_kernel_budget_signal(output: &str) -> bool { || lower.contains("maximum recursion depth") } -// ---- the discriminator teeth (the shared-helper unit coverage) -------------- +// ---- discriminator regression tests ---------------------------------------- // // Both directions, hand-derived (R-CHAR-3): each rlimit phrase is detected; a // counterexample diagnostic (`postcondition not satisfied` / `assertion @@ -148,14 +148,14 @@ mod discriminator { } /// The negative test (REQ-1b / AC-2): the kernel-budget and solver-rlimit - /// discriminators CANNOT be confused. The canonical rlimit signals are NOT detected - /// as a kernel budget, and the canonical kernel-budget signals are NOT detected as an + /// discriminators CANNOT be confused. The canonical rlimit signals are not detected + /// as a kernel budget, and the canonical kernel-budget signals are not detected as an /// rlimit — the two phrase sets are mutually exclusive, so a solver timeout is never /// mis-classed `KernelBudget` and a Lean budget exhaustion is never mis-classed /// `Timeout`. #[test] fn kernel_budget_and_rlimit_cannot_be_confused() { - // The rlimit signals are NOT kernel-budget. + // The rlimit signals are not kernel-budget. for rlimit in [ "error: rlimit exceeded; consider raising the budget", "error: Resource limit (rlimit) exceeded\n0 verified, 1 errors", @@ -170,7 +170,7 @@ mod discriminator { "an rlimit signal must NOT be detected as a kernel/elaboration budget: {rlimit:?}" ); } - // The kernel-budget signals are NOT rlimit. + // The kernel-budget signals are not rlimit. for budget in [ "error: (deterministic) timeout at `whnf`, maximum number of heartbeats (1) has been \ reached", @@ -189,7 +189,7 @@ mod discriminator { /// A counterexample diagnostic is not detected — it stays in the /// Divergent class (the discriminator's negative direction). Neither the rlimit nor - /// the kernel-budget discriminator fires on a genuine counterexample. + /// the kernel-budget discriminator fires on a counterexample. #[test] fn genuine_counterexample_is_not_detected() { assert!( diff --git a/forge/src/vacuity.rs b/forge/src/vacuity.rs index 6e0cedb71..e86f5d057 100644 --- a/forge/src/vacuity.rs +++ b/forge/src/vacuity.rs @@ -179,7 +179,7 @@ fn ens_is_trivially_true(ens: &[thermite_syntax::Clause]) -> Option { /// A syntactically-trivial identity clause: an `Eq`/`Le`/`Ge` whose `lhs` and /// `rhs` are structurally identical (`PartialEq`). `x == x` / `x <= x` / `x >= x` /// are all trivially true. `<`/`>`/`!=` are not identities (`x < x` is false), and -/// `Eq` with differing operands is a real obligation. +/// `Eq` with differing operands is a obligation. fn identity_clause(expr: &Expr) -> bool { matches!( expr, @@ -353,7 +353,7 @@ fn index_arg_mentions_result(index: &thermite_syntax::IndexArg, depth: usize) -> /// whole postcondition conjunction adds nothing. So the rule fires only when every /// clause is req-implied. A contract with a redundant implied clause and a /// stronger clause (`req x > 0 && x < 10` / `ens x > 0` / `ens result == x`) -/// carries a real obligation (`result == x` is not a req conjunct) and is not +/// carries a obligation (`result == x` is not a req conjunct) and is not /// (c)-rejected. Returns the first req-implied `ens` clause index (for the /// diagnostic) only when every clause matches; `None` otherwise. Syntactic only: /// the solver "is `ens` provable from `req`" question is #13. diff --git a/forge/src/vacuity_solver.rs b/forge/src/vacuity_solver.rs index 6c42c8166..5f43b9d8f 100644 --- a/forge/src/vacuity_solver.rs +++ b/forge/src/vacuity_solver.rs @@ -19,7 +19,7 @@ //! //! Both checks reuse the existing Verus contract lowering: each builds a one-query //! `proof fn` harness by lowering the item via `thermite_lower::lower` (so the -//! emitted `requires`/`ensures` text is byte-identical to the real proof's, with +//! emitted `requires`/`ensures` text is byte-identical to the proof's, with //! the combinator + `spec fn` weaving the lowerer already performs) and splicing //! that verbatim contract into the harness frame. The harness is run through verus //! and the verdict interpreted (REQ-3): a verus success is the bad news @@ -145,7 +145,7 @@ struct HarnessSummary { /// root cause is the unsatisfiable `req`. So the unsat-precondition check /// runs first: a contract whose `req` is unsat is reported as `VacuousPrecondition` /// (its true defect), and the tautology check then runs only on a satisfiable -/// precondition, where a proved `ens`-for-arbitrary-result is a genuine tautology +/// precondition, where a proved `ens`-for-arbitrary-result is a tautology /// rather than an artifact of a false premise. This is an implementation precedence /// within the solver stage, not a contract/cause change: both checks and both /// causes are as the design specifies; only which fires first when both @@ -180,7 +180,7 @@ pub fn solver_vacuity_check( // §7 step 2: tautology (assume req / arbitrary result / assert ens). Reached // only when the `req` is satisfiable, so a proved `ens` for an arbitrary result - // is a genuine semantic tautology rather than an artifact of a false premise. + // is a semantic tautology rather than an artifact of a false premise. let taut = build_tautology_harness(f, spec_items, adt_items)?; if matches!( run_harness(&taut, "taut", seed, rlimit)?, @@ -200,7 +200,7 @@ pub fn solver_vacuity_check( /// The pieces of a lowered `fn` a harness reuses verbatim (REQ-1/REQ-2). Extracted /// from `thermite_lower::lower`'s output so the harness's contract text is -/// byte-identical to the real proof's (no re-emission of `req`/`ens` by hand). +/// byte-identical to the proof's (no re-emission of `req`/`ens` by hand). struct LoweredFn { /// Everything inside `verus! {` before the target `fn NAME(`: the woven /// combinator `spec fn` defs, the file's `spec fn`s, and any push-lemma @@ -214,8 +214,8 @@ struct LoweredFn { /// The lowered return type from `-> (result: )` (e.g. `u64`, /// `Option`). The arbitrary-result binder type (OQ-4). ret: String, - /// The lowered `requires` region's lines, captured VERBATIM (each line with its - /// own indentation and trailing comma exactly as the lowerer emitted it), + /// The lowered `requires` region's lines, captured verbatim (each line with its + /// own indentation and trailing comma as the lowerer emitted it), /// including the `requires` keyword line. Empty when the lowerer omitted the /// clause (`req` literally `true`, a trivially-satisfiable precondition that is /// never vacuous), so the harness simply has no `requires`. Verbatim capture @@ -224,7 +224,7 @@ struct LoweredFn { /// appended after every physical line (crosslink #275: the per-line /// reconstruction produced `match result {,` and the harness failed to compile). requires_lines: Vec, - /// The lowered `ensures` region's lines, captured VERBATIM (including the + /// The lowered `ensures` region's lines, captured verbatim (including the /// `ensures` keyword line and every clause line, each as the lowerer emitted /// it). Used only by the tautology harness. Verbatim capture preserves a /// multi-line `match result { … }` ens as valid Verus (the #275 fix; the prior @@ -232,7 +232,7 @@ struct LoweredFn { ensures_lines: Vec, } -/// Build the §7 step-2 tautology harness for `f` (REQ-1). Lowers the real item via +/// Build the §7 step-2 tautology harness for `f` (REQ-1). Lowers the item via /// `thermite_lower::lower` and rebuilds: /// /// ```text @@ -262,7 +262,7 @@ fn build_tautology_harness( // The harness signature: real params plus the arbitrary `result` binder. let params = append_result_param(&lf.params, &lf.ret); out.push_str(&format!("proof fn taut_check({params})\n")); - // Splice the lowered `requires` + `ensures` regions VERBATIM (each line as the + // Splice the lowered `requires` + `ensures` regions verbatim (each line as the // lowerer emitted it, including the keyword lines and original commas), so a // multi-line `match`/`forall` clause reconstructs as valid Verus (#275). for line in &lf.requires_lines { @@ -278,7 +278,7 @@ fn build_tautology_harness( Ok(out) } -/// Build the §7 step-3 vacuity harness for `f` (REQ-2). Lowers the real item and +/// Build the §7 step-3 vacuity harness for `f` (REQ-2). Lowers the item and /// rebuilds: /// /// ```text @@ -333,13 +333,13 @@ fn append_result_param(params: &str, ret: &str) -> String { /// sub-program) and extract the lowered preamble + signature + verbatim /// `requires`/`ensures` lines (REQ-1/REQ-2). This is the reuse the harness rests /// on: the harness's contract text is the same bytes the real L3 proof sees, so a -/// tautology/vacuity verdict reflects the real contract rather than a +/// tautology/vacuity verdict reflects the contract rather than a /// re-derivation. /// /// `adt_items` are the reachable `Item::Struct`/`Item::Enum` declarations the /// caller resolved (`check::reachable_adt_deps`, the same set woven into the L3 /// sub-program). An ADT-returning / ADT-taking `fn` (`-> Account`, `a: Shape`) -/// whose harness omitted these decls failed to COMPILE (`error[E0425]: cannot +/// whose harness omitted these decls failed to compile (`error[E0425]: cannot /// find type`), and a non-compiling harness was silently read as "not a tautology /// / not vacuous" — both anti-Goodhart checks then no-op'd on every ADT fn /// (crosslink #275). Weaving the ADT decls first (so the synthetic `proof fn`'s @@ -353,7 +353,7 @@ fn extract_lowered_fn( adt_items: &[Item], ) -> Result { // The same sub-program shape `check::item_subprogram` builds for the L3 `Fn` - // path: the reachable `struct`/`enum` decls FIRST (#68 — so the type decls + + // path: the reachable `struct`/`enum` decls first (#68 — so the type decls + // their `well_formed` invariants are in scope before any fn that references // them), then the file's `spec fn`s (pure shared deps a contract may // reference), then the target `fn` last (so a forward reference resolves; the @@ -424,7 +424,7 @@ fn parse_lowered_fn(lowered: &str, name: &str) -> Result // The `requires` / `ensures` lines between the signature and the body's `{`. // The lowerer emits ` requires ,` (omitted when `req` is literally // `true`) then ` ensures\n ,\n ...`, then the body opener `{`. - // Capture each region's lines VERBATIM (with the lowerer's own indentation and + // Capture each region's lines verbatim (with the lowerer's own indentation and // trailing commas, keyword lines included) up to the first line whose trimmed // form is `{` (the body block opener `lower_fn` emits). Verbatim capture — not // per-clause re-emission — is the #275 fix: a multi-line `ens` (the @@ -542,7 +542,7 @@ fn lowering_shape_error(what: &str) -> ForgeError { /// exit path: success, a clean failure, or a `?` early-return on an environment/IO /// error. Reuses `check.rs`'s #53 guard (the identical fix). Cleanup is /// best-effort (`Drop` does a `let _ = remove_dir_all`), never a panic (R-CODE-2): -/// a removal failure must not mask the real verus result. +/// a removal failure must not mask the verus result. /// /// R-CODE-4: every environment / internal failure surfaces a `ForgeError` and is /// not read as either "tautology" or "clean": @@ -643,14 +643,14 @@ fn invoke_verus_on_harness( } /// Map a parsed harness summary to a [`HarnessOutcome`] (REQ-3, R-CODE-4). The -/// solver-vacuity polarity, with the COMPILE-vs-VERIFY distinction the #275 fix -/// makes load-bearing: +/// solver-vacuity polarity, with the compile-vs-VERIFY distinction the #275 fix +/// makes required: /// /// - a VIR / internal verus error → `ForgeError::VerusOutput` (an environment /// condition, not a verdict, never a silent clean `false`); -/// - PROVED (`success && errors == 0`) → `Proved`: the harness property holds, +/// - proved (`success && errors == 0`) → `Proved`: the harness property holds, /// which is the bad news (the contract is degenerate, so the caller rejects); -/// - a genuine NON-PROOF (`!success && errors >= 1`) → `Failed`: the harness +/// - a NON-proof (`!success && errors >= 1`) → `Failed`: the harness /// COMPILED and verus checked its obligation (the empty-body `ens`, or the /// `assert(false)`) and could not prove it — the good news, the contract is /// non-degenerate, so clean. A counterexample, a failed assert, and an @@ -658,15 +658,15 @@ fn invoke_verus_on_harness( /// verification error), so a timeout still reads as `Failed` (the conservative /// OQ-3 polarity: an inconclusive query does not reject); /// - a NON-VERDICT (`!success && errors == 0`) → `ForgeError::VerusOutput`: a -/// `!success` run that reported ZERO verification errors never reached the -/// verification phase — the harness failed to COMPILE / elaborate (an `E0425` +/// `!success` run that reported zero verification errors never reached the +/// verification phase — the harness failed to compile / elaborate (an `E0425` /// unresolved name, a parse / type error). That is a HARNESS CONSTRUCTION -/// failure, NOT "verus checked the obligation and it failed" (R-CODE-4: a +/// failure, not "verus checked the obligation and it failed" (R-CODE-4: a /// non-verdict must never be read as a clean `Failed`). Before #275, this case /// mapped to `Failed` → clean, so every ADT-returning / ADT-taking `fn` whose /// harness lacked the `struct`/`enum` decls (the now-fixed weave above) silently -/// bypassed BOTH anti-Goodhart checks. The discriminator is `errors`: verus's -/// `verification-results.errors` counts only VERIFICATION failures, so a +/// bypassed both anti-Goodhart checks. The discriminator is `errors`: verus's +/// `verification-results.errors` counts only verification failures, so a /// compiled harness with an obligation is either `success` (proved) or /// `errors >= 1` (checked-and-failed) — `errors == 0` with `!success` is /// exclusively the never-verified (compile) case (confirmed against verus @@ -689,7 +689,7 @@ fn interpret_summary(summary: HarnessSummary, stderr: &str) -> Result, }, /// A real countermodel of a clause true over ℤ, false over ℝ (REQ-8/2f) → escalate - /// UP to the forge, NEVER `Counterexample`. Carries the raw real point. + /// UP to the forge, never `Counterexample`. Carries the raw real point. RealWitness { /// The raw real point the nlsat query returned. point: RealPoint, @@ -149,13 +149,13 @@ impl CertVerdict { matches!(self, CertVerdict::Proved) } - /// The TOTAL map from the three-arm engine [`Verdict`] into the cert vocabulary - /// (REQ-1 / AC-1), by an EXHAUSTIVE match with NO wildcard arm: `Proven → Proved`, + /// The total map from the three-arm engine [`Verdict`] into the cert vocabulary + /// (REQ-1 / AC-1), by an exhaustive match with no wildcard arm: `Proven → Proved`, /// `Refuted → Counterexample`, `Unknown → Timeout`. Only these three of the seven /// have an engine-level source; the other four are produced upstream (see the - /// module docs). The map is total, so NO `engine::Verdict::Unknown` survives into a + /// module docs). The map is total, so no `engine::Verdict::Unknown` survives into a /// certificate, and — the never-converts-silently invariant (R-VERDICT-1) — `Proved` - /// is constructed ONLY from `Proven`. + /// is constructed only from `Proven`. #[must_use] pub fn from_engine_verdict(v: &Verdict) -> Self { match v { @@ -173,7 +173,7 @@ impl CertVerdict { /// The human detail carried by an engine [`Reason`] (exhaustive, no wildcard): both /// non-`Proven`/`Refuted` reasons are solver-budget/incompleteness events that the cert /// vocabulary classes as `Timeout` (a Lean KERNEL budget is discriminated upstream -/// BEFORE this map, by [`cert_verdict_for_lean`], so it never reaches here as a +/// before this map, by [`cert_verdict_for_lean`], so it never reaches here as a /// `Timeout`). fn reason_detail(reason: &Reason) -> String { match reason { @@ -188,7 +188,7 @@ fn reason_detail(reason: &Reason) -> String { /// 1. A Lean elaboration/kernel-budget exhaustion carries a textually-distinct signal /// ([`crate::tv_signal::is_kernel_budget_signal`]) that the Z3 rlimit text never /// matches and vice-versa (the negative test in `tv_signal`), so a budget exhaustion -/// is classed `KernelBudget` UPSTREAM — never routed through the 3-arm +/// is classed `KernelBudget` upstream — never routed through the 3-arm /// [`CertVerdict::from_engine_verdict`] map (which would mis-call it `Timeout`). /// 2. A proof that ELABORATED but left a residual goal ("unsolved goals", REQ-5/2c) is /// [`CertVerdict::Stuck`] — the residual goal(s) + the frozen-battery missing-bridge @@ -251,7 +251,7 @@ mod tests { }) } - /// The total map is EXHAUSTIVE with no wildcard: each of the three engine arms maps + /// The total map is exhaustive with no wildcard: each of the three engine arms maps /// to exactly its cert image; `Unknown` (both reasons) becomes `Timeout`, so no /// `Unknown` survives (REQ-1 / AC-1). #[test] @@ -278,7 +278,7 @@ mod tests { } /// The never-converts-silently invariant (R-VERDICT-1 / AC-3): `Proved` is - /// constructed ONLY from `Proven`. A `Refuted`/`Unknown` never yields `Proved`. + /// constructed only from `Proven`. A `Refuted`/`Unknown` never yields `Proved`. #[test] fn proved_is_constructed_only_from_proven() { assert!(CertVerdict::from_engine_verdict(&proven()).is_proved()); @@ -291,8 +291,8 @@ mod tests { ); } - /// A Lean kernel/elaboration-budget exhaustion is produced UPSTREAM as - /// `KernelBudget`, NOT mapped to `Timeout` (Q-KBSIGNAL). A residual-goal failure is + /// A Lean kernel/elaboration-budget exhaustion is produced upstream as + /// `KernelBudget`, not mapped to `Timeout` (Q-KBSIGNAL). A residual-goal failure is /// `Stuck` (REQ-5/2c), and only a budget-less, residual-less incompleteness falls /// through to the total engine map as `Timeout`. #[test] @@ -314,7 +314,7 @@ mod tests { ); assert_eq!(v2.kind(), "Stuck"); - // A budget-less, residual-less incompleteness (a genuine rlimit/`unknown`) is the + // A budget-less, residual-less incompleteness (a rlimit/`unknown`) is the // engine map's `Timeout` image. let rlimit = "error: rlimit exceeded; resource limit reached"; let v3 = cert_verdict_for_lean( @@ -324,7 +324,7 @@ mod tests { assert_eq!(v3.kind(), "Timeout"); } - /// serde round-trips for ALL SEVEN variants (REQ-1 / AC-1): each serializes to a + /// serde round-trips for all seven variants (REQ-1 / AC-1): each serializes to a /// `"kind"`-tagged object and deserializes back to an equal value. #[test] fn all_seven_variants_round_trip() { diff --git a/forge/tests/acceptance_programs.rs b/forge/tests/acceptance_programs.rs index c20f0f6a7..fde06e97c 100644 --- a/forge/tests/acceptance_programs.rs +++ b/forge/tests/acceptance_programs.rs @@ -1,5 +1,5 @@ //! The compose-any-program proof (crosslink #103): three acceptance programs -//! that show the verified-primitive basis (C1–C7) composes into real programs. +//! that show the verified-primitive basis (C1–C7) composes into programs. //! A `u64` decimal formatter (`examples/formatter/format.th`), a calculator core //! (`examples/calculator/calc.th`), and a line/CSV parser //! (`examples/parser/parse_lines.th`). Each is grounded against the two external @@ -28,7 +28,7 @@ //! * Parser — `has_sep(s, sep) ens result == contains_sub(s, sep)` certifies L3 //! via the full §7-mutation-scored `forge check` ladder; `fields(s, sep) ens //! result.len() == 1 + count_sep(s, sep)` (the C5 split count-bound) certifies L3 -//! under real verus on the lowering (the thin `{ s.split(sep) }` caller is not +//! under verus on the lowering (the thin `{ s.split(sep) }` caller is not //! §7-mutation-scoreable, the documented split-caller precedent). The runnable //! `split_abc` builds + runs → 3 pieces ([97],[98],[99] == "a","b","c") for //! "a,b,c" split on ',' (byte 44). The full file (incl. the `fields` count-bound @@ -52,7 +52,7 @@ //! missing solver (R-CODE-4). The build + run uses `rustc`, but the `--entry` //! runner carries the #57 native-Linux seccomp prelude (raw `prctl`), so the //! build+run tests SKIP with an explicit warning on any non-Linux platform -//! (`linux_build_run_supported`): FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES +//! (`linux_build_run_supported`): full acceptance OF the build+run PATH requires //! LINUX CI — `cargo test` on macOS/Windows exercises `forge check` / verus / //! lowering but not the runnable seccomp twin. To run the build+run path locally on //! Apple Silicon, use a native **aarch64 Linux container** or an **x86_64 Linux @@ -113,7 +113,7 @@ fn verus_present() -> bool { false } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN on this +/// `true` iff the `forge build --entry` runnable artifact can link + run on this /// platform. The #57 runtime effect sandbox (`forge/src/sandbox.rs`) is native Linux /// only: `synthesize_entry_main` injects a raw `extern "C" { fn prctl }` seccomp-bpf /// prelude with x86_64/aarch64 BPF arch guards, so the emitted runner does not link @@ -486,8 +486,8 @@ fn calculator_string_parse_builds_and_runs_end_to_end() { // ============================================================================ /// (a.1) `has_sep(s, sep) ens result == contains_sub(s, sep)` certifies L3 via the -/// full §7-mutation-scored `forge check` ladder (the C5 substring predicate is real -/// teeth). Authority: `.design/basis/07-strings.md` REQ-13 (grounded `14 verified, +/// full §7-mutation-scored `forge check` ladder. Authority: +/// `.design/basis/07-strings.md` REQ-13 (grounded `14 verified, /// 0 errors`; a broken predicate fails); `thermite-design.md` §6. #[test] fn parser_contains_predicate_certifies_l3() { @@ -505,7 +505,7 @@ fn parser_contains_predicate_certifies_l3() { ); } -/// (a.2) the `fields` split count-bound certifies L3 under real verus on the +/// (a.2) the `fields` split count-bound certifies L3 under verus on the /// lowering. The thin `{ s.split(sep) }` caller is not §7-mutation-scoreable by /// `forge check` (no scoreable body mutant — the documented split-caller precedent, /// `string_search_conformance.rs`), so its L3 is established by verus directly. @@ -551,7 +551,7 @@ fn parser_split_core_builds_and_runs_three_pieces() { "\"a,b,c\" split on ',' (44) must RUN → 3 pieces [97],[98],[99] (== \"a\",\"b\",\"c\"):\nstdout:{out}" ); // Count the piece elements: each piece renders `TString { data: [] }` - // INSIDE the outer `TVecTString { data: [ ... ] }`. The outer wrapper name + // inside the outer `TVecTString { data: [ ... ] }`. The outer wrapper name // `TVecTString` itself contains the substring `TString`, so we count on the // element pattern `data: [9` (every piece byte 97/98/99 starts with '9'), // which the outer wrapper's `data: [TString...` does not match. diff --git a/forge/tests/battery_conformance.rs b/forge/tests/battery_conformance.rs index b2ffe1cea..5de22599d 100644 --- a/forge/tests/battery_conformance.rs +++ b/forge/tests/battery_conformance.rs @@ -1,7 +1,7 @@ //! The live cert-oracle for the forge-tier frozen battery (`.design/stage1-forge-tier.md` //! REQ-5 + AC-9, increment 2c). It drives the built `forge` binary with `check --json` //! over the `conformance/battery/` fixtures and asserts the elaboration-time refusal — -//! a proof citing an unlisted tactic OR an unlisted simp lemma is REFUSED, NAMED. +//! a proof citing an unlisted tactic OR an unlisted simp lemma is REFUSED, named. //! //! AC-9 is exercised here at the certificate level (the battery's verus-free logic — the //! frozen registry, the citation scanner, the gate, the `Stuck` producer — is pinned by @@ -16,7 +16,7 @@ //! discharge is 2e), so it emits no certificate. //! //! `forge check` resolves the verus version before the per-item loop, so these checks -//! need verus present even though the battery short-circuits BEFORE any lowering/verus +//! need verus present even though the battery short-circuits before any lowering/verus //! run; they skip with a logged note when verus is absent (mirroring //! `covenant_conformance.rs`), never panicking on a missing solver. `tests/` is not //! anti-pattern-gated, so `unwrap`/`expect` are fine here. @@ -87,7 +87,7 @@ fn skip_note() { ); } -/// AC-9: a proof citing an unlisted TACTIC is refused, NAMED, before any discharge. +/// AC-9: a proof citing an unlisted tactic is refused, named, before any discharge. #[test] fn unlisted_tactic_is_refused_with_name() { if !verus_present() { @@ -116,7 +116,7 @@ fn unlisted_tactic_is_refused_with_name() { ); } -/// AC-9: a proof citing an unlisted SIMP LEMMA is refused, NAMED, before any discharge. +/// AC-9: a proof citing an unlisted SIMP lemma is refused, named, before any discharge. #[test] fn unlisted_simp_lemma_is_refused_with_name() { if !verus_present() { diff --git a/forge/tests/body_tv.rs b/forge/tests/body_tv.rs index 854e1f80f..112f74e98 100644 --- a/forge/tests/body_tv.rs +++ b/forge/tests/body_tv.rs @@ -1,7 +1,7 @@ //! Conformance for the exec-body (state-refinement) translation-validation phase //! (`.design/verified/exec-stmt-tv.md` REQ-5 + `.design/verified/loop-tv.md` REQ-5; //! epic crosslink #169, blocker #162). The state analogue of -//! `exec_tv_conformance.rs`. Four load-bearing properties, all through the real +//! `exec_tv_conformance.rs`. Four required properties, all through the real //! `verus` binary (skip with a logged reason if absent, mirroring //! `exec_tv_conformance.rs` / `body_teeth.rs`): //! diff --git a/forge/tests/build_conformance.rs b/forge/tests/build_conformance.rs index 7bd4f5897..1b4aa0352 100644 --- a/forge/tests/build_conformance.rs +++ b/forge/tests/build_conformance.rs @@ -73,12 +73,12 @@ fn write_fixture(name: &str, body: &str) -> PathBuf { path } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -129,7 +129,7 @@ fn sum_runs() { assert_eq!(v["crate_type"], "bin", "an --entry build is a runnable bin"); assert_eq!(v["entry"], "sum"); - // AC-3: the produced binary RUNS and prints the hand-derived value `6`. + // AC-3: the produced binary runs and prints the hand-derived value `6`. let artifact = artifact_path_from_json(&stdout); let (ran, output) = run_artifact(&artifact); assert!( @@ -396,7 +396,7 @@ fn checks_are_baked_in() { // the per-run scratch path remapped out of the debug metadata) produce a // byte-identical compiled rlib (§5.3). The emitted source is forge-owned // deterministic; the codegen is reproducible once the path + the -// archive mtime are pinned. This builds via the real CLI twice and diffs the +// archive mtime are pinned. This builds via the CLI twice and diffs the // artifact bytes (R-CHAR-3 — the design's reproducibility AC, not a // toolchain self-comparison of derived strings). @@ -430,7 +430,7 @@ fn rebuilt_library_is_byte_identical() { // ---- AC-7: exit-status discipline (R-CODE-4) -------------------------------- // // A program that parses/validates/effect-checks clean but lowers to Rust rustc -// REJECTS (an `ens` referencing an undefined identifier) yields a non-zero `forge +// rejects (an `ens` referencing an undefined identifier) yields a non-zero `forge // build` exit and a structured RustcOutput error — never a silent success. #[test] @@ -449,7 +449,7 @@ fn uncompilable_lowering_is_nonzero_exit() { let _ = std::fs::remove_file(&fixture); } -// ---- helper: the EXACT bytes build_file compiles for the library form -------- +// ---- helper: the exact bytes build_file compiles for the library form -------- // // `build_file` compiles `thermite_lower::lower_l1(program)` verbatim (build.rs's // `emit_source` is `lower_l1` + an optional appended runner). AC-2 inspects that diff --git a/forge/tests/bv_lowering.rs b/forge/tests/bv_lowering.rs index 12fdf5bc6..c760ae8df 100644 --- a/forge/tests/bv_lowering.rs +++ b/forge/tests/bv_lowering.rs @@ -5,9 +5,9 @@ //! mixed-mechanism function attributes each clause to the engine that grounds it. //! //! The whole suite is gated on the `bv` cargo feature (the shadow-flag plumbing — without -//! it the `@bv` tag is a structured parse error, REQ-1's R-BV-1 lock) AND on `verus`/z3 +//! it the `@bv` tag is a structured parse error, REQ-1's R-BV-1 lock) and on `verus`/z3 //! being reachable (the route reuses the Verus base pass and reaches z3 for the QF_BV and -//! QF_NRA queries). A shard without them SKIPS — the CI lean/verus job is the authoritative +//! QF_NRA queries). A shard without them skips — the CI lean/verus job is the authoritative //! gate, mirroring `g1_gate.rs` and `nlsat_relax_conformance.rs`. #![cfg(feature = "bv")] @@ -168,13 +168,10 @@ fn mix64_certifies_with_two_bitvector_clauses_and_one_unbounded() { assert_eq!(lobls[0]["verdict"]["kind"], Value::from("Proved")); } -/// REQ-8 / AC-9 (reconstruction default-on — the per-clause trust migration): in the SAME -/// item `mix64`, the arithmetic clause `a + b == b + a` migrates its `trust:` to the -/// KERNEL-CHECKED form (reconstruction-supported QF_BV) while the bitwise clause -/// `a ^ b ^ b == a` RETAINS its solver(Z3 QF_BV) trust (the bit-blasting wall — F-J). Same -/// rung (both `Proved` at L4); only the orthogonal trust axis moves. Default-on: no flag. +/// REQ-8 / AC-9: both arithmetic and bitwise clauses in `mix64` use the literal +/// `BitVec N` kernel-checked reconstruction path. Their L4 rung is unchanged. #[test] -fn req8_mix64_arith_clause_migrates_kernel_checked_bitwise_stays_solver() { +fn req8_mix64_arithmetic_and_bitwise_clauses_migrate_kernel_checked() { if !verus_present() { eprintln!("SKIP: verus (z3) absent — the bit-vector route is not run."); return; @@ -204,22 +201,24 @@ fn req8_mix64_arith_clause_migrates_kernel_checked_bitwise_stays_solver() { assert!( add_trust .iter() - .any(|t| t.contains("kernel-checked") && t.contains("BvModel")), - "the arith clause's trust migrated to the kernel-checked BvModel base: {add_trust:?}" + .any(|t| t.contains("Lean kernel") && t.contains("literal BitVec N")), + "the arithmetic clause uses the literal BitVec kernel base: {add_trust:?}" ); assert!( !add_trust.iter().any(|t| t.contains("Z3 QF_BV")), "Z3 is no longer load-bearing for the reconstruction-supported arith clause: {add_trust:?}" ); - // ens#1 — `a ^ b ^ b == a` (bitwise xor): stays solver-trusted. + // ens#1 — `a ^ b ^ b == a` (bitwise xor): migrated too. let xor_trust = trust_of(1); assert!( - xor_trust.iter().any(|t| t.contains("Z3 QF_BV")), - "the xor clause retains its solver(Z3 QF_BV) trust (F-J): {xor_trust:?}" + xor_trust + .iter() + .any(|t| t.contains("Lean kernel") && t.contains("literal BitVec N")), + "the xor clause uses the literal BitVec kernel base: {xor_trust:?}" ); assert!( - !xor_trust.iter().any(|t| t.contains("kernel-checked")), - "the xor clause did NOT migrate (the exporter refuses bitwise): {xor_trust:?}" + !xor_trust.iter().any(|t| t.contains("Z3 QF_BV")), + "the xor clause no longer names Z3 in its migrated trust: {xor_trust:?}" ); } @@ -245,7 +244,7 @@ fn planted_non_injective_shift_is_a_counterexample_with_bit_pattern() { } /// AC-3 (second half): an over-budget 64-bit multiplier query yields `Timeout` under the -/// dedicated budget profile — NEVER `unknown` and never a silent downgrade. The robust +/// dedicated budget profile — never `unknown` and never a silent downgrade. The robust /// invariant (across z3 versions): the clause never lands a silent `BvUnknown` skip; when /// it IS a timeout, the cert names the `bv64-multiplier` profile. #[test] @@ -274,7 +273,7 @@ fn over_budget_multiplier_is_timeout_under_named_profile_never_unknown() { } /// AC-5 (Lock 2 — bv-semantics mutation): a `@bv` fn whose `ens` clause constrains the -/// body via `result` certifies at L4 AND its certificate surfaces a non-trivial mutation +/// body via `result` certifies at L4 and its certificate surfaces a non-trivial mutation /// score from the WRAP-AWARE battery. The `succ_ge` fixture's `ens@bv64 result >= x` over /// the identity body `x + 0` is machine-valid (L4); the frozen off-by-one mutator's /// `x + 1` body is the wrap-exploiting mutant — valid over unbounded integers but false @@ -318,10 +317,10 @@ fn bv_semantics_mutation_surfaces_a_nontrivial_kill_ratio_on_a_result_clause() { } /// REQ-4 / AC-5 (lock 2 — anti-Goodhart gate): a WEAK result-referencing `@bv` contract -/// whose mutants all survive at width is rejected `WeakContract`, exactly as the Verus -/// and Lean paths gate their mutation score — it does NOT silently certify L4. The +/// whose mutants all survive at width is rejected `WeakContract`, as the Verus +/// and Lean paths gate their mutation score — it does not silently certify L4. The /// tautological `ens@bv64 result + 0 == result` survives every (non-equivalent) body -/// mutant: a 0-kill score below the floor. (Without the bv mutation GATE this contract +/// mutant: a 0-kill score below the floor. (Without the bv mutation gate this contract /// certified L4 — the anti-gaming hole RFC §10 forbids.) #[test] fn a_weak_result_referencing_bv_contract_is_gated_weakcontract() { @@ -385,7 +384,7 @@ fn shadowed_clause_count(certs: &[Value]) -> usize { } /// AC-4 (Lock 1 — the shadow flag): every `@bv`-tagged clause's certificate carries -/// `bv_shadow` (the RFC §9 shape) and NOTHING untagged does — `grep bv_shadow` over the +/// `bv_shadow` (the RFC §9 shape) and nothing untagged does — `grep bv_shadow` over the /// certs ≡ exactly the tagged clauses. `mix64` has two `@bv64` clauses + one unbounded /// clause, plus the injectivity lemma's `@bv64` clause: three tagged clauses carry the /// flag, the unbounded clause does not. `nowrap_obligation` is the reserved (REQ-5) slot, @@ -420,7 +419,7 @@ fn every_bv_tagged_clause_carries_the_shadow_flag_and_nothing_else() { "the reserved nowrap_obligation slot (REQ-5) is omitted for a bare @bv64: {s}" ); } - // The untagged (unbounded) clause carries NO shadow flag — grep finds nothing else. + // The untagged (unbounded) clause carries no shadow flag — grep finds nothing else. assert!( obls[2].get("bv_shadow").is_none(), "the untagged unbounded clause has no shadow flag: {}", @@ -440,7 +439,7 @@ fn every_bv_tagged_clause_carries_the_shadow_flag_and_nothing_else() { "the lemma clause names its bv64 semantics: {ls}" ); - // Grep-completeness over the WHOLE cert collection: exactly the three tagged clauses + // Grep-completeness over the whole cert collection: exactly the three tagged clauses // (mix64::ens#0, mix64::ens#1, rotl1_injective::ens#0) carry bv_shadow. assert_eq!( shadowed_clause_count(&certs), @@ -449,7 +448,7 @@ fn every_bv_tagged_clause_carries_the_shadow_flag_and_nothing_else() { ); } -/// AC-4: a refuted `@bv` clause STILL carries the shadow flag — a counterexample is a +/// AC-4: a refuted `@bv` clause still carries the shadow flag — a counterexample is a /// machine-semantics fact, so the fork stays greppable even on a hard fail. #[test] fn a_refuted_bv_clause_still_carries_the_shadow_flag() { @@ -502,10 +501,9 @@ fn forge_audit_lists_the_bv_shadows() { ); } -/// REQ-8 / AC-9: `forge audit` carries the RESIDUAL-TRUST STATEMENT — it aggregates the -/// kernel-checked-vs-solver split and names the still-solver-trusted fragments. On `mix64` -/// the migrated arith clause is kernel-checked while the xor + shift clauses stay -/// solver-trusted; the statement names the bitwise/shift/rotate + EPR rel/array fragments. +/// REQ-8 / AC-9: `forge audit` carries the RESIDUAL-TRUST statement — it aggregates the +/// kernel-checked-vs-solver split and names the remaining unsupported fragments. All +/// QF_BV clauses in `mix64` are kernel-checked; EPR rel/array remains listed. #[test] fn forge_audit_residual_trust_statement_names_the_split() { if !verus_present() { @@ -518,34 +516,25 @@ fn forge_audit_residual_trust_statement_names_the_split() { !rt.is_null(), "a bv project's audit carries the REQ-8 residual-trust statement: {manifest}" ); - // mix64::ens#0 (add) + the nlsat unbounded clause are kernel-grounded; the xor and the - // lemma's shift clause stay solver-trusted (≥ 2 solver-trusted clauses). + // Two mix64 QF_BV clauses, its nlsat clause, and the rotate lemma are kernel-grounded. assert!( - rt["kernel_checked_clauses"].as_u64().unwrap_or(0) >= 1, - "at least the arith clause migrated to kernel-checked: {rt}" + rt["kernel_checked_clauses"].as_u64().unwrap_or(0) >= 4, + "the complete literal QF_BV surface migrated to kernel-checked: {rt}" ); - assert!( - rt["solver_trusted_clauses"].as_u64().unwrap_or(0) >= 2, - "the xor + shift clauses stay solver-trusted: {rt}" + assert_eq!( + rt["solver_trusted_clauses"].as_u64().unwrap_or(u64::MAX), + 0, + "the mix64 QF_BV surface has no solver-trusted residual: {rt}" ); - // The still-solver-trusted clauses are NAMED (the F-J inventory). - let named = rt["solver_trusted"] - .as_array() - .expect("the residual-trust statement names the solver-trusted clauses"); assert!( - named.iter().any(|c| c["item"] == "mix64"), - "mix64's xor clause is named as still-solver-trusted: {rt}" + rt["solver_trusted"].is_null() + || rt["solver_trusted"].as_array().is_some_and(Vec::is_empty), + "there are no named solver-trusted clauses: {rt}" ); - // The standing F-J fragments are named: bitwise/shift/rotate + EPR rel/array. + // The standing EPR residual remains named. let frags = rt["unsupported_fragments"] .as_array() .expect("the statement names the unsupported fragments"); - assert!( - frags - .iter() - .any(|f| f.as_str().unwrap_or("").contains("bitwise/shift/rotate")), - "the bitwise/shift/rotate fragment is named: {rt}" - ); assert!( frags .iter() @@ -562,8 +551,8 @@ fn forge_audit_residual_trust_statement_names_the_split() { } /// REQ-3 / AC-4 regression: the auto-routed bv engine (`forge audit`/`review`) is a -/// PER-ITEM overlay, never a wholesale re-route. An ordinary Verus-provable `fn` that -/// merely shares a program with a `@bv` `fn` keeps its true L3 cert — it is NOT downgraded +/// per-ITEM overlay, never a wholesale re-route. An ordinary Verus-provable `fn` that +/// merely shares a program with a `@bv` `fn` keeps its true L3 cert — it is not downgraded /// to L0. (Before the `bv_check` fix, every `fn` was routed through the bv route, whose /// untagged-clause branch rejects a non-`@bv`, non-relaxable clause — silently downgrading /// `plain_add` from L3 to L0 in the audit.) @@ -651,7 +640,7 @@ fn bv_nowrap_side_obligation_rejects_overflow_and_records_the_verdict() { "the overflowing nowrap fn must reject the project" ); - // (1) `add_overflows` — the side obligation FAILS with a concrete overflow witness. + // (1) `add_overflows` — the side obligation fails with a concrete overflow witness. let of = cert(&certs, "add_overflows"); assert_eq!( of["level"], @@ -707,7 +696,7 @@ fn bv_nowrap_side_obligation_rejects_overflow_and_records_the_verdict() { } /// REQ-6 / AC-7 (the "semantic forks and definition towers" section, normal density): the -/// additive section reports bv-shadow density PER MODULE matching the fixture's known +/// additive section reports bv-shadow density per MODULE matching the fixture's known /// counts, and the project-wide F-F tripwire stays WITHIN the retreat threshold (one tagged /// clause among four contract-bearing clauses, 250‰ < 500‰ → no trip). The whole project /// certifies (the ordinary fns at L3, the @bv fn at L4), so the audit exits 0. @@ -745,7 +734,7 @@ fn forge_audit_semantic_forks_density_matches_known_counts() { assert_eq!(row("plain_add")["shadow_clauses"], Value::from(0)); assert_eq!(row("plain_add")["density_permille"], Value::from(0)); - // The project-wide F-F tripwire: 1/4 = 250‰ < 500‰ → NOT tripped, no warning. + // The project-wide F-F tripwire: 1/4 = 250‰ < 500‰ → not tripped, no warning. let tw = &forks["tripwire"]; assert_eq!(tw["shadow_clauses"], Value::from(1)); assert_eq!(tw["contract_clauses"], Value::from(4)); diff --git a/forge/tests/bv_vacuity_gate.rs b/forge/tests/bv_vacuity_gate.rs index 4c3c5bc4b..aac3d6724 100644 --- a/forge/tests/bv_vacuity_gate.rs +++ b/forge/tests/bv_vacuity_gate.rs @@ -2,10 +2,10 @@ //! //! The v1/default cage rejects an unsatisfiable precondition as `VacuousPrecondition` //! (the anti-Goodhart battery, RFC-1 §10 — a `req false` contract "proves" anything and -//! is a gaming vector). The bv route runs NO vacuity check: it only threads `req` into +//! is a gaming vector). The bv route runs no vacuity check: it only threads `req` into //! the QF_BV query, so a `req false` clause discharges vacuously. For a result-referencing //! clause the mutation gate incidentally catches it (every mutant survives → WeakContract), -//! but a PARAM-ONLY `@bv` clause or a `@bv` LEMMA (no body → no mutation) sails through and +//! but a PARAM-only `@bv` clause or a `@bv` lemma (no body → no mutation) sails through and //! — post-REQ-8 — certifies L4 with a KERNEL-CHECKED trust label on a vacuous proof. //! //! These tests assert the correct (rejected) behavior — the regression guard for the @@ -49,7 +49,7 @@ fn reject_cause(c: &Value) -> Option { .map(String::from) } -/// A `@bv` LEMMA with an unsatisfiable precondition must NOT certify — it is vacuous +/// A `@bv` lemma with an unsatisfiable precondition must not certify — it is vacuous /// (the §10 anti-Goodhart gaming vector). Regression guard for the bv-route vacuity gate. #[test] fn vacuous_bv_lemma_is_rejected_not_certified() { @@ -76,7 +76,7 @@ fn vacuous_bv_lemma_is_rejected_not_certified() { ); } -/// A param-only `@bv` fn clause under an unsatisfiable precondition must NOT certify +/// A param-only `@bv` fn clause under an unsatisfiable precondition must not certify /// kernel-checked — the mutation gate misses param-only clauses, so the req-SAT gate must /// catch it. Regression guard for the bv-route vacuity gate. #[test] diff --git a/forge/tests/cache_conformance.rs b/forge/tests/cache_conformance.rs index 8e1016030..544675d9a 100644 --- a/forge/tests/cache_conformance.rs +++ b/forge/tests/cache_conformance.rs @@ -177,7 +177,7 @@ fn cache_hit_serves_l3_with_verus_unavailable() { let _ = std::fs::remove_dir_all(&cache_dir); let fixture = write_fixture("decisive", &verifiable_program("decisive_fn")); - // Populate the cache with verus present (pinned version, real binary on PATH). + // Populate the cache with verus present (pinned version, binary on PATH). let (code1, certs1) = run_check(&fixture, &cache_dir, &HashMap::new()); assert_eq!(code1, Some(0), "population run must certify L3"); assert_eq!( diff --git a/forge/tests/check_conformance.rs b/forge/tests/check_conformance.rs index f73665a70..0e88e3b67 100644 --- a/forge/tests/check_conformance.rs +++ b/forge/tests/check_conformance.rs @@ -255,7 +255,7 @@ fn assert_stable_subset_matches_golden(corpus: &str, cert_stem: &str, item: &str /// C7 / `.design/basis/09-option-result.md` AC-4 (#100): `parse_valid` certifies L3 /// against the committed `conformance/parse_u64.cert.json` oracle. A valid in-range -/// digit string PROVES `result is Some` via parse_u64's strengthened contract. +/// digit string proves `result is Some` via parse_u64's strengthened contract. #[test] fn parse_valid_cert_matches_golden_deterministic_subset() { if !verus_present() { @@ -285,7 +285,7 @@ fn make_some_cert_matches_golden_deterministic_subset() { /// §4.2-caged accessor; the lowerer maps spec-position `contains_key` to the TMap /// wrapper's `spec_contains_key`. The stable subset (item/level/tautology/ /// vacuous_precondition/effects/slag) must match the committed oracle; the -/// insert-then-get round-trip + absent→None teeth are pinned at the verus +/// insert-then-get round trip and absent→None cases are pinned at the verus /// codegen-grounding level in `map_conformance.rs`. /// /// Unlike the single-L3-item corpora above, `map_kv.th` is multi-item and its diff --git a/forge/tests/composition_basis_conformance.rs b/forge/tests/composition_basis_conformance.rs index f9f79c41a..046c76805 100644 --- a/forge/tests/composition_basis_conformance.rs +++ b/forge/tests/composition_basis_conformance.rs @@ -25,7 +25,7 @@ //! //! - Laws 2 & 3 — assurance + TCB aggregation (#15/#60, REQ-2/REQ-3): `forge audit` //! over the file (which mixes an end_to_end part `pipeline` + a to_boundary part -//! `read_then_inc`) reports the project scope as the genuine min — `to_boundary` +//! `read_then_inc`) reports the project scope as the min — `to_boundary` //! listing the `read_small` crossing, never over-claimed as end_to_end — and the //! TCB enumerates `read_small`'s boundary contract ∪ the toolchain (R-DEFER-9). //! @@ -34,7 +34,7 @@ //! existing machinery shows they await their prerequisite stages (Stage 2 scheme //! lowering, Stage 1/4 ADT/collection lowering) — they stay NOT-STARTED. //! -//! These skip with a logged note if verus is absent (a real verus proof underlies +//! These skip with a logged note if verus is absent (a verus proof underlies //! each cert), mirroring `composition_conformance.rs` / `audit_conformance.rs`. use std::path::{Path, PathBuf}; @@ -68,7 +68,7 @@ fn compose_demo() -> PathBuf { } /// `true` iff verus can be located — mirrors `composition_conformance.rs`. The -/// Stage-5 composition certs are real verus proofs (each step L3-proves through +/// Stage-5 composition certs are verus proofs (each step L3-proves through /// the next step's contract), so the prover must be present. fn verus_present() -> bool { if let Ok(p) = std::env::var("VERUS_BIN") { @@ -115,7 +115,7 @@ fn run_check_floor0(file: &Path) -> (Option, Vec, String) { /// Run `forge audit --json`, returning (exit_code, manifest, stderr). The /// audit runs at the pinned default budget (the `--mutation-floor` lever is not /// exposed on `audit` by design — `forge/src/cli.rs` `run_audit`); the oracle's -/// `project_aggregation` row pins only the project SCOPE + the TCB crossing, which +/// `project_aggregation` row pins only the project scope + the TCB crossing, which /// hold at the default floor. fn run_audit(file: &Path) -> (Option, Value, String) { let out = Command::new(forge_bin()) @@ -296,7 +296,7 @@ fn boundary_composition_read_then_inc_composes_through_the_boundary() { // Laws 2 & 3 (assurance + TCB aggregation, #15/#60 REQ-2/REQ-3): the audit // manifest aggregates the project as the min over parts. compose_demo.th mixes an end_to_end // part (`pipeline`) and a to_boundary part (`read_then_inc`), so the project scope -// is the genuine min — `to_boundary` listing the `read_small` crossing, never +// is the min — `to_boundary` listing the `read_small` crossing, never // over-claimed as end_to_end — and the TCB enumerates `read_small`'s boundary // contract ∪ the toolchain (nothing fiat-trusted omitted, R-DEFER-9). Anchored to // the oracle's `project_aggregation` array. @@ -333,7 +333,7 @@ fn project_aggregation_is_the_honest_min_over_parts() { "the stable v1 audit-manifest format tag" ); - // The genuine min-over-parts scope: a mixed project is to_boundary (not + // The min-over-parts scope: a mixed project is to_boundary (not // over-claimed end_to_end — the no-over-claim guarantee, R-DEFER-9 / #60). assert_eq!( manifest["project_assurance"]["scope"]["kind"], diff --git a/forge/tests/composition_conformance.rs b/forge/tests/composition_conformance.rs index 19af98c8f..0a80f0b05 100644 --- a/forge/tests/composition_conformance.rs +++ b/forge/tests/composition_conformance.rs @@ -18,7 +18,7 @@ //! //! Expected values trace to the golden `conformance/composition/cases.json` and //! `thermite-design.md` §9 (R-CHAR-3), never copied from forge's own output. -//! These skip with a logged note if verus is absent — the §9 composition proof is a real verus +//! These skip with a logged note if verus is absent — the §9 composition proof is a verus //! run (the boundary caller L3-proves against the assumed contract). use std::path::{Path, PathBuf}; @@ -44,7 +44,7 @@ fn cases() -> Value { } /// `true` iff verus can be located — mirrors `e2e_conformance.rs`. The §9 -/// composition cases run a real verus proof (the boundary caller L3-proves +/// composition cases run a verus proof (the boundary caller L3-proves /// against the boundary fn's assumed contract), so the prover must be present. fn verus_present() -> bool { if let Ok(p) = std::env::var("VERUS_BIN") { @@ -163,7 +163,7 @@ fn direct_boundary_caller_verifies_through_the_contract() { } // AC-2 (transitive caller → L3 + to_boundary): the `transitive_boundary_caller` -// case — `h → g → ext_id` — `h`'s sub-program weaves both `g` (real body, proved) +// case — `h → g → ext_id` — `h`'s sub-program weaves both `g` (body, proved) // and `ext_id` (external_body signature), so `h` proves L3 through the contracts; // scope to_boundary via ext_id. Anchored to `cases.json` `transitive`. #[test] @@ -264,8 +264,8 @@ fn req_violating_caller_is_a_counterexample_not_a_false_l3() { // AC-4 + the honesty gate: the pure corpus references only spec fns / combinators, // so no external_body is woven and the cert stays L3 end-to-end, byte-stable. The // lowered string for a pure sub-program contains no `external_body` substring -// (the load-bearing OQ-1 invariant): external_body appears iff a woven dependency -// is `#[boundary]`/`#[slag]`. We exercise the corpus through the real pipeline. +// (the required OQ-1 invariant): external_body appears iff a woven dependency +// is `#[boundary]`/`#[slag]`. We exercise the corpus through the pipeline. #[test] fn corpus_unaffected_stays_l3_end_to_end() { if !verus_present() { @@ -305,7 +305,7 @@ fn corpus_unaffected_stays_l3_end_to_end() { // caller's own contract (`x < 100 ⟹ x <= 100`) → survivor; but the call-bearing // equivalence harness cannot prove `real == mutant` (ext_weak's `ens` does not pin // `real == x`) → not excluded → the survivor stays counted → `wcaller` gates -// `WeakContract`. The genuine #101 anti-launder line, one level up: a mutant the +// `WeakContract`. The #101 anti-launder line, one level up: a mutant the // callee contracts cannot prove equivalent is conservatively counted (R-DEFER-9). // Expected from §9 + equivalent-mutants.md REQ-8 (hand-derived), not forge output. #[test] diff --git a/forge/tests/concurrency.rs b/forge/tests/concurrency.rs index 2afa87d82..7fbd2ea05 100644 --- a/forge/tests/concurrency.rs +++ b/forge/tests/concurrency.rs @@ -36,7 +36,7 @@ //! //! Verus-needing tests skip with a logged reason when verus is absent (mirroring //! `cache_conformance.rs`): the L3 verdict the guarantee rests on needs the solver. -//! The fault-injection and locality shapes that do not need a real proof still run +//! The fault-injection and locality shapes that do not need a proof still run //! their non-verus halves. //! //! Expected verdicts trace to `conformance/sum.cert.json` and the `binary_search` @@ -349,7 +349,7 @@ fn n_concurrent_agents_produce_correct_uncorrupted_certs() { } // AC-1 (uncorrupted cache): every `.json` parses (a torn entry would - // PANIC inside `parse_all_entries`), and no orphan `.tmp` sibling survived the + // panic inside `parse_all_entries`), and no orphan `.tmp` sibling survived the // concurrent atomic publishes. let entries = parse_all_entries(&cache_dir); assert!( @@ -391,7 +391,7 @@ fn n_concurrent_agents_produce_correct_uncorrupted_certs() { /// reported verdict is the same L3 (REQ-1, REQ-3; AC-2). /// /// Note on entry count: a single-fn item legitimately yields more than one -/// content-address entry — the real fn plus one per mutation-scoring (#12) mutant +/// content-address entry — the fn plus one per mutation-scoring (#12) mutant /// (and per strengthening-probe (#14) candidate). The convergence guarantee is /// therefore "the concurrent cache key set equals the serial run's, every entry /// consistent" — not a literal count of one. Asserting one entry would be wrong diff --git a/forge/tests/contract_tv_conformance.rs b/forge/tests/contract_tv_conformance.rs index 1e47e5dd8..f4543de0a 100644 --- a/forge/tests/contract_tv_conformance.rs +++ b/forge/tests/contract_tv_conformance.rs @@ -1,18 +1,18 @@ //! Conformance for the contract-faithfulness translation-validation phase //! (`.design/verified/contract-tv.md` REQ-5 / REQ-3; epic crosslink #139 / -//! blockers #144 + #142). Two load-bearing properties, both through the real +//! blockers #144 + #142). Two required properties, both through the real //! `verus` binary (skips with a logged note if absent, mirroring `check_conformance.rs`): //! //! 1. **Corpus no-false-positive (the key AC):** `forge tv --json` //! over the representative corpus (sum / binary_search / map_kv) yields zero //! `divergent` clauses — the `faithful` production lowering must not trip TV. A -//! real divergence here would be a genuine lowering bug (a find), so the test +//! real divergence here would be a lowering bug (a find), so the test //! pins `divergent == 0` (R-CHAR-3 — the expected value is the design's //! faithful-lowering invariant, not the toolchain's own output). //! 2. **Off-corpus generated run (the thesis payoff):** `forge tv sum.th //! --generated 200 --json` lowers + TV-checks 200 deterministically generated //! clauses; the faithful lowerer makes every checked clause `faithful` (0 -//! `divergent`). Any divergence is a real off-corpus infidelity finding (the +//! `divergent`). Any divergence is a off-corpus infidelity finding (the //! whole point — surfaced). //! //! Expected values trace to the design's faithful-lowering invariant + the frozen @@ -95,7 +95,7 @@ fn corpus_clause_verdict<'a>(report: &'a Value, clause: &str) -> Option<&'a str> // ---- AC: corpus no-false-positive ----------------------------------------- -/// REQ-5 / the key AC: `forge tv sum.th` checks the real contract clauses and +/// REQ-5 / the key AC: `forge tv sum.th` checks the contract clauses and /// finds them all faithful (0 divergent). The faithful production lowering of /// `sum`'s `req`/`ens`/loop-`inv`/`dec` must not trip TV. #[test] @@ -279,7 +279,7 @@ fn off_corpus_generated_run_all_faithful() { return; } // 200 clauses, the design's N (AC-7). Discharging 200 verus runs is slow but is - // the load-bearing thesis check; the `forge tv` binary runs them sequentially. + // the required thesis check; the `forge tv` binary runs them sequentially. let report = run_tv_json(&corpus_dir().join("sum.th"), Some(200)); let gen = report .get("generated") @@ -331,11 +331,11 @@ fn off_corpus_generated_run_all_faithful() { // k`) and non-`Eq` nat comparisons (`acc <= spec_sum(xs)`); every such checked // clause being `faithful` (0 divergent, asserted above) confirms the #146/#148 // cast-paren fix + the #147 gap #2 Eq-only coercion hold off-corpus on both - // encoders. A `divergent`/`unverifiable` here = a real off-corpus hole. The + // encoders. A `divergent`/`unverifiable` here = a off-corpus hole. The // construct presence (so this guard is not vacuous) is asserted directly on the // deterministic generator in `thermite_tv::gen::tests::diverse_construct_coverage` // (`cast_lt >= 1`, `non_eq_nat_cmp >= 1`); here we re-confirm the run is the // extended one by requiring the clause count grew past the old 175-checked ceiling - // is not asserted (byte-view ratio varies by seed) — the load-bearing guard is the + // is not asserted (byte-view ratio varies by seed) — the required guard is the // `divergent == 0 && unverifiable == 0` over the cast-`<`-bearing stream above. } diff --git a/forge/tests/covenant_conformance.rs b/forge/tests/covenant_conformance.rs index 408bd962f..15eb84899 100644 --- a/forge/tests/covenant_conformance.rs +++ b/forge/tests/covenant_conformance.rs @@ -7,16 +7,16 @@ //! pinned by the `covenant_engine` unit tests; this is the end-to-end binary surface): //! - `max_buggy` — a planted bug (`ens result >= y` with a body returning `x`) dies as //! `CovenantRefuted` with the concrete `falsify` counterexample in the certificate, -//! BEFORE any L3 proof search (covenant-before-burn). +//! before any L3 proof search (covenant-before-burn). //! - `max_no_witness` — a forge-routed item whose `witness` block carries no author -//! `inhabit` witness is refused, NAMED (`CovenantNoAuthorWitness`), before burn. +//! `inhabit` witness is refused, named (`CovenantNoAuthorWitness`), before burn. //! - `max_correct` — a correct item's covenant validates (the unstated-vs-stated budget -//! run finds no refutation), burns to L3 WITH the covenant in hand, and carries the +//! run finds no refutation), burns to L3 with the covenant in hand, and carries the //! deterministic evidence block (witness count, falsify generated/refuted = 2002/0, //! fixed seed). //! //! `forge check` resolves the verus version before the per-item loop, so these checks -//! need verus present even though the covenant short-circuits BEFORE the L3 burn; they +//! need verus present even though the covenant short-circuits before the L3 burn; they //! skip with a logged note when verus is absent (mirroring `check_conformance.rs`), //! never panicking on a missing solver. `tests/` is not anti-pattern-gated, so //! `unwrap`/`expect` are fine here. diff --git a/forge/tests/degrade_conformance.rs b/forge/tests/degrade_conformance.rs index fded1e97b..9b60bdac5 100644 --- a/forge/tests/degrade_conformance.rs +++ b/forge/tests/degrade_conformance.rs @@ -12,7 +12,7 @@ //! `ens`) is a hard fail: non-certifying, not a degraded L1/L2 cert, no //! `lowered_assurance` flag. The determinism of this is pinned by //! `degrade::tests::counterexample_never_degrades`; this asserts it end-to-end -//! against real verus. +//! against verus. //! - AC-2 (forced degrade → L2): a forced low `--rlimit` is the L3-timeout //! lever; best-effort skip with a logged reason (OQ-1: provoking a live resourceout is //! timing-fragile). When a live degrade is provoked, the cert is a certified @@ -192,13 +192,13 @@ fn golden_cert_deserializes_with_additive_degrade_fields() { // ===== AC-4: the key anti-cheat AC — a counterexample does not degrade ======= // AC-4: a broken contract (a provably-false `ens` for the -// body) is a hard failure end-to-end against real verus: nonzero exit, +// body) is a hard failure end-to-end against verus: nonzero exit, // non-certifying (not L3/L2/L1), no `lowered_assurance` flag, no `degrade_reason`. // The ladder does not "certify L1" or degrade a disproved contract: that would // hide a bug behind a lowered-assurance stamp (§12, R-DEFER-9). The // determinism of the short-circuit is pinned by // `degrade::tests::counterexample_never_degrades`; this is the end-to-end -// witness against real verus. Expected: a false `ens` is a counterexample, not a +// witness against verus. Expected: a false `ens` is a counterexample, not a // degrade (degrade-ladder.md REQ-2 / "The anti-cheat distinction is real"), not // forge's output. #[test] diff --git a/forge/tests/divergence_249_axiom_mask.rs b/forge/tests/divergence_249_axiom_mask.rs index 3b700ac4c..9a72600a4 100644 --- a/forge/tests/divergence_249_axiom_mask.rs +++ b/forge/tests/divergence_249_axiom_mask.rs @@ -17,11 +17,11 @@ //! statement-binding gate (`canonical_theorem_statement`, first textual occurrence) //! reads a namespaced theorem carrying the canonical obligation statement proven via //! `thermite_cheat`. The axiom report inspected and the statement bound belong to two -//! different declarations: the cheat-backed proof of the real obligation is masked by +//! different declarations: the cheat-backed proof of the obligation is masked by //! the clean same-short-name decoy. forge certifies the item at L3. //! //! Expected (authority, not forge's own output, R-CHAR-3): item `f` does not certify -//! at ANY proven rung. A proof resting on `thermite_cheat` (outside the REQ-4 +//! at any proven rung. A proof resting on `thermite_cheat` (outside the REQ-4 //! allowlist) is a cheat and is never Proven. This test fails against commit 5f601a2d //! (forge emits L3). //! @@ -29,7 +29,7 @@ //! path — the kernel/forge tier (the L4-shaped, kernel-grounded rung in the new //! ladder: `L0 < L1 < L2 < L3 < L4`, `manifest.rs::Level`). The v1-era "certifies at //! L3" framing is widened to its real soundness requirement: a cheat-backed proof is -//! NEVER Proved at any PROVEN rung — neither the SOLVER rung **L3** nor the +//! never Proved at any proven rung — neither the SOLVER rung **L3** nor the //! kernel-grounded rung **L4**. The assertion below checks the item certifies at //! neither (it must remain a non-certified L0 reject). Authority: RFC-1 / GH #2; //! `.design/stage1-forge-tier.md` REQ-8; `.design/verified/proof-backends.md` REQ-4/§1. @@ -180,7 +180,7 @@ fn divergence_interactive_axiom_allowlist_masked_by_shortname_decoy() { // GH #2 ladder re-anchor, 2f): the obligation's canonical statement is proven only // via `axiom thermite_cheat : ∀ p : Prop, p` (maximal vacuity, outside the // {propext, Classical.choice, Quot.sound} allowlist). Such a proof is a cheat and - // is never Proven → item `f` does not certify at ANY PROVEN rung — neither the + // is never Proven → item `f` does not certify at any proven rung — neither the // SOLVER rung L3 nor the kernel-grounded rung L4. forge (commit 5f601a2d) emits // "L3" — the divergence. (It must remain a non-certified L0 reject.) assert!( diff --git a/forge/tests/divergence_251_macro_poison.rs b/forge/tests/divergence_251_macro_poison.rs index a22e9a133..e4dcffc47 100644 --- a/forge/tests/divergence_251_macro_poison.rs +++ b/forge/tests/divergence_251_macro_poison.rs @@ -193,7 +193,7 @@ fn divergence_interactive_notation_poison_reelaborates_obligation_to_true() { // `Thermite.stabilizesProp` to the constant-`True` predicate, so the obligation's // conclusion re-elaborates to `True`. A proof of `True` is not a discharge of the // obligation, and the enumerable trusted base the cert would list (it lists none — - // `True` rests on no axioms) is a fiction: the real obligation was never proven. + // `True` rests on no axioms) is a fiction: the obligation was never proven. // Such a re-elaboration cheat is never Proven → item `f` does not certify at L3. // forge (commit 2367628c) emits "L3" — the divergence. assert_ne!( diff --git a/forge/tests/divergence_252_indented_command_escape.rs b/forge/tests/divergence_252_indented_command_escape.rs index ef4cde54b..daa5d422e 100644 --- a/forge/tests/divergence_252_indented_command_escape.rs +++ b/forge/tests/divergence_252_indented_command_escape.rs @@ -196,7 +196,7 @@ fn divergence_interactive_indented_command_escapes_allowlist() { // indented top-level command), so the obligation's conclusion re-elaborates to // `True`. A proof of `True` is not a discharge of the obligation, and the enumerable // trusted base the cert lists (none — `True` rests on no axioms) is a fiction: the - // real obligation was never proven. Such a re-elaboration cheat is never Proven → + // obligation was never proven. Such a re-elaboration cheat is never Proven → // item `f` does not certify at L3. forge (commit 4d745e35) emits "L3" — the divergence // the #251 column-0-only allowlist leaves open. assert_ne!( diff --git a/forge/tests/divergence_audit_check2_exit_swallow.rs b/forge/tests/divergence_audit_check2_exit_swallow.rs index efa8c00ed..b1238a4c1 100644 --- a/forge/tests/divergence_audit_check2_exit_swallow.rs +++ b/forge/tests/divergence_audit_check2_exit_swallow.rs @@ -7,7 +7,7 @@ //! `forge tv` / `forge exec-tv` / `forge body-tv` exit nonzero on any //! Divergent obligation. `forge/src/cli.rs::run_tv` (and `run_body_tv` / //! `run_exec_tv`, the same convention) documents and implements: -//! "Any DIVERGENT clause (corpus OR generated) is a real lowering-fidelity +//! "Any DIVERGENT clause (corpus OR generated) is a lowering-fidelity //! finding → verification-failure exit" → `ExitCode::from(EXIT_VERIFICATION_FAILURE)`. //! //! But `scripts/audit.sh` check [2]'s per-subcommand loop does: @@ -17,12 +17,12 @@ //! parsing the report. A Divergent run (exit 1, report header "… 1 DIVERGENT …") //! is therefore never counted: the program prints a green //! "ok 0 divergent", TOT_DIV stays 0, and check [2] prints -//! "PASS ZERO divergent across the whole corpus". The gate that exists to +//! "PASS zero divergent across the whole corpus". The gate that exists to //! catch one condition cannot fire on that condition. //! //! Authority (R-CHAR-3 — the expected value is not taken from the script): //! - `scripts/audit.sh` own stated contract (header + check [2] note): -//! "PASS iff ZERO Divergent across the corpus" — so one Divergent ⇒ FAIL ⇒ RC=1. +//! "PASS iff zero Divergent across the corpus" — so one Divergent ⇒ fail ⇒ RC=1. //! - `forge/src/cli.rs::run_tv` — the Divergent ⇒ nonzero-exit convention the //! script consumes (the fake forge below reproduces it, including //! `forge/src/contract_tv.rs::render_report`'s header line shape). @@ -30,7 +30,7 @@ //! §1 (trust relocation: the audit is what the skeptic relies on). //! //! Method: extract check [2] verbatim from the live `scripts/audit.sh` (between -//! its `# CHECK 2` / `# CHECK 3` banners), run it against a fake `forge` that +//! its `# check 2` / `# check 3` banners), run it against a fake `forge` that //! reports one corpus program as `1 DIVERGENT` with the real exit convention //! (exit 1), and assert the authority's expectation: the check fails (RC=1) //! and does not print the green zero-divergent PASS. This test fails against @@ -127,7 +127,7 @@ fn divergence_audit_check2_swallows_divergent_exit() { fs::remove_dir_all(&scratch).ok(); - // Authority expectation 1 — "PASS iff ZERO Divergent": one Divergent obligation + // Authority expectation 1 — "PASS iff zero Divergent": one Divergent obligation // (delivered with forge's real verification-failure exit) trips the gate. assert!( stdout.contains("FINAL_RC=1"), diff --git a/forge/tests/divergence_battery_vacuous.rs b/forge/tests/divergence_battery_vacuous.rs index b634efa9d..a68ac7387 100644 --- a/forge/tests/divergence_battery_vacuous.rs +++ b/forge/tests/divergence_battery_vacuous.rs @@ -11,18 +11,18 @@ //! bools and never consults `cert.reject`, so the battery view renders the //! §7.1-(a)-rejected contract as "non-vacuous" — the exact degenerate-contract //! lie the battery exists to prevent. (`goal_repl::render_goal_item` gets this -//! right: it reports `NOT CERTIFIED — EnsIsTrivial` for the same cert.) +//! right: it reports `not certified — EnsIsTrivial` for the same cert.) //! //! Authority: //! - `thermite-design.md` §7: "A function does not certify until its //! **contract** certifies"; §7.1 (a): "`ens` simplifies to `true` → reject". //! - `.design/forge/goal-repl.md` REQ-1: `forge battery` "reports the §7 -//! anti-Goodhart battery ... WITHOUT re-defining any verdict. A thin VIEW +//! anti-Goodhart battery ... without re-defining any verdict. A thin view //! over the existing per-item pipeline." The pipeline's verdict for this //! program is rejected `EnsIsTrivial`; the view re-defines it to non-vacuous. //! - `conformance/vacuity/triage.json` `reject[]` entry `ens_is_true` //! (`cause: EnsIsTrivial`, program `fn f() -> () req true ens true fx pure { }`) -//! — the hand-derived §7.1 oracle this test loads its input AND expected +//! — the hand-derived §7.1 oracle this test loads its input and expected //! cause from (R-CHAR-3: nothing here is copied from the verb's own output). //! //! Toolchain (symbol anchors): `goal_repl::render_battery_item` (reads diff --git a/forge/tests/divergence_body_tv_frame.rs b/forge/tests/divergence_body_tv_frame.rs index ba54bf0cd..7dac811c2 100644 --- a/forge/tests/divergence_body_tv_frame.rs +++ b/forge/tests/divergence_body_tv_frame.rs @@ -1,9 +1,9 @@ //! Divergence pin (critic audit of #162, commit 540cea0d): `forge body-tv` -//! classifies a FAITHFUL, in-subset body as **Divergent** ("a real body-lowering +//! classifies a faithful, in-subset body as **Divergent** ("a body-lowering //! infidelity", nonzero exit) when the obligation frame fails to compile — i.e. //! when the source `req` references a `spec fn` helper (the design's central //! contract idiom, `thermite-design.md` §3/§4: `req sorted(haystack)`), because -//! `body_tv::corpus_req` threads the `req` text VERBATIM into a +//! `body_tv::corpus_req` threads the `req` text verbatim into a //! `BodyObligationFrame`/`LoopObligationFrame` whose `spec_defs` is empty //! (`spec_defs: Vec::new()` in `straight_line_body_tv` / `build_loop_frame`), //! so verus aborts with an undefined-function compile error and `run_obligation` @@ -17,7 +17,7 @@ //! `Skipped` class, "never masking an infidelity" — and symmetrically never //! fabricating one (R-HONEST-3). //! - `.design/verified/loop-tv.md` § "The four-way reporting": `Divergent` — -//! "any obligation's PRODUCTION side fails `postcondition not satisfied` (a +//! "any obligation's production side fails `postcondition not satisfied` (a //! counterexample)". The loop entry obligation (`loop-tv.md` REQ-2: entry = //! `proof fn { assert(inv[cells:=entry]); }`) contains no production text at //! all, so its compile abort can never be "the production loop text did not @@ -28,12 +28,12 @@ //! failure, not an infidelity". `body_tv::corpus_req` has no such gate. //! //! Observed (live, verus 0.2026.05.24): both fixtures below report -//! `1 DIVERGENT` ("verus ABORTED (compile/parse) … a real body-lowering / +//! `1 DIVERGENT` ("verus ABORTED (compile/parse) … a body-lowering / //! loop-lowering infidelity") and exit 1, although the production lowering of -//! each body is exactly faithful. +//! each body is faithful. //! //! These tests assert the authority's behavior (a faithful in-subset body is -//! never Divergent; the honest classes for an uncompilable frame are +//! never Divergent; the classes for an uncompilable frame are //! Skipped/Unverifiable) and therefore fail against the current toolchain. //! Tracking: crosslink blocker (see issue filed with this commit). @@ -106,14 +106,14 @@ fn run_body_tv_json(file: &Path) -> (Value, bool) { (doc, out.status.success()) } -/// Divergence (straight-line arm): a FAITHFUL `{ let v: u32 = xs[0]; v }` body — -/// squarely inside the frozen 2.2.1 subset, production lowering exactly faithful — +/// Divergence (straight-line arm): a faithful `{ let v: u32 = xs[0]; v }` body — +/// squarely inside the frozen 2.2.1 subset, production lowering faithful — /// whose `req` references a `spec fn` helper (`all_small(xs)`, the /// `req sorted(haystack)` corpus idiom) must never be `divergent` /// (exec-stmt-tv.md REQ-5: Divergent ⟺ the lowering and the reference disagree; /// an uncompilable frame is "a framing failure, not an infidelity" — the /// exec_tv req-gate authority). The current toolchain reports `1 divergent` -/// ("a real body-lowering infidelity") + exit 1: a fabricated infidelity. +/// ("a body-lowering infidelity") + exit 1: a fabricated infidelity. #[test] fn spec_helper_req_straight_line_body_is_not_divergent() { if verus_bin().is_none() { @@ -157,7 +157,7 @@ fn spec_helper_req_straight_line_body_is_not_divergent() { ); } -/// Divergence (loop arm): a FAITHFUL v1 `while lo < n inv lo <= n dec n - lo` +/// Divergence (loop arm): a faithful v1 `while lo < n inv lo <= n dec n - lo` /// loop whose fn `req` references a `spec fn` helper must never be `divergent`. /// The entry obligation (`loop-tv.md` REQ-2: `proof fn { assert(inv[cells:=entry]) }`) /// contains no production text, so its compile abort cannot be "the production diff --git a/forge/tests/divergence_c12_map_goldens_missing.rs b/forge/tests/divergence_c12_map_goldens_missing.rs index 391cba5cf..5107c032b 100644 --- a/forge/tests/divergence_c12_map_goldens_missing.rs +++ b/forge/tests/divergence_c12_map_goldens_missing.rs @@ -5,7 +5,7 @@ //! mandates, as the external truth the toolchain does not author for itself (goal.md //! verification model (A)/(B); R-CHAR-3): //! -//! > The orchestrator authors a NEW corpus program — `conformance/map_kv.th` [...] +//! > The orchestrator authors a new corpus program — `conformance/map_kv.th` [...] //! > and its golden lowering at `tests/golden/lower/map_kv.verus.rs`, hand-authored //! > from the GROUNDED form below and confirmed to pass `verus`. The cert golden //! > lives at `conformance/map_kv.cert.json`. diff --git a/forge/tests/divergence_c7_corpus_goldens_missing.rs b/forge/tests/divergence_c7_corpus_goldens_missing.rs index 4c57232c3..840115a0d 100644 --- a/forge/tests/divergence_c7_corpus_goldens_missing.rs +++ b/forge/tests/divergence_c7_corpus_goldens_missing.rs @@ -4,7 +4,7 @@ //! add" — the doc mandates, as the external truth the toolchain does not //! author for itself (goal.md verification model (B); R-CHAR-3): //! -//! > The orchestrator authors a NEW corpus program — `conformance/option_result.th` +//! > The orchestrator authors a new corpus program — `conformance/option_result.th` //! > (`Some`/`None`/`Ok`/`Err` construct + match + `is` + a payload-in-contract //! > `ens`, certifying L3) and extends the C4 string corpus with //! > `conformance/parse_u64.th` (the `String`→`Option` parser, certifying L3 diff --git a/forge/tests/divergence_collections.rs b/forge/tests/divergence_collections.rs index f3c4c84c1..f978a48cc 100644 --- a/forge/tests/divergence_collections.rs +++ b/forge/tests/divergence_collections.rs @@ -126,7 +126,7 @@ fn divergence_push_one_l3_not_mutation_gated_l0() { /// Confirmation (not a divergence — this passes today): `checked_get` is the /// no-OOB accessor. It certifies L3 / pure, and the `req i < v.len()` is -/// load-bearing. An off-by-one bound `req i <= v.len()` leaves `get`'s +/// required. An off-by-one bound `req i <= v.len()` leaves `get`'s /// index precondition undischarged → L0 (not laundered). This test asserts both /// the L3 and the bound; it pins that the accessor is not a no-op. It is /// un-ignored because it passes against `a48d2a1`; if a future change launders @@ -151,7 +151,7 @@ fn confirm_checked_get_bound_is_load_bearing() { ); assert_eq!(cg["effects"], serde_json::json!(["pure"])); - // (2) the bound is load-bearing: an off-by-one `req i <= v.len()` + // (2) the bound is required: an off-by-one `req i <= v.len()` // (the design's `oob_get_no_req`-class negative, R-DEFER-9 non-vacuity) leaves // get's `i < len` precondition undischarged -> L0 (caught, not laundered). let fixture = diff --git a/forge/tests/divergence_covenant_assign_to_immutable.rs b/forge/tests/divergence_covenant_assign_to_immutable.rs index 27b58513c..0a70758b9 100644 --- a/forge/tests/divergence_covenant_assign_to_immutable.rs +++ b/forge/tests/divergence_covenant_assign_to_immutable.rs @@ -6,7 +6,7 @@ //! Authority: `.design/stage1-forge-tier.md` REQ-4 — the `falsify` run is "aimed at //! the executable semantics". `covenant_eval.rs` module docs (the faithfulness //! contract): the evaluator admits the pure scalar fragment, and "anything outside -//! it … is an honest `CovenantEvalError::Unsupported` … it never silently evaluates +//! it … is an `CovenantEvalError::Unsupported` … it never silently evaluates //! a wrong value, mirroring `thermite_tv::exec_encode`'s `RefEncodeError::Unsupported` //! (R-CODE-2 / R-APG-1)." `thermite-syntax/src/ast.rs` `Stmt::Let { mutable: bool, .. }` //! records whether a binding is `mut`; the parser populates it. @@ -14,11 +14,11 @@ //! Divergence: `covenant_eval.rs` `eval_stmts` destructures `Stmt::Let { name, init, .. }` //! and DISCARDS `mutable`, and its `Stmt::Assign` arm re-`insert`s the target name into //! the env unconditionally. So an assignment `r = 1` to a binding introduced by an -//! IMMUTABLE `let r = 0;` is accepted as a valid mutation and the body evaluates to a +//! immutable `let r = 0;` is accepted as a valid mutation and the body evaluates to a //! concrete value — while Rust/Verus reject the body outright //! (`error[E0384]: cannot assign twice to immutable variable`). The evaluator therefore //! validates a covenant (reports a clean `falsify_generated > 0`, `falsify_refuted == 0` -//! run) on a body that has NO well-defined value: a silent wrong value, the precise +//! run) on a body that has no well-defined value: a silent wrong value, the precise //! thing the module contract and REQ-4 forbid. An assignment to a non-`mut` binding is //! outside the executable fragment and must surface as a loud covenant error. //! @@ -29,7 +29,7 @@ //! { let r = 0; if x > 0 { r = 1; } else { r = 1; } r } //! ``` //! -//! `r` is declared by an IMMUTABLE `let r = 0;`, so `r = 1;` is a compile error in +//! `r` is declared by an immutable `let r = 0;`, so `r = 1;` is a compile error in //! Verus — the body never builds. The covenant evaluator instead threads the (illegal) //! mutation and reports a clean validated `falsify` run, identical to the control with //! a legal `let mut r = 0;` (`ASSIGN_MUT_CONTROL`). The control isolates the divergence @@ -101,9 +101,9 @@ fn first_cert(program: &str, name: &str) -> Value { .unwrap_or_else(|| panic!("forge --json must emit at least one cert: {value}")) } -/// The DIVERGENCE input: `r` is introduced by an IMMUTABLE `let r = 0;`, then assigned +/// The divergence input: `r` is introduced by an immutable `let r = 0;`, then assigned /// `r = 1;` in both `if` branches. Verus rejects this body (`cannot assign twice to -/// immutable variable`), so it has no executable value and the covenant must NOT report +/// immutable variable`), so it has no executable value and the covenant must not report /// a clean validated `falsify` run — the evaluator must surface the assignment to a /// non-`mut` binding as a loud covenant error (outside the executable fragment). const ASSIGN_IMMUTABLE: &str = "\ @@ -116,8 +116,8 @@ fn setone(x: u64) -> u64 witness { inhabit (0); falsify 1000; } "; -/// The control: the SAME program with a LEGAL `let mut r = 0;`. This body is well-formed -/// and returns 1 for every input, so the covenant validates with a clean run. The ONLY +/// The control: the same program with a LEGAL `let mut r = 0;`. This body is well-formed +/// and returns 1 for every input, so the covenant validates with a clean run. The only /// textual difference from `ASSIGN_IMMUTABLE` is the `mut` keyword — isolating the /// divergence to the dropped `Stmt::Let.mutable` flag in `covenant_eval`. const ASSIGN_MUT_CONTROL: &str = "\ @@ -140,7 +140,7 @@ fn assignment_to_immutable_let_is_not_a_silent_clean_covenant() { return; } - // Control: the legal `let mut` body validates cleanly (no covenant refutation, a + // Control: the legal `let mut` body validates (no covenant refutation, a // clean falsify run). This establishes that the covenant evidence shape below is the // "validated" shape, not an artifact of some unrelated gate. let control = first_cert(ASSIGN_MUT_CONTROL, "mut"); @@ -156,9 +156,9 @@ fn assignment_to_immutable_let_is_not_a_silent_clean_covenant() { ); // Authority (REQ-4 / the covenant_eval faithfulness contract): `r` is declared by an - // IMMUTABLE `let r = 0;`, so `r = 1;` is ill-formed in Rust/Verus and the body has no + // immutable `let r = 0;`, so `r = 1;` is ill-formed in Rust/Verus and the body has no // executable value. The evaluator must surface the assignment to a non-`mut` binding - // as a loud covenant error — it must NOT silently thread the illegal mutation and + // as a loud covenant error — it must not silently thread the illegal mutation and // report a clean validated falsify run. The divergence: it drops `Stmt::Let.mutable`, // accepts the assignment, and produces covenant_evidence IDENTICAL to the `let mut` // control (a clean `falsify_refuted == 0` run over `falsify_generated > 0` inputs) on diff --git a/forge/tests/divergence_covenant_bitwise_not_integer.rs b/forge/tests/divergence_covenant_bitwise_not_integer.rs index ba900ffba..7e1f9ec65 100644 --- a/forge/tests/divergence_covenant_bitwise_not_integer.rs +++ b/forge/tests/divergence_covenant_bitwise_not_integer.rs @@ -3,7 +3,7 @@ //! is in-language and L3-certifiable. //! //! Authority: -//! - `.design/syntax/ast.md` REQ-10 / OQ-4: there is ONE `!` token / ONE +//! - `.design/syntax/ast.md` REQ-10 / OQ-4: there is one `!` token / one //! `UnaryOp::Not` whose meaning is per the operand type — "**bitwise-not on an //! integer type, logical-not on `bool`** ... resolved by Verus's type-directed //! `!`". OQ-4: "a fn using `!` on an integer and a fn using `!` on `bool` both @@ -14,20 +14,20 @@ //! - `forge/src/covenant_eval.rs` module docs (the evaluator's own stated fragment): //! "The evaluator admits the pure scalar fragment: integer (`u32`/`u64`/`usize`) //! and `bool` values; the arithmetic/comparison/logical/**bitwise** operators; -//! `!`; ...". Integer `!` is therefore claimed to be INSIDE the fragment, not an -//! out-of-fragment honest `Unsupported`. +//! `!`; ...". Integer `!` is therefore claimed to be inside the fragment, not an +//! out-of-fragment `Unsupported`. //! //! Divergence: `covenant_eval::eval_expr`'s `Expr::Unary { op: UnaryOp::Not, .. }` arm -//! evaluates ONLY `Value::Bool(!v.as_bool()?)` — an integer operand hits `as_bool()` and +//! evaluates only `Value::Bool(!v.as_bool()?)` — an integer operand hits `as_bool()` and //! returns `CovenantEvalError::Type("expected a bool, found integer N")`. In //! `covenant_engine::analyze_covenant` that non-Trap eval error becomes a -//! `CovenantError::UnsupportedItem`, so the covenant gate REFUSES the item before burn -//! (`CovenantUnsupportedItem`, L0). The same item WITHOUT a witness block certifies L3. +//! `CovenantError::UnsupportedItem`, so the covenant gate refuses the item before burn +//! (`CovenantUnsupportedItem`, L0). The same item without a witness block certifies L3. //! A sound, in-language item is downgraded from L3 to an L0 refusal purely because it //! carries a covenant and uses the documented integer `!` — a fragment-completeness //! divergence (REQ-4), not an out-of-fragment Unsupported. //! -//! Control: the same `fn` with NO witness block certifies L3 (verified against the live +//! Control: the same `fn` with no witness block certifies L3 (verified against the live //! binary), isolating the divergence to the covenant evaluator's unary-not arm. //! //! Tracking: filed by the critic (see report). `forge check` resolves the verus version @@ -96,8 +96,8 @@ fn first_cert(program: &str, name: &str) -> Value { /// `fn flipbits(x: u32) -> u32 ... { !x }` with `ens result == !x` — a sound, in-language /// item. `!x` on a `u32` is bitwise-not (ast.md REQ-10/OQ-4), Verus-native. The body -/// agrees with `ens` for every `x`, so the covenant should VALIDATE and the item should -/// certify L3 with covenant evidence. The ONLY textual difference from the control is the +/// agrees with `ens` for every `x`, so the covenant should validate and the item should +/// certify L3 with covenant evidence. The only textual difference from the control is the /// trailing `witness` block. const FLIP_WITH_WITNESS: &str = "\ fn flipbits(x: u32) -> u32 @@ -109,7 +109,7 @@ fn flipbits(x: u32) -> u32 witness { inhabit (5); falsify 20; } "; -/// The control: the SAME `fn` with NO witness block. It certifies L3 (the integer `!` +/// The control: the same `fn` with no witness block. It certifies L3 (the integer `!` /// is Verus-native; ast.md OQ-4 — it certifies under Verus's type-directed `!`). const FLIP_NO_WITNESS: &str = "\ fn flipbits(x: u32) -> u32 @@ -140,9 +140,9 @@ fn covenant_admits_integer_bitwise_not_in_its_fragment() { ); // Authority (ast.md REQ-10/OQ-4 + covenant_eval.rs's own stated fragment): integer - // `!` is INSIDE the covenant scalar fragment. The covenanted item must therefore be + // `!` is inside the covenant scalar fragment. The covenanted item must therefore be // covenant-CHECKED (validated, since the body == `ens` for all x) and certify L3 with - // covenant evidence — NOT refused as `CovenantUnsupportedItem`. + // covenant evidence — not refused as `CovenantUnsupportedItem`. let cert = first_cert(FLIP_WITH_WITNESS, "withwitness"); assert_eq!( cert["level"], diff --git a/forge/tests/divergence_covenant_out_of_range_witness.rs b/forge/tests/divergence_covenant_out_of_range_witness.rs index 2f5d4622b..0becc146a 100644 --- a/forge/tests/divergence_covenant_out_of_range_witness.rs +++ b/forge/tests/divergence_covenant_out_of_range_witness.rs @@ -1,13 +1,13 @@ //! Divergence test (acto-critic): the covenant engine accepts an `inhabit` witness -//! whose integer value is OUTSIDE the declared parameter width, then uses that -//! out-of-domain value to manufacture a FALSE `CovenantRefuted` on a sound item. +//! whose integer value is outside the declared parameter width, then uses that +//! out-of-domain value to manufacture a false `CovenantRefuted` on a sound item. //! //! Authority: `.design/stage1-forge-tier.md` REQ-4 — "`inhabit` witnesses are //! type-checked and *executed* against `req`" and a `falsify` hit is "the hard-fail //! verdict `CovenantRefuted` with the counterexample attached". A `CovenantRefuted` -//! is owed ONLY for "a `req`-satisfying input the body violates `ens` on" (REQ-4 / +//! is owed only for "a `req`-satisfying input the body violates `ens` on" (REQ-4 / //! AC-8). An input is a value of the parameter's declared type; `4294967296` (= 2^32) -//! is NOT a `u32`, so it is not an input of `fn f(x: u32)` at all and cannot witness a +//! is not a `u32`, so it is not an input of `fn f(x: u32)` at all and cannot witness a //! refutation. //! //! Divergence: `forge/src/covenant_engine.rs` `bind_params` checks only the value KIND @@ -18,15 +18,15 @@ //! parameter is accepted as a valid `req`-satisfying witness, then run through the body //! → `ens`: the body `x as u32` truncates to `0` (the correct truncating-cast model), //! `ens result == x` becomes `0 == 4294967296` → false → `CovenantRefuted`. The item is -//! SOUND for every actual `u32` input (`x as u32 == x` holds for all `x: u32`); only the +//! sound for every actual `u32` input (`x as u32 == x` holds for all `x: u32`); only the //! out-of-domain witness manufactures the refutation. The author witness must be //! width-checked against the parameter type (a `WitnessTypeMismatch`/`ArityMismatch`- //! class covenant error — an out-of-range literal is ill-typed, Verus rejects -//! `4294967296u32`), NOT silently widened and used to refute a correct item. +//! `4294967296u32`), not silently widened and used to refute a correct item. //! -//! Concrete divergence (`OOB_WITNESS`) vs control (`INRANGE_WITNESS`): the ONLY textual +//! Concrete divergence (`OOB_WITNESS`) vs control (`INRANGE_WITNESS`): the only textual //! difference is the witness value `4294967296` (out of `u32` range) vs `5` (in range). -//! The in-range control validates cleanly; the out-of-range version is refuted. +//! The in-range control validates; the out-of-range version is refuted. //! //! Tracking: #300 (filed by the critic). //! @@ -93,7 +93,7 @@ fn first_cert(program: &str, name: &str) -> Value { .unwrap_or_else(|| panic!("forge --json must emit at least one cert: {value}")) } -/// The DIVERGENCE input: the witness `4294967296` (= 2^32) is OUTSIDE the `u32` range of +/// The divergence input: the witness `4294967296` (= 2^32) is outside the `u32` range of /// the parameter `x`, so it is not an input of `f` at all. The item is sound for every /// real `u32` (`x as u32 == x`), yet the out-of-domain witness manufactures a refutation. const OOB_WITNESS: &str = "\ @@ -106,7 +106,7 @@ fn f(x: u32) -> u32 witness { inhabit (4294967296); falsify 100; } "; -/// The control: the SAME program with an IN-RANGE witness `5`. This validates cleanly +/// The control: the same program with an IN-RANGE witness `5`. This validates /// (no refutation). The only textual difference from `OOB_WITNESS` is the witness value, /// isolating the divergence to the missing parameter-width check in `bind_params`. const INRANGE_WITNESS: &str = "\ @@ -129,7 +129,7 @@ fn out_of_range_witness_must_not_manufacture_a_false_covenant_refutation() { return; } - // Control: the in-range witness validates cleanly (no covenant reject). This proves + // Control: the in-range witness validates (no covenant reject). This proves // the item is sound and the refutation below is an artifact of the out-of-range // witness, not of the contract or the body. let control = first_cert(INRANGE_WITNESS, "inrange"); @@ -139,9 +139,9 @@ fn out_of_range_witness_must_not_manufacture_a_false_covenant_refutation() { rejected, got {control}" ); - // Authority (REQ-4 / AC-8): a `CovenantRefuted` is owed ONLY for a `req`-satisfying + // Authority (REQ-4 / AC-8): a `CovenantRefuted` is owed only for a `req`-satisfying // INPUT the body violates `ens` on. `4294967296` is not a `u32`, so it is not an input - // of `f`; the author witness must be width-checked and refused as ill-typed, NEVER + // of `f`; the author witness must be width-checked and refused as ill-typed, never // used to refute a sound item. The divergence: `bind_params` checks only Int-vs-Bool // (not the u32 width), accepts the out-of-range literal, and the truncating-cast body // (`x as u32` -> 0) makes `ens result == x` (0 == 2^32) false, manufacturing a diff --git a/forge/tests/divergence_covenant_slag_ordering.rs b/forge/tests/divergence_covenant_slag_ordering.rs index 1e2c9dd01..bff82c8ea 100644 --- a/forge/tests/divergence_covenant_slag_ordering.rs +++ b/forge/tests/divergence_covenant_slag_ordering.rs @@ -1,32 +1,32 @@ //! Divergence test (acto-critic): the covenant pre-stage in `forge/src/check.rs` -//! runs BEFORE the `#[slag]` gate, so a `#[slag]` item that also carries a `witness` +//! runs before the `#[slag]` gate, so a `#[slag]` item that also carries a `witness` //! block is covenant-falsified against its (proof-exempt) stub body and emitted as a //! `CovenantRefuted` cert with `slag: false` — losing the slag flag/metadata and //! manufacturing a hard-fail on a proof-exempt item. //! //! Authority: //! - `.design/forge/slag.md` REQ-2: a VALID `#[slag]` item is "exempt from the L3 -//! proof obligation: `forge check` does NOT invoke `verus` on it ... The +//! proof obligation: `forge check` does not invoke `verus` on it ... The //! certificate level is `Level::L1` ... with `slag: true`." The body content is //! explicitly "irrelevant to slag certification" (slag.md §"Exact ... fixture //! programs": "the body is proof-exempt, so its content is irrelevant"). //! - `.design/forge/slag.md` REQ-4 (audit visibility): "a slag item is visible in //! the certificate ... the existing `Certificate.slag: bool` is set `true`." -//! - `.design/forge/slag.md` REQ-5: the slag gate runs PER ITEM with +//! - `.design/forge/slag.md` REQ-5: the slag gate runs per ITEM with //! validate -> triage(a/b/c) -> emit L1 `slag: true`; "a non-slag item ... is //! untouched." //! //! Divergence: `check::check_file`'s per-item loop runs the covenant pre-stage //! (`covenant_gate(analyze_covenant(...))`, the `Item::Fn` block ~check.rs L446) and -//! `continue`s on a `CovenantGate::Refuted` BEFORE ever reaching the `gate_fn` slag +//! `continue`s on a `CovenantGate::Refuted` before ever reaching the `gate_fn` slag //! short-circuit (~check.rs L509). So a `#[slag]` item carrying a `witness` block: //! (1) has its DELIBERATE-STUB body executed by the `falsify` driver, and //! (2) is emitted as `CovenantRefuted` with `slag: false`, -//! even though the SAME `#[slag]` item WITHOUT the witness certifies `L1`, `slag:true` +//! even though the same `#[slag]` item without the witness certifies `L1`, `slag:true` //! (the stub body is proof-exempt). The covenant pre-stage produces a false verdict //! shape on a slag item and drops its audit-visibility flag. //! -//! Control: the same `#[slag]` item with NO witness block certifies `L1`, `slag:true` +//! Control: the same `#[slag]` item with no witness block certifies `L1`, `slag:true` //! (verified against the live binary in this test), isolating the divergence to the //! covenant-before-slag ordering. //! @@ -95,8 +95,8 @@ fn first_cert(program: &str, name: &str) -> Value { } /// A valid `#[slag]` item (all three fields present, non-empty) whose stub body `{ 0 }` -/// does NOT satisfy `ens result == x` — but per slag.md REQ-2 the body is PROOF-EXEMPT, -/// so the item certifies L1 `slag: true` regardless. Adding a `witness` block must NOT +/// does not satisfy `ens result == x` — but per slag.md REQ-2 the body is proof-EXEMPT, +/// so the item certifies L1 `slag: true` regardless. Adding a `witness` block must not /// turn the proof-exempt stub into a covenant hard-fail nor drop the slag flag. const SLAG_WITH_WITNESS: &str = "\ #[slag(reason = \"vendored\", owner = \"agent:forge-7\", review = \"required\")] @@ -109,7 +109,7 @@ fn f(x: u32) -> u32 witness { inhabit (5); falsify 10; } "; -/// The control: the SAME `#[slag]` item with NO witness block. It certifies L1, +/// The control: the same `#[slag]` item with no witness block. It certifies L1, /// `slag: true` (the proof-exempt stub body is irrelevant — slag.md REQ-2/REQ-4). const SLAG_NO_WITNESS: &str = "\ #[slag(reason = \"vendored\", owner = \"agent:forge-7\", review = \"required\")] @@ -147,7 +147,7 @@ fn slag_item_keeps_its_slag_flag_under_a_covenant_block() { // Authority (slag.md REQ-2/REQ-4/REQ-5): a #[slag] item is proof-exempt and its // certificate carries slag:true. Adding a `witness` block does not strip the slag // identity. The covenant pre-stage (check.rs, the Item::Fn covenant block that runs - // BEFORE the gate_fn slag short-circuit) executes the proof-exempt stub body, emits + // before the gate_fn slag short-circuit) executes the proof-exempt stub body, emits // a CovenantRefuted, and `continue`s — so the cert reports slag:false (the slag // flag/metadata are lost) on an item that IS slag. let cert = first_cert(SLAG_WITH_WITNESS, "withwitness"); diff --git a/forge/tests/divergence_covenant_stmt_if_false_refutation.rs b/forge/tests/divergence_covenant_stmt_if_false_refutation.rs index 957332c2b..422076d3c 100644 --- a/forge/tests/divergence_covenant_stmt_if_false_refutation.rs +++ b/forge/tests/divergence_covenant_stmt_if_false_refutation.rs @@ -1,20 +1,20 @@ //! Divergence test (acto-critic): the covenant engine's executable-semantics -//! evaluator (`forge/src/covenant_eval.rs`) manufactures a FALSE refutation on a +//! evaluator (`forge/src/covenant_eval.rs`) manufactures a false refutation on a //! statement-position `if`/`else` whose branches carry a tail expression. //! //! Authority: `.design/stage1-forge-tier.md` REQ-4 — the `falsify` run is "aimed at //! the executable semantics", and a `CovenantRefuted` is "a `req`-satisfying input -//! whose body violates `ens`". AC-8 — a `CovenantRefuted` must name a genuine -//! counterexample. The `covenant_eval` module contract is explicit: it "never +//! whose body violates `ens`". AC-8 requires a `CovenantRefuted` to name a +//! counterexample. The `covenant_eval` module contract states that it never //! silently evaluates a wrong value" (module docs, `forge/src/covenant_eval.rs`). //! //! Divergence: the surface grammar (`thermite-syntax/src/parser.rs`, the `TokKind::If` -//! arm of block parsing) emits `Stmt::If` for an `if`/`else` that is NOT in tail +//! arm of block parsing) emits `Stmt::If` for an `if`/`else` that is not in tail //! position even when its branches HAVE tail expressions — e.g. the if/else followed //! by a further tail. Rust/Verus executable semantics DISCARD a statement-position //! `if`'s value. `eval_stmts` (`forge/src/covenant_eval.rs`, the `Stmt::If` arm) //! instead reads the taken branch's `then.tail` / `else_.tail` and returns it as an -//! EARLY RETURN of the enclosing block, so the block evaluates to the wrong value. +//! EARLY return of the enclosing block, so the block evaluates to the wrong value. //! //! Concrete divergence: //! @@ -23,12 +23,12 @@ //! { if x > 0 { x } else { x } 0 } //! ``` //! -//! This function returns `0` for EVERY input (the if/else value is discarded; the +//! This function returns `0` for every input (the if/else value is discarded; the //! tail `0` is the result), so `ens result == 0` holds universally and the covenant -//! must VALIDATE. The evaluator instead computes `result == x` and reports +//! must validate. The evaluator instead computes `result == x` and reports //! `CovenantRefuted` whenever `x != 0`, blocking a correct item from the L3 burn. //! -//! A control fixture with the SAME contract and body `{ 0 }` (no statement-if) +//! A control fixture with the same contract and body `{ 0 }` (no statement-if) //! validates and burns to L3, isolating the divergence to the `Stmt::If` evaluation. //! //! Tracking: #298 (filed by the critic). @@ -99,8 +99,8 @@ fn first_cert(program: &str, name: &str) -> Value { /// A function that returns `0` for every input: the statement-position `if`/`else` /// value is discarded by Rust/Verus executable semantics; the tail `0` is the -/// result. `ens result == 0` therefore holds universally and the covenant MUST -/// validate (it must NOT be `CovenantRefuted`). The `inhabit (0)` author witness +/// result. `ens result == 0` therefore holds universally and the covenant must +/// validate (it must not be `CovenantRefuted`). The `inhabit (0)` author witness /// already satisfies `req` and `ens`, so any refutation comes from the `falsify` /// generator drawing some `x != 0`. const ALWAYS_ZERO_STMT_IF: &str = "\ @@ -113,7 +113,7 @@ fn alwayszero(x: u64) -> u64 witness { inhabit (0); falsify 1000; } "; -/// The control: the SAME contract and an equivalent body with no statement-if. It +/// The control: the same contract and an equivalent body with no statement-if. It /// validates and burns (verified against the live binary), isolating the divergence /// to the `Stmt::If` evaluation. const ALWAYS_ZERO_PLAIN: &str = "\ @@ -136,7 +136,7 @@ fn stmt_position_if_value_must_be_discarded_not_a_false_refutation() { return; } - // Control: the plain body must NOT be CovenantRefuted (it returns 0 always). + // Control: the plain body must not be CovenantRefuted (it returns 0 always). let control = first_cert(ALWAYS_ZERO_PLAIN, "plain"); assert_ne!( control["reject"]["cause"], "CovenantRefuted", @@ -146,8 +146,8 @@ fn stmt_position_if_value_must_be_discarded_not_a_false_refutation() { // Authority (REQ-4 / the covenant_eval faithfulness contract): the // statement-position `if`/`else` value is DISCARDED, so this body also returns 0 - // for every input and `ens result == 0` holds universally — the covenant MUST - // validate, NOT refute. The evaluator's `Stmt::If` arm instead returns the taken + // for every input and `ens result == 0` holds universally — the covenant must + // validate, not refute. The evaluator's `Stmt::If` arm instead returns the taken // branch's tail as an early return, manufacturing a `CovenantRefuted` on `x != 0`. let cert = first_cert(ALWAYS_ZERO_STMT_IF, "stmtif"); assert_ne!( diff --git a/forge/tests/divergence_effect_link_string_wrappers.rs b/forge/tests/divergence_effect_link_string_wrappers.rs index 0be472eb7..7c1ce0b81 100644 --- a/forge/tests/divergence_effect_link_string_wrappers.rs +++ b/forge/tests/divergence_effect_link_string_wrappers.rs @@ -7,7 +7,7 @@ //! (… the latter over Stage 7 `String`), `os::write`/`os::print` //! (`std::io::stdout().write_all`, Stage 7 `String` arg). Each wrapper's //! signature MATCHES the `#[boundary]` primitive it backs (params + return)." -//! - REQ-3: "a verified program using an effect primitive COMPILES + RUNS + does +//! - REQ-3: "a verified program using an effect primitive compiles + runs + does //! real I/O". //! - the wrapper-set table: rows `os::write` / `os::print` (`write(output)`, //! Stage-7 `String` arg) + `os::read_line` (Stage-7 `String` return). @@ -86,12 +86,12 @@ fn write_fixture(name: &str, src: &str) -> PathBuf { p } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -113,9 +113,9 @@ fn print_wrapper_builds_and_runs() { if !linux_build_run_supported("print_wrapper_builds_and_runs") { return; } - // AUTHORITY (08-runnable-effect-link.md REQ-1): "os::write/os::print + // authority (08-runnable-effect-link.md REQ-1): "os::write/os::print // (std::io::stdout().write_all, Stage 7 String arg). Each wrapper's signature - // MATCHES the #[boundary] primitive it backs"; REQ-3: it COMPILES + RUNS. + // MATCHES the #[boundary] primitive it backs"; REQ-3: it compiles + runs. let fixture = write_fixture("print", PRINT_DEMO); let (ok, stdout, stderr) = run_forge_build(&[fixture.to_str().unwrap(), "--entry", "greet", "--json"]); @@ -144,7 +144,7 @@ fn read_line_wrapper_builds() { if !linux_build_run_supported("read_line_wrapper_builds") { return; } - // AUTHORITY (08-runnable-effect-link.md REQ-1): "os::read_byte/os::read_line + // authority (08-runnable-effect-link.md REQ-1): "os::read_byte/os::read_line // (std::io::stdin().read/read_line, the latter over Stage 7 String)". let fixture = write_fixture("read_line", READ_LINE_DEMO); let (ok, stdout, stderr) = diff --git a/forge/tests/divergence_harness_names_checked_item.rs b/forge/tests/divergence_harness_names_checked_item.rs index d930923de..26e3ad3d2 100644 --- a/forge/tests/divergence_harness_names_checked_item.rs +++ b/forge/tests/divergence_harness_names_checked_item.rs @@ -1,11 +1,11 @@ //! Divergence pin (crosslink #92, the diagnostics half) — a per-item verus -//! harness was named after the FIRST item of the woven sub-program rather than +//! harness was named after the first item of the woven sub-program rather than //! the item it checks, so a failure reported its source location under a //! sibling's name. //! //! `run_verus` took its scratch-dir/`.rs` stem from //! `program.items.first().map(|i| i.name())`. `item_subprogram` weaves the ADT -//! decls and spec fns first and pushes the checked item LAST, so any item with +//! decls and spec fns first and pushes the checked item last, so any item with //! anything woven ahead of it reported diagnostics under the wrong name: //! //! spec fn helper(n: u64) -> u64 { n } diff --git a/forge/tests/divergence_holed_lowering.rs b/forge/tests/divergence_holed_lowering.rs index 1074f2ad8..02e2b0f32 100644 --- a/forge/tests/divergence_holed_lowering.rs +++ b/forge/tests/divergence_holed_lowering.rs @@ -21,7 +21,7 @@ //! Authority: //! - `.design/forge/goal-repl.md` REQ-4: "an item with any open hole is //! L0-equivalent until every hole is filled"; REQ-5: a holed item gets "no -//! lowering, no verus"; Architecture: "A holed item NEVER reaches verus; it +//! lowering, no verus"; Architecture: "A holed item never reaches verus; it //! can never accidentally certify." //! - `thermite_syntax::ast` (`FnItem.holes` doc, shipped by bf29a050): "a holed //! item never lowers — it short-circuits at `forge check`" — the @@ -136,7 +136,7 @@ fn divergence_build_emits_artifact_for_holed_item() { /// ships it to verus, and reports the body `faithful` — a TV verdict certifying /// fidelity of an incomplete body whose open goal was silently deleted. /// Authority: `.design/forge/goal-repl.md` REQ-5 + Architecture ("a holed item -/// NEVER reaches verus") + the `FnItem.holes` contract ("a holed item never +/// never reaches verus") + the `FnItem.holes` contract ("a holed item never /// lowers"). Expected: the holed body is never counted `faithful` (the /// classes are a refusal or an explicit skip naming the open hole). #[test] diff --git a/forge/tests/divergence_kernel_time_boundary.rs b/forge/tests/divergence_kernel_time_boundary.rs index 8aac0369c..025fea98a 100644 --- a/forge/tests/divergence_kernel_time_boundary.rs +++ b/forge/tests/divergence_kernel_time_boundary.rs @@ -12,12 +12,12 @@ //! no syscall mapping"): an admitted-effect boundary carries a std-bodied wrapper. //! //! Amended authority (R-CHAR-3 trace — `.design/build/kernel-target.md`): -//! - OQ-2 (RESOLVED — REJECT; amended by #198): "`time`/`rand` MOVE INTO the reject +//! - OQ-2 (resolved — REJECT; amended by #198): "`time`/`rand` MOVE INTO the reject //! set: the v1 kernel admit set is now EXACTLY `pure`/`alloc`/`panic`/`diverge`, //! and `KERNEL_REJECTED_FX = [\"read\",\"write\",\"net\",\"term\",\"time\",\"rand\"]`." //! A kernel has no ambient clock (`clock_gettime`) or entropy (`getrandom`) any //! more than it has `read`/`write` (the critic's own observation). -//! - REQ-3: a kernel refusal is "a NAMED-effect, nonzero-exit, NO-artifact +//! - REQ-3: a kernel refusal is "a named-effect, nonzero-exit, NO-artifact //! structured `ForgeError`" — so `effect_link_demo.th --target kernel` returns //! a refusal naming `time`, exit 2, no artifact, and no raw `E0433` reaches //! the user (the std-bodied wrapper is refused before codegen). diff --git a/forge/tests/divergence_multi_adt_subprogram.rs b/forge/tests/divergence_multi_adt_subprogram.rs index e611e8251..877c03406 100644 --- a/forge/tests/divergence_multi_adt_subprogram.rs +++ b/forge/tests/divergence_multi_adt_subprogram.rs @@ -180,7 +180,7 @@ fn spare_adt_decl_does_not_break_its_siblings() { let certs = check_program("spare", SPARE_DECL_PROGRAM); // .design/forge/check.md REQ-5 + thermite-design.md §6: a correct source - // certifies L3. `Unused` carries no contract to discharge, exactly as `Role` + // certifies L3. `Unused` carries no contract to discharge, as `Role` // does; both are enum decls lowering to a Verus enum (01-adts REQ-9). assert_eq!( level_of(&certs, "Role"), diff --git a/forge/tests/divergence_mutation.rs b/forge/tests/divergence_mutation.rs index a8e92ce35..f4151bfe0 100644 --- a/forge/tests/divergence_mutation.rs +++ b/forge/tests/divergence_mutation.rs @@ -9,7 +9,7 @@ //! reports exactly which mutants survived." The floor's purpose is to catch a //! contract that under-constrains the body. //! - `goal.md` R-DEFER-9 (anti-Goodhart): "the design's §7 battery exists -//! precisely to catch this" — a contract that does not constrain its result +//! to catch this" — a contract that does not constrain its result //! must not certify clean. A path that lets a weak contract certify L3 //! unscored is a bypass = a hole. //! - `.design/forge/mutation-scoring.md` REQ-5 (the floor gate) + REQ-7 (the @@ -125,7 +125,7 @@ fn is_clean_l3(cert: &Value) -> bool { // Expected (authority): a contract that fails to constrain its result must not // certify clean L3 unscored; the floor must catch it (§7 / R-DEFER-9). The // early-return mutant should be generatable for any body so 0/0 is unreachable -// for a real fn. +// for a fn. // // Actual (fa55760): clean L3 certify, `mutants_killed: "0/0"`, ungated. // diff --git a/forge/tests/divergence_numfmt_display_order.rs b/forge/tests/divergence_numfmt_display_order.rs index ff595a263..60b408441 100644 --- a/forge/tests/divergence_numfmt_display_order.rs +++ b/forge/tests/divergence_numfmt_display_order.rs @@ -40,12 +40,12 @@ fn forge_bin() -> PathBuf { PathBuf::from(env!("CARGO_BIN_EXE_forge")) } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { diff --git a/forge/tests/divergence_provenance.rs b/forge/tests/divergence_provenance.rs index fe6783250..6c6fde65b 100644 --- a/forge/tests/divergence_provenance.rs +++ b/forge/tests/divergence_provenance.rs @@ -18,7 +18,7 @@ //! the validator rejects any `Expr::StructLit` of a `#[sealed]` struct with //! `SpecError::SealedConstruction`. `forge check` on a bypass program now fails at //! validation (a whole-program spec error — exit non-zero, the `SealedConstruction` -//! diagnostic on stderr, no L3 certificate ever emitted), exactly as every other +//! diagnostic on stderr, no L3 certificate ever emitted), as every other //! validator reject (`NonExhaustiveMatch`, `UnknownField`, …) does. The door is the //! only launder point — its body is foreign (`external_body`), with no in-language //! `StructLit`, so the safe doored path (`query(parameterize(input))`) is unaffected @@ -26,9 +26,9 @@ //! //! Authority (`.design/basis/06-provenance-and-sinks.md`): //! - REQ-8: a `#[sealed]` clean type "CANNOT be constructed by a `StructLit` -//! anywhere in Thermite code, so the ONLY way to obtain one is through its +//! anywhere in Thermite code, so the only way to obtain one is through its //! `#[boundary]` door". `query(Sql { … })` is `SpecError::SealedConstruction`. -//! - REQ-2: "No mark-change exists outside a door … TRUE only because the clean +//! - REQ-2: "No mark-change exists outside a door … true only because the clean //! types are `#[sealed]` (REQ-8)." The StructLit launder is closed. //! - AC-7: these three `#[ignore]`d tests are un-ignored and pass — each launder //! yields `SealedConstruction` and does not certify L3. @@ -201,7 +201,7 @@ fn bypass_delete(u: User) -> u64 /// Taint bypass — `query(Sql { stmt: input.raw })` from a `Tainted input`. /// -/// AUTHORITY (06-provenance-and-sinks.md REQ-8/AC-7): `Sql` is `#[sealed]`, so a +/// authority (06-provenance-and-sinks.md REQ-8/AC-7): `Sql` is `#[sealed]`, so a /// `StructLit` minting it is `SpecError::SealedConstruction` — the door /// (`parameterize`) is the only launder point. The tainted payload reaching the /// SQL sink as a clean `Sql` via a struct literal must be rejected at validation, @@ -214,7 +214,7 @@ fn taint_structlit_bypass_must_not_certify_l3() { /// Secret bypass — `emit(Public { val: s.val })` from a `Secret s`. /// -/// AUTHORITY (06-provenance-and-sinks.md Axis 2 / REQ-8/AC-7): `Public` is +/// authority (06-provenance-and-sinks.md Axis 2 / REQ-8/AC-7): `Public` is /// `#[sealed]`, so a `StructLit` minting it is `SealedConstruction` — `declassify` /// is the only release door. A `Public` struct literal reading a secret payload is /// an un-audited release rejected at validation, never L3. Fixed by #77 — @@ -227,7 +227,7 @@ fn secret_structlit_bypass_must_not_certify_l3() { /// Capability bypass — `delete(Authorized { id: u.id })` from a `User u`. /// -/// AUTHORITY (06-provenance-and-sinks.md Axis 3 / REQ-8/AC-7): `Authorized` is +/// authority (06-provenance-and-sinks.md Axis 3 / REQ-8/AC-7): `Authorized` is /// `#[sealed]`, so a `StructLit` forging it is `SealedConstruction` — `authorize` /// is the only `Authorized` producer. A forged capability via a struct literal is /// rejected at validation, never L3. Fixed by #77 — un-ignored. diff --git a/forge/tests/divergence_rlimit_phrase_drift.rs b/forge/tests/divergence_rlimit_phrase_drift.rs index 9654c80ac..c003f16a2 100644 --- a/forge/tests/divergence_rlimit_phrase_drift.rs +++ b/forge/tests/divergence_rlimit_phrase_drift.rs @@ -14,7 +14,7 @@ //! phases (the per-phase copies deleted). With one shared discriminator the phrase set //! cannot drift again, and the z3-phrased resourceout is caught in every phase. //! -//! The z3 phrase is load-bearing rather than defensive padding: the distributed z3 +//! The z3 phrase is required rather than defensive padding: the distributed z3 //! binary (verus 0.2026.05.24.ecee80a toolchain, `~/.local/share/verus/verus-x86-linux/z3`) //! contains the literal diagnostic `max. resource limit exceeded`, Z3's own //! resourceout message (its `:reason-unknown` text on an rcounts exhaustion). That diff --git a/forge/tests/divergence_scheme_certs.rs b/forge/tests/divergence_scheme_certs.rs index f52b9c1d6..cbd93e6a2 100644 --- a/forge/tests/divergence_scheme_certs.rs +++ b/forge/tests/divergence_scheme_certs.rs @@ -129,7 +129,7 @@ fn divergence_list_fold_three_distinct_item_certs() { /// Companion (un-ignored, a no-divergence guard for the crux). The corpus /// `list_fold.th` certifies at project level L3: the generated `fold_list` / /// `for_all_list` schemes + the materialized `fold_bound_list` law verify under -/// real verus. This pins that the scheme engine itself is sound end-to-end; the +/// verus. This pins that the scheme engine itself is sound end-to-end; the /// divergence above is the per-item certificate identity, not a proof /// failure. Authority: `conformance/adt-schemes/cases.json` `certify` (all L3). #[test] @@ -141,7 +141,7 @@ fn list_fold_project_assurance_is_l3() { let certs = check_json(&corpus("list_fold.th")); // Every emitted certificate is L3 (the oracle certifies all items L3); no // item degrades. This holds even under the identity collision (the served - // cert is itself a genuine L3), so it stays green and is not the divergence. + // cert is itself a L3), so it stays green and is not the divergence. assert!( !certs.is_empty(), "forge must emit at least one certificate for list_fold.th" diff --git a/forge/tests/divergence_solver_vacuity.rs b/forge/tests/divergence_solver_vacuity.rs index 56c530a09..9311bdb7a 100644 --- a/forge/tests/divergence_solver_vacuity.rs +++ b/forge/tests/divergence_solver_vacuity.rs @@ -19,7 +19,7 @@ //! AC-1/AC-2/AC-3 + the "Resolved" vacuity-first ordering, not literal-copied //! from forge's own output. //! -//! These checks issue real verus queries, so they skip with an eprintln when verus is +//! These checks issue verus queries, so they skip with an eprintln when verus is //! absent (mirroring `solver_vacuity_conformance.rs`), never panic. `unwrap`/ //! `expect` are fine here — `tests/` is not anti-pattern-gated. @@ -108,7 +108,7 @@ fn check(name: &str, program: &str) -> Value { /// Run `forge check --json` and return the certificate for the named /// item. A program that declares an ADT emits a cert for the `struct`/`enum` -/// before the `fn`, so the fn's cert is NOT the first one — these ADT probes must +/// before the `fn`, so the fn's cert is not the first one — these ADT probes must /// look the cert up by item name (`check` returns only the first cert). fn check_item(name: &str, program: &str, item: &str) -> Value { let path = write_temp(name, program); @@ -330,9 +330,9 @@ fn fresh_unsat_req_is_detected() { // =========================================================================== // Vacuity-first ordering — `.design/forge/solver-vacuity.md` "Resolved" -// (CHECK-ORDER): an unsat `req` makes every `ensures` vacuously provable, so the +// (check-ORDER): an unsat `req` makes every `ensures` vacuously provable, so the // root cause must be reported as `VacuousPrecondition`, not mislabeled -// `SemanticTautology`. Conversely a genuine tautology with a satisfiable `req` +// `SemanticTautology`. Conversely a tautology with a satisfiable `req` // must still report `SemanticTautology`. // =========================================================================== @@ -362,7 +362,7 @@ fn unsat_req_with_tautological_ens_is_reported_as_vacuous_not_tautology() { ); } -/// A genuine tautology with a satisfiable `req` (`x < 100`): the vacuity check +/// A tautology with a satisfiable `req` (`x < 100`): the vacuity check /// passes (req is satisfiable), so the tautology check runs and fires. Authority: /// the tautology check runs only on a satisfiable precondition → `SemanticTautology`. #[test] @@ -390,11 +390,11 @@ fn tautology_with_satisfiable_req_is_reported_as_tautology() { // =========================================================================== // crosslink #275 — the ADT soundness hole. Before the fix, the harness builder // omitted the reachable `struct`/`enum` decls, so an ADT-returning / ADT-taking -// fn's harness referenced an undeclared type and failed to COMPILE (E0425); the +// fn's harness referenced an undeclared type and failed to compile (E0425); the // interpreter mapped that compile failure (`success:false, errors:0`) to the -// clean `Failed`, so BOTH anti-Goodhart checks silently no-op'd on every ADT fn +// clean `Failed`, so both anti-Goodhart checks silently no-op'd on every ADT fn // (R-CODE-4 "non-verdict read as clean"). The fix weaves the reachable ADT decls -// into the harness AND treats a `!success && errors == 0` (never-verified) +// into the harness and treats a `!success && errors == 0` (never-verified) // summary as a loud `ForgeError`, not clean. These probes pin the soundness // direction: a DEGENERATE ADT contract must now be CAUGHT, not silently certified. // Authority: `.design/forge/solver-vacuity.md` §7 steps 2-3 + AC-2/AC-3. diff --git a/forge/tests/divergence_spec_call_param_cast.rs b/forge/tests/divergence_spec_call_param_cast.rs index e6d96da2a..fc099f2be 100644 --- a/forge/tests/divergence_spec_call_param_cast.rs +++ b/forge/tests/divergence_spec_call_param_cast.rs @@ -1,5 +1,5 @@ //! End-to-end live pin for crosslink #225 — a recursive `spec fn` over a `u32` -//! param, named in an exec twin's contract, must certify L3 under real verus. +//! param, named in an exec twin's contract, must certify L3 under verus. //! //! The bug (root cause `thermite-lower/src/lower.rs`, the `plain_user_spec_call` //! arm): the recursive call `s_dec(n - 1)` in the `spec fn` body, plus the diff --git a/forge/tests/divergence_strings.rs b/forge/tests/divergence_strings.rs index 9da7b729e..489b1ae6a 100644 --- a/forge/tests/divergence_strings.rs +++ b/forge/tests/divergence_strings.rs @@ -207,7 +207,7 @@ fn confirm_byte_at_bound_is_load_bearing() { // (2) the OOB negative — the oracle's `oob_byte_at_no_req.program` (R-CHAR-3): // no `req s.len() > 0` -> byte_at's `0 < len` undischarged -> L0. // (3) an off-by-one bound `req i <= s.len()` -> `i < len` undischarged - // (`i == len` is OOB) -> L0 (the bound is load-bearing). + // (`i == len` is OOB) -> L0 (the bound is required). let fixture = std::env::temp_dir().join(format!("forge_div_str_oob_{}.th", std::process::id())); std::fs::write( &fixture, diff --git a/forge/tests/divergence_vacuity.rs b/forge/tests/divergence_vacuity.rs index a20751a2f..416e5e1d8 100644 --- a/forge/tests/divergence_vacuity.rs +++ b/forge/tests/divergence_vacuity.rs @@ -120,7 +120,7 @@ fn divergence_ens_implied_by_req_over_rejects_partial_implication() { } /// Companion (control): a contract whose only `ens` clause is a req conjunct is a -/// genuine (c) reject. Pins that the fix narrows the rule to the "every clause" +/// (c) reject. Pins that the fix narrows the rule to the "every clause" /// reading without regressing the true-positive. Authority: /// `conformance/vacuity/triage.json` `ens_conjunct_req` (cause `EnsImpliedByReq`). #[test] @@ -142,7 +142,7 @@ fn ens_fully_implied_by_req_still_rejects_c() { /// rejected with §7.1 (c) (`ens true` matches the `req true` conjunct). /// /// Authority: `.design/forge/vacuity-triage.md` OQ-4 — "A contract `ens true` + -/// `ens result == x` is NOT (a)-rejected (it carries a real conjunct)." REQ-3 +/// `ens result == x` is not (a)-rejected (it carries a real conjunct)." REQ-3 /// applies the same "every clause" logic: the whole `ens` is implied by `req` /// only if every clause is, and `result == x` is not implied by `req true`. So /// this contract is non-vacuous and must not be a (c) reject. @@ -156,7 +156,7 @@ fn divergence_redundant_true_clause_with_real_clause_not_c_rejected() { "multi_ens_true_plus_real", ); // The whole `ens` (true && result == x) is not implied by `req true` - // (`result == x` is a real obligation), so (c) must not fire. + // (`result == x` is a obligation), so (c) must not fire. assert_ne!( reject_cause(&cert).as_deref(), Some("EnsImpliedByReq"), @@ -169,7 +169,7 @@ fn divergence_redundant_true_clause_with_real_clause_not_c_rejected() { /// Guard (not a divergence; pins correct behavior so a fix cannot regress it): /// `Lt`/`Ne` identity operands (`x < x`, `x != x`) are not syntactically `true` /// and must not be (a)-rejected. Authority: `.design/forge/vacuity-triage.md` -/// REQ-1 — identity is `Eq`/`Le`/`Ge` only; "`<`/`>`/`!=` are NOT identities +/// REQ-1 — identity is `Eq`/`Le`/`Ge` only; "`<`/`>`/`!=` are not identities /// (`x < x` is false)". With a unit return (so (b) is exempt) and a non-implied /// `req`, these pass triage and reach verus. #[test] diff --git a/forge/tests/divergence_vec_push_frame.rs b/forge/tests/divergence_vec_push_frame.rs index f820f73fa..2ca63bfda 100644 --- a/forge/tests/divergence_vec_push_frame.rs +++ b/forge/tests/divergence_vec_push_frame.rs @@ -70,7 +70,7 @@ fn run_verus(file: &Path) -> Option<(bool, String)> { Some((out.status.success(), combined)) } -/// A `Vec` program that pushes two values, lowered by the real toolchain. The +/// A `Vec` program that pushes two values, lowered by the toolchain. The /// emitted `TVecU64` wrapper carries whatever `push` ens the lowerer produces. const VEC_TWO_PUSH: &str = r#" fn two(x: u64, y: u64) -> u64 diff --git a/forge/tests/e2e_conformance.rs b/forge/tests/e2e_conformance.rs index 2eec904e7..310eff01b 100644 --- a/forge/tests/e2e_conformance.rs +++ b/forge/tests/e2e_conformance.rs @@ -198,7 +198,7 @@ fn to_boundary_cases_classify_via_the_crossing() { Value::from(via), "`{item}` (case {name}) records the oracle crossing `via`" ); - // PROJECT claim (REQ-4): a file with any to-boundary fn is TO-THE-BOUNDARY. + // project claim (REQ-4): a file with any to-boundary fn is TO-the-BOUNDARY. assert!( !project_is_end_to_end(&certs), "case `{name}` has a to-boundary fn → project TO-THE-BOUNDARY" diff --git a/forge/tests/editor_runs.rs b/forge/tests/editor_runs.rs index aa9daccce..f32584d36 100644 --- a/forge/tests/editor_runs.rs +++ b/forge/tests/editor_runs.rs @@ -2,7 +2,7 @@ //! max-verified interactive multi-line editor that runs. This integration test //! grounds `examples/editor/editor.th` end-to-end against the external truths the //! toolchain does not author for itself — the real `verus` SMT prover (the cert -//! levels) and the real `rustc` compiler + a real process run (the build + the +//! levels) and the real `rustc` compiler + a process run (the build + the //! piped-keystroke session). //! //! The #125 multi-line extension: on top of the shipped edit core, the editor adds @@ -59,7 +59,7 @@ //! the total corpus). //! //! Driving the built `forge` binary (not a library API) keeps `forge` a pure `bin` -//! crate and exercises the real CLI surface. The cert-level checks run verus; if +//! crate and exercises the CLI surface. The cert-level checks run verus; if //! verus is absent they skip with a logged reason (the `check_conformance.rs` //! precedent) — never panic on a missing solver. `tests/` is not anti-pattern-gated, //! so `unwrap`/`expect`/`panic!` are fine here (R-APG-2). Expected levels trace to @@ -179,12 +179,12 @@ fn write_fixture(name: &str, body: &str) -> PathBuf { path } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -410,7 +410,7 @@ fn editor_content_pins_present_in_source() { // The editor's three content-bearing edit ops carry the exact `bytes_eq` pin // shapes the manifest / 07-strings REQ-18 spell — over the field-access operand // (`&result.text`/`&b.text`, the editor's Buffer-wrapped String, #279). These - // are content teeth, not the length pins (which remain): the bytes are certified + // check content in addition to the existing length pins: the bytes are certified // byte-for-byte. A reviewer reading the source must see the windows verbatim, so // a silent regression to a length-only pin (the O-5 cheat) is caught here. let src = std::fs::read_to_string(editor_th()).expect("read editor.th"); @@ -464,7 +464,7 @@ fn editor_content_pinned_ops_still_certify_l3() { eprintln!("SKIP: verus not available — editor content-pin cert-oracle not run."); return; } - // The content pins discharge at real verus (the #277 slice/concat byte-content + // The content pins discharge at verus (the #277 slice/concat byte-content // ens + the #278 `lemma_bytes_eq_bridge` + the #279 field-access operand view all // landed): each content-pinned op still certifies L3 — the windows are proven, // not asserted (R-DEFER-9). The whole editor exits 0 (O-1). Expected = L3 from @@ -483,7 +483,7 @@ fn editor_content_pinned_ops_still_certify_l3() { "the `bytes_eq`-content-pinned `{op}` must certify L3 — the byte windows \ discharge via the prove-once bridge (AC-14/AC-15):\n{cert:#?}" ); - // O-2/O-3: no new survivor. The content pins add content teeth on top of the + // O-2/O-3: no new survivor. The content pins add byte-level checks to the // length pins; the §7 scored ratio is unperturbed and carries no survivor. let survivor = cert["contract_quality"]["survivor"].as_str().unwrap_or(""); assert!( diff --git a/forge/tests/effect_link_conformance.rs b/forge/tests/effect_link_conformance.rs index 078f5768b..66b3d8afe 100644 --- a/forge/tests/effect_link_conformance.rs +++ b/forge/tests/effect_link_conformance.rs @@ -1,5 +1,5 @@ //! Conformance test for the runnable effect link (Basis Stage 8, issue **#81**) -//! against the external truths: the real `rustc` compiler, the real Linux seccomp +//! against the external truths: the real `rustc` compiler, the Linux seccomp //! kernel, the real `verus` prover, and the hand-derived oracle //! `conformance/effect-link/cases.json` (`.design/basis/08-runnable-effect-link.md`). //! @@ -60,7 +60,7 @@ fn seccomp_kill_available() -> bool { } /// `true` iff a `verus` binary is resolvable (mirrors `effect_stdlib_conformance.rs`). -/// The `verify_unchanged` case is a real verus proof of the L3 compose-through. +/// The `verify_unchanged` case is a verus proof of the L3 compose-through. fn verus_present() -> bool { if let Ok(p) = std::env::var("VERUS_BIN") { if Path::new(&p).exists() { @@ -429,7 +429,7 @@ fn read_byte_links_and_runs_both_arms() { out_a.contains("130"), "AC-2: stdin `A` (byte 65) → doubled() = 130 (65+65, hand-derived):\n{out_a}" ); - // Run with EMPTY stdin (EOF) → doubled() = 0 (the handled EOF/256 arm). + // Run with empty stdin (EOF) → doubled() = 0 (the handled EOF/256 arm). let out_eof = run_with_stdin(&artifact, b""); assert!( out_eof.contains("doubled() = 0"), diff --git a/forge/tests/effect_stdlib_conformance.rs b/forge/tests/effect_stdlib_conformance.rs index 59f954053..688826280 100644 --- a/forge/tests/effect_stdlib_conformance.rs +++ b/forge/tests/effect_stdlib_conformance.rs @@ -56,7 +56,7 @@ fn cases() -> Value { } /// `true` iff verus can be located — mirrors `composition_conformance.rs`. The -/// compose-through L3 is a real verus proof, so the prover must be present; the +/// compose-through L3 is a verus proof, so the prover must be present; the /// boundary L1 cert resolves the verus version up front for the proof cache. fn verus_present() -> bool { if let Ok(p) = std::env::var("VERUS_BIN") { @@ -630,7 +630,7 @@ fn sandbox_derives_fx_time_allowlist_and_kills_off_allowlist() { // The kill case: a pure filter (fx pure, 23 syscalls) denies the openat probe → // SIGSYS, exit 159. This is the shipped #57 `pure_probe_killed` precedent, run - // here to confirm the per-effect confinement teeth (a syscall outside the declared + // here to confirm per-effect confinement (a syscall outside the declared // fx is killed at the boundary). let sum = conformance_dir().join("sum.th"); let (ok2, stdout2, stderr2) = run_forge_build(&[ diff --git a/forge/tests/ergonomics_conformance.rs b/forge/tests/ergonomics_conformance.rs index bf579f4d0..b5e39fcb0 100644 --- a/forge/tests/ergonomics_conformance.rs +++ b/forge/tests/ergonomics_conformance.rs @@ -6,7 +6,7 @@ //! adds a proof rule, weakens an obligation, or launders a verification path //! (R-DEFER-9). These run against the two external truths the toolchain does not //! author for itself: the built `forge` binary's certificate ladder (`forge -//! check`, real verus) and the `thermite_spec::validate` exhaustiveness checker. +//! check`, verus) and the `thermite_spec::validate` exhaustiveness checker. //! //! Pins the C10 deliverables (`.design/basis/11-ergonomics.md`): //! diff --git a/forge/tests/exec_tv_conformance.rs b/forge/tests/exec_tv_conformance.rs index ec3028f7b..e8d4d178a 100644 --- a/forge/tests/exec_tv_conformance.rs +++ b/forge/tests/exec_tv_conformance.rs @@ -1,6 +1,6 @@ //! Conformance for the exec-position (body) translation-validation phase //! (`.design/verified/exec-tv.md` REQ-5 / REQ-3; epic crosslink #151, blockers -//! #154 / #156). Two load-bearing properties, both through the real `verus` binary +//! #154 / #156). Two required properties, both through the real `verus` binary //! (skip with a diagnostic if absent, mirroring `contract_tv_conformance.rs`): //! //! The generated run (primary — the off-corpus #122/#146 regression guard): @@ -8,7 +8,7 @@ //! exec expr via `thermite_lower::lower_exec_expr` and discharges the exec-fn //! obligation `result == `. The faithful lowerer + the //! adequate carried frames make every checked expr `faithful` (0 divergent, 0 -//! unverifiable, 0 skipped). A `divergent` is a real off-corpus exec-lowering +//! unverifiable, 0 skipped). A `divergent` is a off-corpus exec-lowering //! infidelity (the point — file it `-l blocker`). The construct coverage //! (cast-`<` / arith / cast / index) is asserted non-vacuous (the #122/#146 classes //! are exercised in real numbers). @@ -90,7 +90,7 @@ fn run_exec_tv_json(file: &Path, generated: Option) -> Value { /// REQ-3 / AC-7: the 200-expr off-corpus generated exec run. The faithful lowerer + /// the adequate carried frames make every checked expr `faithful` (0 divergent, 0 -/// unverifiable, 0 skipped). A `divergent` here is a real off-corpus exec-lowering +/// unverifiable, 0 skipped). A `divergent` here is a off-corpus exec-lowering /// infidelity finding (the point — surfaced with a diagnostic). Also asserts the run is /// substantive and the #122/#146 construct classes are exercised (else the guard is /// vacuous). diff --git a/forge/tests/g1_gate.rs b/forge/tests/g1_gate.rs index 0a6cba350..c977ee95f 100644 --- a/forge/tests/g1_gate.rs +++ b/forge/tests/g1_gate.rs @@ -1,8 +1,8 @@ //! The G1 gate cert-oracle (`.design/stage1-forge-tier.md` REQ-10 / AC-14): the //! `isqrt_class` example, end to end. `forge check --engine forge -//! conformance/forge/isqrt_class.th` drives the PER-CLAUSE hybrid route — two NON-TRIVIAL +//! conformance/forge/isqrt_class.th` drives the per-clause hybrid route — two NON-TRIVIAL //! relaxable consequences of the integer-sqrt characterization (`r*r <= n < (r+1)^2`) -//! discharged by the nlsat relax route at L4 (the real-arithmetic squeeze, NOT a restatement +//! discharged by the nlsat relax route at L4 (the real-arithmetic squeeze, not a restatement //! of `req`), one non-relaxable (`%`) clause discharged at L3 by the author's `proof for` //! block — so the certificate exhibits clauses at **L4, L4, L3**, the item level is the MIN //! (**L3**), and all four forge-tier evidence blocks are populated: @@ -13,7 +13,7 @@ //! * `burn` — the L3 clause's proof tokens + cited frozen simp-lemmas. //! //! The route invokes z3 (nlsat, bundled with verus) and lake (the built Lean spine), so the -//! test SKIPS when either is absent (mirroring the sibling live-spine tests); the CI lean +//! test skips when either is absent (mirroring the sibling live-spine tests); the CI lean //! job is the authoritative gate. The deterministic oracle fields are pinned against the //! committed golden `conformance/forge/isqrt_class.cert.json` (R-CHAR-3). @@ -55,7 +55,7 @@ fn verus_present() -> bool { } /// `lake` on PATH / under `~/.elan` — the L3 clause + the re-elaboration mutation need it -/// (the built spine). Absent → the gate route cannot discharge, so the test SKIPS. +/// (the built spine). Absent → the gate route cannot discharge, so the test skips. fn lake_present() -> bool { if let Ok(home) = std::env::var("HOME") { if PathBuf::from(home).join(".elan/bin/lake").exists() { diff --git a/forge/tests/goal_repl.rs b/forge/tests/goal_repl.rs index 3588afbe1..7b2b0e0fb 100644 --- a/forge/tests/goal_repl.rs +++ b/forge/tests/goal_repl.rs @@ -10,7 +10,7 @@ //! not enforced), so its fidelity is asserted cross-verb (battery ratio == check //! ratio from the same binary), never literal-copied (R-CHAR-3). //! - `forge goal conformance/sum.th sum` renders the §5.1 goal state: the `given` -//! (`req`) + `want` (`ens`) source text and `ALL GOALS DISCHARGED` (AC-2). +//! (`req`) + `want` (`ens`) source text and `all goals discharged` (AC-2). //! - `forge edit` resolves a semantic address, splices the replacement clause at //! its span, re-emits, and re-checks (REQ-3 / AC-4). //! - a bad address is a structured error, never a panic (REQ-7 / AC-4). @@ -127,7 +127,7 @@ fn battery_view_matches_check_verdicts() { } // REQ-2 / AC-2: `forge goal sum` renders the §5.1 goal state — given (`req`) + -// want (`ens`) source text and ALL GOALS DISCHARGED for the clean corpus item. +// want (`ens`) source text and all goals discharged for the clean corpus item. #[test] fn goal_render_discharged_for_sum() { if !verus_present() { diff --git a/forge/tests/goal_repl_fill.rs b/forge/tests/goal_repl_fill.rs index 47c06c31b..c2fab2315 100644 --- a/forge/tests/goal_repl_fill.rs +++ b/forge/tests/goal_repl_fill.rs @@ -1,7 +1,7 @@ //! `forge/tests/goal_repl_fill.rs` — conformance for the goal-state REPL increment //! (iii): the `?N` body-hole token + the open-hole validator + `forge fill` + the //! §5.1 dialogue golden (#193; `.design/forge/goal-repl.md` REQ-4/REQ-5/REQ-6 + -//! AC-5/AC-6). Drives the real toolchain: +//! AC-5/AC-6). Drives the toolchain: //! //! - `thermite_syntax` parses a `body = ?0` fn to a clean holed AST carrying its //! open holes; a `?N` in a `spec fn` body / expression / clause position is a @@ -290,7 +290,7 @@ fn fill_introducing_new_holes_re_presents_them() { let _ = std::fs::remove_file(&th); } -// `forge fill` on a non-hole address is an honest Usage error directing to `edit` +// `forge fill` on a non-hole address is an Usage error directing to `edit` // (the two verbs have distinct contracts — REQ-3 vs REQ-6), never a silent splice. #[test] fn fill_on_a_non_hole_address_is_an_honest_error() { @@ -309,8 +309,8 @@ fn fill_on_a_non_hole_address_is_an_honest_error() { } // REQ-6 (verus-gated): filling the hole with correct code closes it and the item -// certifies L3 (the §5.1 "ALL GOALS DISCHARGED" terminal). Skips with a logged -// note without verus (the discharge claim is a real proof). +// certifies L3 (the §5.1 "all goals discharged" terminal). Skips with a logged +// note without verus (the discharge claim is a proof). #[test] fn fill_closing_the_hole_certifies_l3() { if !verus_present() { @@ -339,11 +339,11 @@ fn fill_closing_the_hole_certifies_l3() { // AC-6: the §5.1 binary_search dialogue as the end-to-end acceptance oracle. // --------------------------------------------------------------------------- -// The full §5.1 loop, driven turn-by-turn against the real toolchain. Asserts the +// The full §5.1 loop, driven turn-by-turn against the toolchain. Asserts the // structural oracle from `conformance/goal/binary_search.dialogue.json` (given/want/ // holes/discharged-vs-open/counterexample-presence), never the illustrative // timings/mutant-counts (R-CHAR-3; the golden README pins the split). Verus-gated: -// the discharge/counterexample turns are real proofs. +// the discharge/counterexample turns are proofs. #[test] fn ac6_binary_search_dialogue_structural_oracle() { if !verus_present() { @@ -438,7 +438,7 @@ loop \ // Turn 4 — guard the branch (rewrite the body via `edit` on the loop) so the // invariant is preserved; the item certifies L3 with a non-vacuous battery line - // (the §5.1 terminal `ALL GOALS DISCHARGED ✓ binary_search certified L3`). We + // (the §5.1 terminal `all goals discharged ✓ binary_search certified L3`). We // rewrite to the corpus's guarded branch by re-creating the corpus body. let corpus = std::fs::read_to_string(repo_root().join("conformance/binary_search.th")) .expect("read binary_search.th"); diff --git a/forge/tests/lean_while.rs b/forge/tests/lean_while.rs index ce3d0b57a..63bf0f6d6 100644 --- a/forge/tests/lean_while.rs +++ b/forge/tests/lean_while.rs @@ -1,4 +1,4 @@ -//! `forge/tests/lean_while.rs` — the integration-level oracle suite for the WHILE-BODY +//! `forge/tests/lean_while.rs` — the integration-level oracle suite for the WHILE-body //! Lean exporter (`.design/verified/proof-backends.md` REQ-11 / §4.2, increment (v-b), //! blocker #264). The v1 `while` shape exports the five per-item obligations + the two //! generator-proved composed theorems (`while_compose` / `loopDenote_exits_of_dec`); a @@ -20,7 +20,7 @@ //! - **O-2** the full refusal matrix — every out-of-v1 shape does not certify L3-via-lean //! (it is the `Unknown`/degrade skip, never a false verdict). //! - **O-3** the while-true vacuity fixture is never `Proven` L3-via-lean (the §4.2.3 -//! termination-vacuity gate — the conjoined `_converges` obligation has teeth). +//! termination-vacuity gate, where the conjoined `_converges` obligation is required). //! - **O-4** an in-grammar while-body mutant is attempted (REQ-11.7) — the certificate's //! mutation report says "against lean" with a non-zero attempted denominator, not the //! `UntestedAgainstLean`/`0/0` backstop that every while mutant hit pre-(v-b). @@ -167,7 +167,7 @@ fn count_certifies_l3_via_lean_auto() { } // ════════════════════════════════════════════════════════════════════════════════ -// O-1 (honest) — `conformance/sum.th` does not certify L3-via-lean (the §4 residual). +// O-1 — `conformance/sum.th` does not certify L3-via-lean (the §4 residual). // ════════════════════════════════════════════════════════════════════════════════ #[test] @@ -181,7 +181,7 @@ fn sum_does_not_certify_l3_via_lean_recursive_residual() { // sum.th's `ens result == spec_sum(xs)` is a recursive-registry contract clause (the // recursive `spec_sum` spec-fn) — the §4 stabilized form is the interactive residual, // so the auto Lean path refuses it (the contract-tier gate), and it does not certify - // L3 via the Lean engine. Expected from §4.2.1 / REQ-7 (R-CHAR-3): the honest landing + // L3 via the Lean engine. Expected from §4.2.1 / REQ-7 (R-CHAR-3): the landing // is not-L3-via-lean (it degrades / falls to Verus). Never a false L3-via-lean. assert!( !(level_of(&cert) == "L3" && certified_via_lean(&cert)), @@ -201,7 +201,7 @@ fn refusal_matrix_no_lean_certification() { return; } // Each shape is out of the §4.2.1 v1 grammar (the §4.2.5 inventory). Under `--engine - // lean` each is the honest `Unknown`/degrade skip — never an L3-via-lean verdict. The + // lean` each is the `Unknown`/degrade skip — never an L3-via-lean verdict. The // out class is hand-derived from §4.2.5 (R-CHAR-3). Boundary cases pinned in // `engine.rs::while_refusal_inventory_is_structured` (the structured `ExportRefusal` // variant); here we assert the CERTIFICATE-level consequence (no Lean L3). diff --git a/forge/tests/literal_layer.rs b/forge/tests/literal_layer.rs index f93f4c550..c3c254cc1 100644 --- a/forge/tests/literal_layer.rs +++ b/forge/tests/literal_layer.rs @@ -159,7 +159,7 @@ fn escape_nul_certifies_l3_byte_0() { ); } -/// Non-vacuity (R-DEFER-9): the escape byte is load-bearing. A contract claiming +/// Non-vacuity (R-DEFER-9): the escape byte is required. A contract claiming /// the wrong control code (`\x1b` == 99 instead of 27) does not certify L3. The /// proof requires the literal to decode to the right byte; a wrong claim leaves the /// `ens` undischarged → not L3. diff --git a/forge/tests/map_conformance.rs b/forge/tests/map_conformance.rs index 3de9749d6..15358a460 100644 --- a/forge/tests/map_conformance.rs +++ b/forge/tests/map_conformance.rs @@ -14,7 +14,7 @@ //! * The emitted `TMapU64U64` wrapper (Vec-of-pairs backing + spec view + the //! ops) + the insert-then-get round-trip (`insert(k,v)` then `get(k) == //! Some(v)`) + the absent→None refusal (`get(absent) == None`) + `contains_key` -//! true/false verify under real verus `verified, 0 errors` (AC-1/AC-2/AC-3). +//! true/false verify under verus `verified, 0 errors` (AC-1/AC-2/AC-3). //! * Non-vacuity (R-DEFER-9): a crafted `get` returning `Some(0)` for an absent //! key fails verus (the `None => !spec_contains_key(k)` arm bites) (AC-2). //! * The `map_kv.th` corpus program parses (the two-arg `Map`), @@ -164,12 +164,12 @@ fn cert_for<'a>(certs: &'a [Value], item: &str) -> &'a Value { .unwrap_or_else(|| panic!("no cert for `{item}` in {certs:?}")) } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -295,7 +295,7 @@ fn main() {} /// AC-1 / AC-2 / AC-3 (grounded): the `TMapU64U64` wrapper + the insert-then-get /// round-trip (`insert(k,v)` then `get(k) == Some(v)`) + the absent→None refusal -/// (`get(absent) == None`) + `contains_key` both branches verify under real verus +/// (`get(absent) == None`) + `contains_key` both branches verify under verus /// `verified, 0 errors`. /// /// Authority: `.design/basis/13-map.md` AC-1/AC-2/AC-3 — the grounded `TMapU64U64` @@ -319,7 +319,7 @@ fn ac1_2_3_map_wrapper_roundtrip_and_absent_none_verify_l3() { } /// AC-2 non-vacuity (R-DEFER-9): a crafted `get` returning `Some(0)` for an absent -/// key fails verus. The `None => !spec_contains_key(k)` arm has real teeth: a body +/// key fails verus. The `None => !spec_contains_key(k)` arm rejects a body /// returning `Some(0)` for a key not in the map does not satisfy the Some arm's /// `spec_contains_key(k)`, so the postcondition is undischarged. /// @@ -351,7 +351,7 @@ fn ac2_broken_get_some_for_absent_fails_real_verus() { } /// AC-1: the `map_kv.th` corpus program parses (the two-arg `Map`) and -/// its emitted lowering verifies under real verus `verified, 0 errors` (the `TMap` +/// its emitted lowering verifies under verus `verified, 0 errors` (the `TMap` /// wrapper is woven + the spec-position `contains_key`/`len` rewrites + the /// `Map::new()` reachability all compose). /// @@ -383,12 +383,12 @@ fn ac1_map_kv_corpus_lowering_verifies_under_real_verus() { /// killed). This is the L3 cert anchor for the contains_key cage admission. /// /// `build_one` (`ens result.contains_key(k)`) and `lookup_absent` -/// (`ens result is None`) verify under real verus (the `ac1_..._lowering` test: +/// (`ens result is None`) verify under verus (the `ac1_..._lowering` test: /// the round-trip membership + the absent→None refusal), but their thin partial /// contracts do not meet the §7 anti-Goodhart mutation floor (a `Map`-returning /// fn has no scoreable scalar-zero mutant; a `None`-returning partial contract is /// satisfied by an always-`None` body — the #101 partial-`None` class). The -/// round-trip / absent→None teeth are pinned at the verus codegen-grounding level +/// round-trip and absent→None cases are pinned at the verus codegen-grounding level /// (`ac1_2_3` + `ac2_broken_..`), R-HONEST-3: the L3 forge cert is anchored on the /// mutation-strong accessor rather than a thin contract. /// @@ -417,10 +417,10 @@ fn ac3_map_kv_contains_key_accessor_certifies_l3() { /// local `Map`, `insert(7, 42)`, `get(7)`, and returns `42` (the L1 `TMap` /// runtime — `emit_map_runtime_l1`'s plain-Rust Vec-of-pairs newtype with the /// `thermite_check!` capacity/uniqueness guards + `get -> Option`). The build -/// uses real `rustc` + a real process run; `rustc` is always present, so it does not skip. +/// uses real `rustc` + a process run; `rustc` is always present, so it does not skip. /// /// Authority: `.design/basis/13-map.md` AC-1 ("`forge build` a Map program → -/// COMPILES + RUNS (insert + get → the value)"). +/// compiles + runs (insert + get → the value)"). #[test] fn ac1_map_kv_builds_and_runs_insert_get_yields_value() { if !linux_build_run_supported("ac1_map_kv_builds_and_runs_insert_get_yields_value") { diff --git a/forge/tests/merge_example.rs b/forge/tests/merge_example.rs index 7608eb260..867ce6a8c 100644 --- a/forge/tests/merge_example.rs +++ b/forge/tests/merge_example.rs @@ -1,7 +1,7 @@ //! AC-11 (`.design/stage1-forge-tier.md` REQ-7, increment 2e) — the merge example, //! end to end: `forge goal --proof` renders a forge-routed goal with its hypotheses in //! scope; `forge fill ?p0 ""` closes the goal; the resulting certificate -//! (the Lean discharge of the forge-tier `lemma`) carries the BURN RECEIPT (the committed +//! (the Lean discharge of the forge-tier `lemma`) carries the burn receipt (the committed //! proof's token count + cited lemmas). //! //! The discharge legs (`fill`'s re-check, `check --engine lean`) invoke lake against the @@ -92,7 +92,7 @@ fn proof_view_renders_the_merge_goal_with_hypotheses() { } // AC-11 (live spine): `forge fill ?p0 ""` closes the merge goal end to -// end, and the re-check certifies L3 + surfaces the BURN RECEIPT (token count + cited +// end, and the re-check certifies L3 + surfaces the burn receipt (token count + cited // lemmas). SKIPs when lake is absent (the discharge needs the built spine). #[test] fn fill_closes_the_merge_goal_and_the_cert_carries_the_burn_receipt() { diff --git a/forge/tests/metrics_dashboard.rs b/forge/tests/metrics_dashboard.rs index 3ce6e49b1..220a092e5 100644 --- a/forge/tests/metrics_dashboard.rs +++ b/forge/tests/metrics_dashboard.rs @@ -1,9 +1,9 @@ //! AC-12 (umbrella `.design/thermite2-program.md` REQ-7): the §6 metrics dashboard. //! Drives the built `forge` binary with `audit --metrics` and asserts that forge emits -//! the routing-reason + verdict + TV-phase telemetry and the audit PRINTS the dashboard -//! — and, critically, that the dashboard GATES NOTHING (#274, +//! the routing-reason + verdict + TV-phase telemetry and the audit prints the dashboard +//! — and, critically, that the dashboard gates nothing (#274, //! `.design/forge/audit-manifest.md` REQ-10): the exit code is byte-for-byte identical -//! with and without `--metrics`, on a certifying project AND on a failing one. +//! with and without `--metrics`, on a certifying project and on a failing one. //! //! Three telemetry kinds, exercised across two fixtures: //! - the v1 Verus corpus (`conformance/sum.th`) drives the **TV phase split** (the @@ -54,7 +54,7 @@ fn verus_present() -> bool { } /// `lake` on PATH / under `~/.elan` — the forge-tier L3 (lemma) clause needs the built -/// Lean spine. Absent → the gate route cannot discharge, so the forge-tier test SKIPS. +/// Lean spine. Absent → the gate route cannot discharge, so the forge-tier test skips. fn lake_present() -> bool { if let Ok(home) = std::env::var("HOME") { if PathBuf::from(home).join(".elan/bin/lake").exists() { @@ -90,8 +90,8 @@ fn write_temp_program(name: &str, program: &str) -> PathBuf { path } -// AC-12: `forge audit --metrics` over the v1 corpus PRINTS the §6 dashboard (the TV -// phase split + the in-cage routing share) and GATES NOTHING — the exit code is +// AC-12: `forge audit --metrics` over the v1 corpus prints the §6 dashboard (the TV +// phase split + the in-cage routing share) and gates nothing — the exit code is // identical to the bare audit, and the metrics are appended after the manifest. #[test] fn audit_metrics_prints_dashboard_and_gates_nothing() { @@ -107,7 +107,7 @@ fn audit_metrics_prints_dashboard_and_gates_nothing() { let (bare_code, bare_stdout, _) = run_audit(&file, &[]); let (metrics_code, metrics_stdout, _) = run_audit(&file, &["--metrics"]); - // GATES NOTHING: the exit code is byte-for-byte identical with and without --metrics. + // gates nothing: the exit code is byte-for-byte identical with and without --metrics. assert_eq!( bare_code, metrics_code, "`forge audit --metrics` must not change the exit code (audit gates nothing, #274)" @@ -136,7 +136,7 @@ fn audit_metrics_prints_dashboard_and_gates_nothing() { "the TV split must report the faithful baseline:\n{metrics_stdout}" ); - // The bare audit prints NO metrics section (the flag is opt-in). + // The bare audit prints no metrics section (the flag is opt-in). assert!( !bare_stdout.contains("§6 metrics dashboard"), "the bare `forge audit` must NOT print the metrics section:\n{bare_stdout}" @@ -178,7 +178,7 @@ fn audit_metrics_output_is_deterministic() { ); } -// AC-12: under `--json` the metrics go to stderr so stdout stays ONE valid v1 manifest +// AC-12: under `--json` the metrics go to stderr so stdout stays one valid v1 manifest // document (the dashboard never corrupts the oracle-asserted JSON surface). #[test] fn audit_metrics_json_keeps_stdout_a_single_manifest() { @@ -188,7 +188,7 @@ fn audit_metrics_json_keeps_stdout_a_single_manifest() { } let file = repo_root().join("conformance/sum.th"); let (_code, stdout, stderr) = run_audit(&file, &["--metrics", "--json"]); - // stdout is a single JSON manifest (the metrics did NOT leak into it). + // stdout is a single JSON manifest (the metrics did not leak into it). let manifest: Value = serde_json::from_str(stdout.trim()).unwrap_or_else(|e| { panic!("`forge audit --metrics --json` stdout must be one JSON doc: {e}\n{stdout}") }); @@ -232,7 +232,7 @@ fn run_check_forge_json(file: &Path) -> (Option, Vec) { } // AC-12 ("forge emits the routing-reason + verdict telemetry fields the §6 dashboard -// needs"): the forge-tier `isqrt_class` example, checked with `--engine forge`, EMITS the +// needs"): the forge-tier `isqrt_class` example, checked with `--engine forge`, emits the // per-clause telemetry the metrics dashboard projects into the cage-vs-forge share by // reason + the seven-verdict counts — 2 nlsat (relaxable → forge) + 1 lean (lemma → // forge) clauses, all `Proved`. (The dashboard's aggregation of these fields into the @@ -256,7 +256,7 @@ fn forge_engine_emits_routing_and_verdict_telemetry() { .expect("the cert carries per-clause obligations"); assert_eq!(obls.len(), 3, "three ens clauses, three obligations"); - // The ROUTING telemetry: the per-clause `engine` field (which the dashboard projects + // The routing telemetry: the per-clause `engine` field (which the dashboard projects // to in-cage / relaxable / lemma). Two nlsat (relaxable) + one lean (lemma). let engines: Vec<&str> = obls .iter() diff --git a/forge/tests/mutual_recursion_conformance.rs b/forge/tests/mutual_recursion_conformance.rs index a9be72b8c..75ded00c3 100644 --- a/forge/tests/mutual_recursion_conformance.rs +++ b/forge/tests/mutual_recursion_conformance.rs @@ -14,7 +14,7 @@ //! (renamed cause `MutualRecursionMissingDecreases`). //! //! These run against the external truth the toolchain does not author for itself: -//! the built `forge` binary's certificate ladder (`forge check`, real verus). +//! the built `forge` binary's certificate ladder (`forge check`, verus). //! R-CODE-4: the subprocess status is checked, never swallowed. //! //! Pins the C11 ACs (the grounded forms from the design's Verification section, @@ -190,7 +190,7 @@ const THREE_CYCLE_L3: &str = "fn step_a(n: u64) -> u64\n \ {\n if n == 0 { 0 } else { step_a(n - 1) }\n}\n"; // --------------------------------------------------------------------------- -// AC-1 (REQ-1/REQ-3): a dec-complete mutual pair certifies L3 (real verus). +// AC-1 (REQ-1/REQ-3): a dec-complete mutual pair certifies L3 (verus). // --------------------------------------------------------------------------- #[test] diff --git a/forge/tests/nlsat_relax_conformance.rs b/forge/tests/nlsat_relax_conformance.rs index 3ad847890..9b6a56d86 100644 --- a/forge/tests/nlsat_relax_conformance.rs +++ b/forge/tests/nlsat_relax_conformance.rs @@ -5,8 +5,8 @@ //! - the isqrt characterization certifies **L4** push-button with `engine: nlsat` //! attribution (the relaxable squeeze admitted, real-valid → kernel-grounded); //! - `∀ n. n*n ≠ 2` (true over ℤ, false over ℝ) yields a **RealWitness** carrying the -//! raw real point (√2), NEVER a `Counterexample`; -//! - a div-containing clause is rejected by the relaxable check (an honest skip). +//! raw real point (√2), never a `Counterexample`; +//! - a div-containing clause is rejected by the relaxable check (a skip). //! //! Live: gated on `z3` (the relax route issues a direct Z3 nlsat QF_NRA query). CI //! test-shards without z3 SKIP rather than fail, mirroring the sibling lake-gated live @@ -110,7 +110,7 @@ fn nlsat_isqrt_certifies_l4_pushbutton() { } // AC-12: a true-over-ℤ, false-over-ℝ claim (`∀ n. n*n ≠ 2`) yields a RealWitness -// carrying the real point, NEVER a Counterexample. +// carrying the real point, never a Counterexample. #[test] fn nlsat_n_squared_ne_two_is_real_witness_never_counterexample() { if !z3_present() { @@ -152,7 +152,7 @@ fn nlsat_n_squared_ne_two_is_real_witness_never_counterexample() { n_val.starts_with("1.41"), "the RealWitness carries the raw real point n ≈ √2 (got `{n_val}`); cert: {cert}" ); - // And it is NOT a counterexample reject (the headline: it escalates UP, not down). + // And it is not a counterexample reject (the headline: it escalates UP, not down). assert_ne!( cert.get("reject") .and_then(|r| r.get("cause")) @@ -162,7 +162,7 @@ fn nlsat_n_squared_ne_two_is_real_witness_never_counterexample() { ); } -// REQ-8b: a div-containing clause is rejected by the relaxable check (an honest skip, +// REQ-8b: a div-containing clause is rejected by the relaxable check (a skip, // non-certified — not relaxable, never a false verdict). No z3 needed. #[test] fn nlsat_div_clause_is_not_relaxable_skip() { diff --git a/forge/tests/operators_conformance.rs b/forge/tests/operators_conformance.rs index fbf799165..c6536b044 100644 --- a/forge/tests/operators_conformance.rs +++ b/forge/tests/operators_conformance.rs @@ -221,7 +221,7 @@ fn unary_not_certifies_l3_per_type() { // --------------------------------------------------------------------------- /// `%` without `req b != 0` is L0; the divide-by-zero obligation bites (ast.md -/// REQ-11; AC-6). The same teeth as the existing `/`. +/// REQ-11; AC-6). This matches the existing `/` check. #[test] fn rem_without_nonzero_req_is_l0() { if !verus_present() { @@ -303,7 +303,7 @@ fn char_hex_binary_literals_certify_exact_value_l3() { ); } -/// Non-vacuity (R-DEFER-9): the char byte is load-bearing. A contract claiming the +/// Non-vacuity (R-DEFER-9): the char byte is required. A contract claiming the /// wrong code (`'A'` == 66 instead of 65) does not certify L3 (lexer.md AC-8). #[test] fn char_literal_value_is_load_bearing_wrong_code_not_l3() { diff --git a/forge/tests/option_result_conformance.rs b/forge/tests/option_result_conformance.rs index 987e93f2e..a991d2519 100644 --- a/forge/tests/option_result_conformance.rs +++ b/forge/tests/option_result_conformance.rs @@ -3,7 +3,7 @@ //! payload-in-contract projection (the spec-`match`-in-`ens`) + the deferred //! `parse_u64` (the C4 `07-strings.md` REQ-9 payoff). These run against the two //! external truths the toolchain does not author for itself: the built `forge` -//! binary's certificate ladder (`forge check`, real verus) and — for the generated +//! binary's certificate ladder (`forge check`, verus) and — for the generated //! `parse_u64` whose round-trip cannot be a thin caller's mutation-scored cert — //! the `verus` binary on the emitted lowering of a `parse_u64`-calling //! program (R-CODE-4: the subprocess status is checked, never swallowed). @@ -276,7 +276,7 @@ fn ac4_parse_u64_lowering_verifies_under_real_verus() { } /// AC-4 non-vacuity (R-DEFER-9): a hand-broken `parse_u64` whose body returns -/// `Some(0)` unconditionally fails verus. The round-trip success `ens` has real teeth: +/// `Some(0)` unconditionally fails verus. The round-trip success `ens` rejects this body: /// a body that returns `Some(0)` for a non-"0" input does not satisfy `parse_be(s) == /// 0`, so the postcondition is undischarged. /// @@ -284,7 +284,7 @@ fn ac4_parse_u64_lowering_verifies_under_real_verus() { /// fails (`3 verified, 1 errors`). `thermite-design.md` §7: the battery catches a /// false claim. The break is injected into a standalone verus probe of the generated /// contract (the surface cannot mutate the generated fn body), confirming the round- -/// trip `ens` is a real proof, not vacuous. +/// trip `ens` is a proof, not vacuous. #[test] fn ac4_broken_parse_u64_body_fails_real_verus() { if !verus_present() { diff --git a/forge/tests/profile_conformance.rs b/forge/tests/profile_conformance.rs index 78152c75c..677f53a79 100644 --- a/forge/tests/profile_conformance.rs +++ b/forge/tests/profile_conformance.rs @@ -14,7 +14,7 @@ //! the default rlimit (L3, no profile; the cert-oracle is unperturbed); a //! broken contract is a counterexample (not a timeout, no profile); and a //! forced low `--rlimit` run is the timeout lever. The live forced-timeout is -//! best-effort: empirically (OQ-1, confirmed by running the real binary) Z3 +//! best-effort: empirically (OQ-1, confirmed by running the binary) Z3 //! frequently returns `unknown` fast on synthetic goals without exhausting the //! rlimit, so `--profile` does not always emit a report. When a profile is //! emitted the cert is a `VerusTimeout` with the profile + suggested_move diff --git a/forge/tests/proof_fill.rs b/forge/tests/proof_fill.rs index 708a4fa89..51f112eb8 100644 --- a/forge/tests/proof_fill.rs +++ b/forge/tests/proof_fill.rs @@ -89,7 +89,7 @@ fn fill_closes_a_proof_hole_and_commits_the_tactics() { let _ = std::fs::remove_file(&th); } -// AC-11 / REQ-5: a fill citing a tactic OUTSIDE the frozen battery is REFUSED on the +// AC-11 / REQ-5: a fill citing a tactic outside the frozen battery is REFUSED on the // re-check, named — never silently accepted (the 2c battery is the elaboration gate). #[test] fn fill_with_an_unlisted_tactic_is_refused_by_the_battery() { @@ -107,7 +107,7 @@ fn fill_with_an_unlisted_tactic_is_refused_by_the_battery() { let _ = std::fs::remove_file(&th); } -// The §5.1 fill loop: a fill whose tactics introduce a NEW `?pN` hole re-presents the +// The §5.1 fill loop: a fill whose tactics introduce a new `?pN` hole re-presents the // new open hole (and the item stays non-certified until it is closed). #[test] fn fill_introducing_a_new_hole_re_presents_it() { diff --git a/forge/tests/proof_hole_gate.rs b/forge/tests/proof_hole_gate.rs index 3b6b92300..2f623ff13 100644 --- a/forge/tests/proof_hole_gate.rs +++ b/forge/tests/proof_hole_gate.rs @@ -1,11 +1,11 @@ -//! AC-7 (`.design/stage1-forge-tier.md` REQ-3): an OPEN `?pN` proof hole blocks +//! AC-7 (`.design/stage1-forge-tier.md` REQ-3): an open `?pN` proof hole blocks //! certification and build through the shared `goal_repl::open_proof_hole_reason` //! path — the proof-tier mirror of the `?N` body-hole short-circuit (#193). A //! forge-tier item carrying an open proof hole is incomplete: it must never //! certify and never ship a build artifact. //! -//! These assertions need NO verus: the open-hole short-circuit precedes lowering / -//! verus (exactly like the body-hole `OpenHole` gate in `goal_repl_fill.rs`). +//! These assertions need no verus: the open-hole short-circuit precedes lowering / +//! verus (like the body-hole `OpenHole` gate in `goal_repl_fill.rs`). use std::path::PathBuf; use std::process::Command; @@ -79,7 +79,7 @@ fn open_proof_hole_blocks_build() { let _ = std::fs::remove_file(&th); } -// A hole-FREE lemma is NOT rejected for the open-hole reason — the gate fires only +// A hole-free lemma is not rejected for the open-hole reason — the gate fires only // on an actual open proof hole (no false positives). #[test] fn hole_free_lemma_is_not_open_hole_rejected() { diff --git a/forge/tests/provenance_conformance.rs b/forge/tests/provenance_conformance.rs index 132b3efcc..cb37f99b3 100644 --- a/forge/tests/provenance_conformance.rs +++ b/forge/tests/provenance_conformance.rs @@ -27,7 +27,7 @@ //! exercised by this oracle (the v1 type-level slice rejects a direct //! `query(input)` by the sink's parameter type alone — grounded). //! -//! These run a real verus proof (the doored callers L3-prove against the assumed +//! These run a verus proof (the doored callers L3-prove against the assumed //! door contracts), so they skip with a logged note if verus is absent — never //! panic on a missing solver, mirroring `composition_conformance.rs` / //! `audit_conformance.rs`. @@ -180,7 +180,7 @@ fn assert_careless_rejected(certs: &[Value], item: &str) { /// Assert a safe / doored path CERTIFIES `L3` to-the-boundary via the named sink: /// the marked value is laundered to the clean type through the door, the sink -/// accepts it, and the caller proves THROUGH the door+sink contracts. +/// accepts it, and the caller proves through the door+sink contracts. fn assert_safe_certifies(certs: &[Value], item: &str, expect_via: &str) { let cert = find_cert(certs, item); assert_eq!( @@ -189,7 +189,7 @@ fn assert_safe_certifies(certs: &[Value], item: &str, expect_via: &str) { "`{item}` routes the marked value through its door → type-checks → proves \ L3 (the default floor; the equality contract is mutation-killable)" ); - // #17 scope ⊥ level: L3 AND to_boundary via the reached sink/door. + // #17 scope ⊥ level: L3 and to_boundary via the reached sink/door. assert_eq!( cert["assurance_scope"]["kind"], Value::from("to_boundary"), diff --git a/forge/tests/recursion_conformance.rs b/forge/tests/recursion_conformance.rs index d72dd515e..0f78ad8ac 100644 --- a/forge/tests/recursion_conformance.rs +++ b/forge/tests/recursion_conformance.rs @@ -4,8 +4,8 @@ //! (`.design/basis/10-recursion-tuples.md` REQ-1..4). //! //! These run against the external truths the toolchain does not author for itself: -//! the built `forge` binary's certificate ladder (`forge check`, real verus) and -//! the L1 build+run path (`forge build`, real rustc) — R-CODE-4: the subprocess +//! the built `forge` binary's certificate ladder (`forge check`, verus) and +//! the L1 build+run path (`forge build`, rustc) — R-CODE-4: the subprocess //! status is checked, never swallowed. //! //! Pins the C9-A deliverables (the grounded forms from the design's Verification @@ -110,12 +110,12 @@ fn level(certs: &[Value], item: &str) -> String { .to_string() } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -153,7 +153,7 @@ const SPIN_DIVERGE: &str = "fn spin(n: u64) -> u64\n \ {\n if n == 0 {\n 0\n } else {\n spin(n - 1)\n }\n}\n"; // --------------------------------------------------------------------------- -// REQ-1/REQ-3, AC-1: a recursive fn with `dec` certifies L3 (real verus). +// REQ-1/REQ-3, AC-1: a recursive fn with `dec` certifies L3 (verus). // --------------------------------------------------------------------------- #[test] diff --git a/forge/tests/req_bounded_mul_conformance.rs b/forge/tests/req_bounded_mul_conformance.rs index 127a7e2cd..fb8a67a9f 100644 --- a/forge/tests/req_bounded_mul_conformance.rs +++ b/forge/tests/req_bounded_mul_conformance.rs @@ -79,8 +79,8 @@ fn level_of<'a>(certs: &'a [Value], item: &str) -> &'a str { } /// The user's exact `/goal` case: `sq(n) req n <= 30 ens result == n * n` -/// certifies L3 — the var*var overflow discharge bites (#196). WITHOUT the -/// aid this fails "possible arithmetic underflow/overflow"; WITH it, verus +/// certifies L3 — the var*var overflow discharge bites (#196). without the +/// aid this fails "possible arithmetic underflow/overflow"; with it, verus /// proves the bound via `nonlinear_arith` and the body's `n * n` overflow /// obligation discharges. #[test] diff --git a/forge/tests/restratify.rs b/forge/tests/restratify.rs index 891965d42..ba14287fa 100644 --- a/forge/tests/restratify.rs +++ b/forge/tests/restratify.rs @@ -1,7 +1,7 @@ //! `forge edit --restratify` — the restratification rewrite, end to end //! (`.design/stage2-stratified-cage.md` REQ-7 / AC-7). Drives the built `forge` binary -//! and asserts the §6 kv-alternation example is rewritten and certified IN-CAGE, plus the -//! R-SIDE-1 withheld-certification discipline (certification is WITHHELD when `Side` is +//! and asserts the §6 kv-alternation example is rewritten and certified in-cage, plus the +//! R-side-1 withheld-certification discipline (certification is WITHHELD when `Side` is //! undischarged). The certification logic itself is the pure-Rust //! `thermite_spec::restratify` (unit-tested in-crate + the Lean `restrat_conservative` / //! `PinRestratDropSide`); this test pins the CLI WIRING. @@ -15,8 +15,8 @@ fn forge_bin() -> &'static str { env!("CARGO_BIN_EXE_forge") } -/// AC-7 — the end-to-end rewrite: original REJECTED (cycle), φ' and `Side` both ADMITTED, -/// `Side` discharged in-cage, φ CERTIFIED. The JSON report attests each step. +/// AC-7 — the end-to-end rewrite: original rejected (cycle), φ' and `Side` both admitted, +/// `Side` discharged in-cage, φ certified. The JSON report attests each step. #[test] fn restratify_certifies_kv_example_end_to_end() { let out = Command::new(forge_bin()) @@ -35,7 +35,7 @@ fn restratify_certifies_kv_example_end_to_end() { assert_eq!(doc["side"]["verdict"], "admitted"); assert_eq!(doc["side_discharged"], Value::Bool(true)); assert_eq!(doc["certified"], Value::Bool(true)); - // R-SIDE-1 is attested by the report: undischarged Side ⇒ withheld. + // R-side-1 is attested by the report: undischarged Side ⇒ withheld. assert_eq!(doc["withheld_when_side_undischarged"], Value::Bool(true)); } @@ -63,7 +63,7 @@ fn restratify_rejects_positional_args() { assert_eq!(out.status.code(), Some(2), "usage error is exit 2"); } -/// AC-7 — the WITHHELD-CERTIFICATION discipline (R-SIDE-1), at the certification API: φ' +/// AC-7 — the WITHHELD-CERTIFICATION discipline (R-side-1), at the certification API: φ' /// is admitted, but with `Side` UNDISCHARGED the φ-certificate is WITHHELD. This is the /// mis-certification that dropping `Side` would permit — the Lean `PinRestratDropSide` /// mirror. @@ -74,7 +74,7 @@ fn certification_withheld_when_side_undischarged() { // Side discharged in-cage ⇒ certified. assert!(certify(&phi, true).is_certified()); - // Side undischarged ⇒ WITHHELD, with the R-SIDE-1 reason. + // Side undischarged ⇒ WITHHELD, with the R-side-1 reason. let withheld = certify(&phi, false); assert!(!withheld.is_certified()); assert!(matches!( diff --git a/forge/tests/sandbox_conformance.rs b/forge/tests/sandbox_conformance.rs index 56921dbf4..7dd80f411 100644 --- a/forge/tests/sandbox_conformance.rs +++ b/forge/tests/sandbox_conformance.rs @@ -1,5 +1,5 @@ //! Conformance test for `forge build`'s runtime effect sandbox (issue #57) against -//! the external truth: the real `rustc` compiler, the real Linux seccomp kernel, +//! the external truth: the real `rustc` compiler, the Linux seccomp kernel, //! and the hand-derived oracle `conformance/sandbox/cases.json` //! (`.design/forge/runtime-sandbox.md`). //! @@ -11,7 +11,7 @@ //! probe after the filter): denied under a `pure` filter, allowed under `read(_)`. //! //! Verification is by execution (the design's AC-1..AC-3 plus the -//! panic-not-killed interaction): build each fixture through the real CLI, run the +//! panic-not-killed interaction): build each fixture through the CLI, run the //! produced binary, and assert the exit code / output. Expected values //! (exit 0 + `6`; exit 159; exit 101 + `[ens]`) trace to the oracle / the §4.1 //! mechanism, never copied from toolchain output (R-CHAR-3). @@ -107,12 +107,12 @@ fn write_fixture(name: &str, body: &str) -> PathBuf { path } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { diff --git a/forge/tests/scratch_cleanup.rs b/forge/tests/scratch_cleanup.rs index ccd111009..e4288c524 100644 --- a/forge/tests/scratch_cleanup.rs +++ b/forge/tests/scratch_cleanup.rs @@ -111,7 +111,7 @@ fn assert_no_scratch_leak(file: &Path, label: &str) -> Option { code } -// ---- #53: the SUCCESS path leaves no orphan ------------------------------ +// ---- #53: the success path leaves no orphan ------------------------------ #[test] fn success_path_leaves_no_scratch_orphan() { diff --git a/forge/tests/solver_vacuity_conformance.rs b/forge/tests/solver_vacuity_conformance.rs index 78ae4be4c..ccb16c704 100644 --- a/forge/tests/solver_vacuity_conformance.rs +++ b/forge/tests/solver_vacuity_conformance.rs @@ -5,7 +5,7 @@ //! hand-derived oracle `conformance/solver-vacuity/cases.json` (R-CHAR-3: expected //! verdicts trace to the oracle, never to forge's own output). //! -//! These checks issue real verus queries (the harness must prove for a detection), +//! These checks issue verus queries (the harness must prove for a detection), //! so every case here needs verus. The verus-needing cases skip with an eprintln //! when verus is absent (mirroring `lower_conformance.rs` / //! `vacuity_slag_conformance.rs`) rather than panic. diff --git a/forge/tests/spec_fn_string_param.rs b/forge/tests/spec_fn_string_param.rs index 6e7b73130..1b06eece6 100644 --- a/forge/tests/spec_fn_string_param.rs +++ b/forge/tests/spec_fn_string_param.rs @@ -19,7 +19,7 @@ //! `Seq`, for the generated byte-view fns only). //! //! This test is the regression pin so (A) cannot silently regress: it drives the -//! built `forge` binary's certificate ladder (real verus, the external cert +//! built `forge` binary's certificate ladder (verus, the external cert //! oracle the toolchain does not author for itself) over a `spec fn` that scans a //! `&String` param with `byte_at`, plus an exec twin and a fn naming it in a //! contract; all three must certify L3. The negative arm pins non-vacuity: a diff --git a/forge/tests/strat_differential.rs b/forge/tests/strat_differential.rs index d3c0cb3a7..13879b468 100644 --- a/forge/tests/strat_differential.rs +++ b/forge/tests/strat_differential.rs @@ -5,11 +5,11 @@ //! //! It runs the shipped `forge strat-tv` command (the same path the scheduled rotating-seed //! job and the audit check [8] gate drive), which spawns `lake env lean --run -//! Thermite/Strat/Cls/Wire.lean`. When `lake` is absent (a non-Lean machine) it SELF-SKIPS +//! Thermite/Strat/Cls/Wire.lean`. When `lake` is absent (a non-Lean machine) it SELF-skips //! (the `forge strat-tv` command itself reports a skip and exits 0); this test additionally //! guards so it only asserts the live agreement where the Lean toolchain exists — the //! `lean` CI job, where `cargo nextest run -p forge` runs with the spine built. There the -//! `lake_present()` branch is taken and the differential is genuinely exercised. +//! `lake_present()` branch is taken and the differential is exercised. use std::path::PathBuf; use std::process::Command; @@ -33,7 +33,7 @@ fn lake_present() -> bool { .unwrap_or(false) } -/// AC-4 (the headline): the Rust classifier returns the SAME verdict as the Lean +/// AC-4 (the headline): the Rust classifier returns the same verdict as the Lean /// `admitted` on N generated formulas, with zero disagreements. Run at the pinned default /// seed (reproducible); a disagreement exits the `forge strat-tv` command non-zero (the /// hard CI failure check [8] raises), failing this test with the verbatim finding. @@ -90,7 +90,7 @@ fn rust_classifier_matches_lean_admitted_on_generated_formulas() { ); } -/// AC-4 (the watchdog dimension): a DIFFERENT seed walks a different slice of the clause +/// AC-4 (the watchdog dimension): a different seed walks a different slice of the clause /// space and must also agree completely — the rotating-seed property the scheduled CI job /// relies on (a seed-dependent classifier divergence would surface as a red build). #[test] diff --git a/forge/tests/strengthening_conformance.rs b/forge/tests/strengthening_conformance.rs index e46527460..fed392335 100644 --- a/forge/tests/strengthening_conformance.rs +++ b/forge/tests/strengthening_conformance.rs @@ -4,8 +4,8 @@ //! hand-derived oracle `conformance/strengthening/cases.json` (R-CHAR-3: //! expected outcomes trace to §7 / the oracle, never to forge's own output). //! -//! Strengthening probes issue real verus queries per candidate (a suggestion is -//! surfaced only if it verifies against the real body and kills the #12 survivor), +//! Strengthening probes issue verus queries per candidate (a suggestion is +//! surfaced only if it verifies against the body and kills the #12 survivor), //! so every case here needs verus. The verus-needing cases skip with an eprintln //! when verus is absent (mirroring `mutation_conformance.rs`) rather than panic. //! @@ -155,7 +155,7 @@ fn cert_for<'a>(certs: &'a [Value], item: &str) -> &'a Value { .unwrap_or_else(|| panic!("no `{item}` cert in {certs:?}")) } -/// The strengthening suggestion CLAUSE strings on a cert (empty/absent → []). +/// The strengthening suggestion clause strings on a cert (empty/absent → []). fn suggestion_clauses(cert: &Value) -> Vec { cert.get("strengthening") .and_then(|s| s.as_array()) diff --git a/forge/tests/string_format_conformance.rs b/forge/tests/string_format_conformance.rs index e10fcea46..853852507 100644 --- a/forge/tests/string_format_conformance.rs +++ b/forge/tests/string_format_conformance.rs @@ -3,7 +3,7 @@ //! and `n.to_string()` with the gold-standard round-trip (`parse_le(result) == n`, //! REQ-8). These run the built `forge` binary end-to-end against the external truths //! the toolchain does not author for itself: the real `verus` SMT prover (the cert -//! levels) and the real `rustc` compiler + a real process run (the formatter prints +//! levels) and the real `rustc` compiler + a process run (the formatter prints //! the decimal). //! //! It pins the three C4 deliverables (REQ-9 `parse_u64` is out, blocked on C7/#95): @@ -13,7 +13,7 @@ //! * `n.to_string()` → L3 with the round-trip `ens parse_le(result) == n` (the //! grounded `16 verified, 0 errors` form: the divide/mod-by-10 digit loop, the //! `pow10`/`parse_le` spec fns, the `lemma_parse_push` append lemma). A wrong -//! digit emission fails the round-trip ens (the contract is load-bearing, +//! digit emission fails the round-trip ens (the contract is required, //! R-DEFER-9 non-vacuity). //! * `forge build` a formatter (`fn show42() -> String { ... n.to_string() }` //! entry) → compiles, runs, and prints the correct decimal (42 → the bytes @@ -96,12 +96,12 @@ fn cert_for<'a>(certs: &'a [Value], item: &str) -> &'a Value { .unwrap_or_else(|| panic!("no cert for `{item}` in {certs:?}")) } -/// `true` iff the `forge build --entry` runnable artifact can LINK + RUN here. The +/// `true` iff the `forge build --entry` runnable artifact can link + run here. The /// #57 runtime seccomp sandbox (`forge/src/sandbox.rs`) is native Linux only, with /// generated filters for x86_64 and aarch64. The emitted runner does not link off /// Linux (`Undefined symbols: _prctl` on macOS). /// The build+run tests SKIP with an explicit warning on any non-Linux platform — -/// FULL ACCEPTANCE OF THE BUILD+RUN PATH REQUIRES LINUX CI. Mirrors the +/// full acceptance OF the build+run PATH requires LINUX CI. Mirrors the /// `verus_present()` skip precedent (a missing capability is a logged skip, not a /// panic, R-CODE-4). fn linux_build_run_supported(test: &str) -> bool { @@ -192,10 +192,10 @@ fn ac7_to_string_round_trip_certifies_l3() { } /// AC-7 non-vacuity (R-DEFER-9) — a wrong digit emission fails the round-trip ens. -/// The round-trip `parse_le(result) == n` is load-bearing: a formatter that emits +/// The round-trip `parse_le(result) == n` is required: a formatter that emits /// the wrong digit produces a byte sequence that does not parse back to `n`, so the /// `ens` is undischarged → not L3. (Here the surface program is correct; this test -/// pins that the generated `u64_to_string`'s round-trip ens is a real proof: it +/// pins that the generated `u64_to_string`'s round-trip ens is a proof: it /// fails for a broken loop, grounded `15 verified, 1 errors` for a +1 digit shift. /// The surface cannot inject a wrong digit into the generated fn, so the non-vacuity /// is proved at the codegen-grounding level; here we pin that an overclaimed ens, @@ -213,7 +213,7 @@ fn ac7_overclaimed_round_trip_is_rejected() { // The fn returns `n.to_string()` (parse_le == n) but claims `parse_le(result) == // n + 1`, an overclaim. The generated round-trip ens proves `parse_le == n`, // so `parse_le == n + 1` is false (for n where n != n+1, i.e. always) → verus - // fails the postcondition → not L3. The round-trip is real teeth. + // fails the postcondition and therefore does not reach L3. let certs = check_program( "tostring_overclaim", "fn bad(n: u64) -> String\n req n < 1000\n ens parse_be(result) == n + 1\n fx alloc\n{ n.to_string() }\n", diff --git a/forge/tests/string_l3_completeness.rs b/forge/tests/string_l3_completeness.rs index 736192748..205f7efcb 100644 --- a/forge/tests/string_l3_completeness.rs +++ b/forge/tests/string_l3_completeness.rs @@ -5,7 +5,7 @@ //! Stage 7 Strings shipped (#79), but two reachable lowering paths were never //! exercised by `conformance/string_demo.th`: //! -//! GAP 1 — `slice`'s exec-position arg coercion. The `TString` wrapper's index +//! gap 1 — `slice`'s exec-position arg coercion. The `TString` wrapper's index //! accessor `slice(lo: usize, hi: usize)` takes `usize`, but a Thermite surface //! index is commonly a `u64` (`s.slice(0, k)` with `k: u64`). Verus does no //! implicit `u64 -> usize` narrowing, so the un-coerced arg produced @@ -13,7 +13,7 @@ //! index arg of both string index intrinsics (`byte_at`/`slice`) with `as usize` //! (`thermite-lower::lower` `lower_expr` MethodCall exec arm + `is_usize_cast`). //! -//! GAP 2 — the `TString` wrapper def woven into the per-item sub-program when a +//! gap 2 — the `TString` wrapper def woven into the per-item sub-program when a //! `String`/`Type::String` is reachable as a struct/enum field type (not just a //! fn param/return). `struct Buf { text: String, .. }`'s field lowered to `pub //! text: TString` but the per-item sub-program did not emit the wrapper def @@ -24,7 +24,7 @@ //! fns in spec position (the fn-signature `Ctx::string_fields` + the struct-`inv` //! `lower_inv_expr` MethodCall arm for `inv cursor <= text.len()`). //! -//! These run the built `forge` binary end-to-end (real verus). If verus is absent +//! These run the built `forge` binary end-to-end (verus). If verus is absent //! they skip with an eprintln (rather than panic on a missing solver), matching //! `divergence_strings.rs`. //! @@ -102,7 +102,7 @@ fn cert_for<'a>(certs: &'a [Value], item: &str) -> &'a Value { .unwrap_or_else(|| panic!("no cert for `{item}` in {certs:?}")) } -/// GAP 1 — a `String` `slice(lo, hi)` whose `hi` is a `u64` parameter certifies +/// gap 1 — a `String` `slice(lo, hi)` whose `hi` is a `u64` parameter certifies /// L3: the exec arg lowering coerces `k as usize` for the `usize` accessor (was /// `error[E0308]: expected usize, found u64` -> L0). /// @@ -138,10 +138,10 @@ fn gap1_slice_u64_arg_coerces_and_certifies_l3() { ); } -/// GAP 1 (the editor op the gap blocked) — a bounded mid-string insert via +/// gap 1 (the editor op the gap blocked) — a bounded mid-string insert via /// slice+concat certifies L3. `s.slice(0, p).concat(ins).concat(s.slice(p, /// s.len()))`: the `s.len()` arg is a non-literal `u64`, so it coerces `as -/// usize` for the second `slice` (the GAP-1 fix applied to the realistic editor +/// usize` for the second `slice` (the gap-1 fix applied to the realistic editor /// path, not just the single triggering site). /// /// Authority: `.design/basis/07-strings.md` REQ-4 — `slice`'s `ens result.len() == @@ -169,7 +169,7 @@ fn gap1_mid_string_insert_via_slice_concat_certifies_l3() { ); } -/// GAP 2 — a `struct Buf { text: String, cursor: u64 }` with a String-field +/// gap 2 — a `struct Buf { text: String, cursor: u64 }` with a String-field /// type-invariant (`inv cursor <= text.len()`) and a constructing `fn mk(t: String) /// -> Buf` both certify L3: the `TString` wrapper def is woven into the per-item /// sub-program because `String` is reachable as a struct field type (was @@ -216,7 +216,7 @@ fn gap2_buf_struct_with_string_field_certifies_l3() { ); } -/// GAP 2 (the second reachable form) — a `fn` reading `b.text.len()` from a `&Buf` +/// gap 2 (the second reachable form) — a `fn` reading `b.text.len()` from a `&Buf` /// parameter certifies L3: the String-field receiver `b.text`'s `.len()` rewrites /// to `b.text.spec_len()` in the `ens` contract (the field analog of the bare /// `String`-value rewrite), and the wrapper is woven because `Buf`'s field reaches @@ -251,12 +251,12 @@ fn gap2_fn_reading_string_field_len_certifies_l3() { ); } -/// Non-vacuity (R-DEFER-9) — the GAP-1 coercion does not launder an unsound slice: -/// `slice`'s `req self.well_formed() && lo <= hi && hi <= len` is load-bearing. A +/// Non-vacuity (R-DEFER-9) — the gap-1 coercion does not launder an unsound slice: +/// `slice`'s `req self.well_formed() && lo <= hi && hi <= len` is required. A /// contract that does not establish `s.well_formed()` (no CAP bound on `s.len()`) /// leaves `slice`'s `self.well_formed()` precondition undischarged -> verus fails /// -> L0. The `as usize` coercion fixes the type mismatch only; it never weakens -/// the bound (the same way `byte_at`'s `i < len` stays load-bearing). +/// the bound (the same way `byte_at`'s `i < len` stays required). /// /// Authority: `.design/basis/07-strings.md` REQ-4 (slice requires /// `self.well_formed()`) + AC-4 / R-DEFER-9 (a missing bound is caught, not diff --git a/forge/tests/string_search_conformance.rs b/forge/tests/string_search_conformance.rs index f5aa371a7..3a75c292b 100644 --- a/forge/tests/string_search_conformance.rs +++ b/forge/tests/string_search_conformance.rs @@ -3,7 +3,7 @@ //! 07-strings.md REQ-13), `find` (→ `Option`, REQ-14, reusing C7), `split` (→ //! `Vec`, REQ-15, reusing C6), and `trim` (→ `String`, REQ-16). These run //! against the two external truths the toolchain does not author for itself: the -//! built `forge` binary's certificate ladder (`forge check`, real verus) for the +//! built `forge` binary's certificate ladder (`forge check`, verus) for the //! predicate/find ops, and — for the `split`/`trim` constructing ops whose thin //! surface caller cannot be mutation-scored (the §7 floor needs a scoreable body //! mutant; a one-line `{ s.split(sep) }` delegates entirely to the proven method, the @@ -15,13 +15,13 @@ //! * `s.starts_with(p)` / `s.contains(p)` / `s.ends_with(p)` → L3 pure with the //! `ens result == occurs_at(..)` / `contains_sub(..)` contract; a true case //! (a known prefix) proves `result == true`, a false case proves `result == -//! false`, and a broken `starts_with` fails verus (the predicate is real teeth). +//! false`, and a broken `starts_with` fails verus. //! * `s.find(p)` → L3 pure with the spec-`match`-in-`ens`; a pinned Some case //! (needle present at 0) proves `result is Some`, so the always-`None` mutant is //! provably wrong (killable — the #101 trap avoided). -//! * `s.split(sep)` → the count-bound + sep-free contract verifies under real verus +//! * `s.split(sep)` → the count-bound + sep-free contract verifies under verus //! `0 errors`; a `split`-drop body (always 1 piece) fails the count bound. -//! * `s.trim()` → the length floor + subrange content verifies under real verus. +//! * `s.trim()` → the length floor + subrange content verifies under verus. //! * The `contains` name-clash: a String `s.contains(needle)` and a Vec //! `v.contains(x)` both certify (receiver-type dispatch — `TString::contains` vs //! `TVec::contains` — neither clobbers). @@ -155,7 +155,7 @@ fn verus_on_lowered(tag: &str, program: &str) -> (bool, String) { /// AC-9 — `starts_with`/`contains`/`ends_with` certify L3 pure with the /// `occurs_at`/`contains_sub` contract; a true case and a false case both prove. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-13 — the predicates lower to the byte +/// authority: `.design/basis/07-strings.md` REQ-13 — the predicates lower to the byte /// scans, the contract names the seeded `occurs_at`/`contains_sub` spec fns inside the /// §4.2 cage. `thermite-design.md` §6: a fully-discharged verus proof is L3. Grounded /// `14 verified, 0 errors`. @@ -195,7 +195,7 @@ fn ac9_predicates_certify_l3_pure() { /// true`, so the predicate is non-vacuous (a broken always-false `starts_with` would /// fail this contract). /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-13 — a true case proves `result == +/// authority: `.design/basis/07-strings.md` REQ-13 — a true case proves `result == /// true`; non-vacuity (the false case bites a broken predicate). Grounded. #[test] fn ac9_true_case_pinned_certifies_l3() { @@ -220,7 +220,7 @@ fn ac9_true_case_pinned_certifies_l3() { /// AC-10 — `find` certifies L3 pure with the spec-`match`-in-`ens`; the Some case is /// pinned so the always-None mutant is killable (#101 trap avoided). /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-14 — `s.find(p)` lowers to the +/// authority: `.design/basis/07-strings.md` REQ-14 — `s.find(p)` lowers to the /// occurrence scan, the `ens match result { Some(at) => occurs_at(..), None => /// !contains_sub(..) }` (the C7 spec-`match`). A pinned Some case (needle present) /// proves `result is Some`. Grounded. `thermite-design.md` §6: a discharged proof is L3. @@ -254,13 +254,13 @@ fn ac10_find_certifies_l3_with_pinned_some() { /// AC-9 non-vacuity (R-DEFER-9) — a broken `starts_with` (drops the byte-mismatch /// check, always returns `true`) fails verus. The predicate's `ens result == -/// occurs_at(..)` is load-bearing: an always-`true` body does not satisfy it when the +/// occurs_at(..)` is required: an always-`true` body does not satisfy it when the /// prefix does not match. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-13 — a broken `starts_with` fails +/// authority: `.design/basis/07-strings.md` REQ-13 — a broken `starts_with` fails /// (`13 verified, 1 errors`, the false case bites). `thermite-design.md` §7. The break /// is injected into a standalone verus probe (the surface cannot mutate the generated -/// method body), confirming the predicate's contract is a real proof. +/// method body), confirming the predicate's contract is a proof. #[test] fn ac9_broken_starts_with_fails_real_verus() { if !verus_present() { @@ -313,13 +313,13 @@ fn main() {} ); } -/// AC-11 — `split` verifies under real verus with the count-bound + sep-free contract +/// AC-11 — `split` verifies under verus with the count-bound + sep-free contract /// (the Vec push loop, fx alloc). A thin `{ s.split(sep) }` caller cannot be /// mutation-scored (the parse_u64 AC-4 precedent — the method's proof is the -/// deliverable), so the cert level is established by the real verus run, not the +/// deliverable), so the cert level is established by the verus run, not the /// §7-gated `forge check` level. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-15 — `s.split(sep)` lowers to the scan +/// authority: `.design/basis/07-strings.md` REQ-15 — `s.split(sep)` lowers to the scan /// loop pushing `TString` pieces into a `TVecTString` (reusing C6), `ens /// result.len() == 1 + count_sep(s@, sep) && forall|k| sep_free(..)`. Grounded `7 /// verified, 0 errors`. `thermite-design.md` §6. @@ -343,9 +343,9 @@ fn ac11_split_count_bound_verifies_under_real_verus() { } /// AC-11 non-vacuity (R-DEFER-9) — a broken `split` that drops the mid-loop -/// `pieces.push` (always 1 piece) fails the count bound under real verus. +/// `pieces.push` (always 1 piece) fails the count bound under verus. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-15 — a broken `split` fails (`6 +/// authority: `.design/basis/07-strings.md` REQ-15 — a broken `split` fails (`6 /// verified, 1 errors`, the count bound bites). `thermite-design.md` §7. #[test] fn ac11_broken_split_fails_real_verus() { @@ -412,10 +412,10 @@ fn main() {} ); } -/// AC-12 — `trim` verifies under real verus with the length floor + subrange content +/// AC-12 — `trim` verifies under verus with the length floor + subrange content /// contract (fx alloc). Like `split`, the thin caller is verus-grounded directly. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-16 — `s.trim()` lowers to the +/// authority: `.design/basis/07-strings.md` REQ-16 — `s.trim()` lowers to the /// forward/backward whitespace scan + bounded copy, `ens result.len() <= s.len() && /// exists|lo,hi| result == s.subrange(lo,hi)`. Grounded `8 verified, 0 errors`. #[test] @@ -441,7 +441,7 @@ fn ac12_trim_verifies_under_real_verus() { /// both ops certify L3, receiver-type-dispatched (`TString::contains` vs /// `TVec::contains`), neither clobbers the other. /// -/// AUTHORITY: `.design/basis/07-strings.md` REQ-13 (the design-flagged name-clash) + +/// authority: `.design/basis/07-strings.md` REQ-13 (the design-flagged name-clash) + /// `.design/basis/04-collections.md` REQ-12 (the Vec membership `contains`). Rust keys /// inherent-method resolution on the receiver type, so the shared surface name resolves /// to two distinct methods. `thermite-design.md` §6. @@ -457,7 +457,7 @@ fn contains_name_clash_both_string_and_vec_certify() { // C6 admits `v.contains` as a flat built-in but the v1 corpus exercises it in exec, // not a contract `ens`). Both lower in one program: the String op resolves to // `TString::contains`, the Vec op to `TVec::contains` — receiver-type dispatch, no - // clobber. The whole program lowering verifying under real verus is the proof both + // clobber. The whole program lowering verifying under verus is the proof both // dispatch correctly (a clobber would mis-resolve one and fail verus). let (ok, output) = verus_on_lowered( "name_clash", diff --git a/forge/tests/tuples_conformance.rs b/forge/tests/tuples_conformance.rs index fb286c31c..26b4237a5 100644 --- a/forge/tests/tuples_conformance.rs +++ b/forge/tests/tuples_conformance.rs @@ -4,7 +4,7 @@ //! `.1`/…), the v1 §2.3 "one way" tuple access (not destructuring — REQ-9 //! deferred). These run against the two external truths the toolchain does not //! author for itself: the built `forge` binary's certificate ladder (`forge -//! check`, real verus) and the real `verus` binary on the emitted lowering. +//! check`, verus) and the real `verus` binary on the emitted lowering. //! //! Pins the C9-B deliverables (`.design/basis/10-recursion-tuples.md`): //! @@ -214,7 +214,7 @@ fn tuple_expr_and_projection_nodes() { } // --------------------------------------------------------------------------- -// End-to-end ladder pins (REQ-8; real verus via `forge check`). +// End-to-end ladder pins (REQ-8; verus via `forge check`). // --------------------------------------------------------------------------- const SWAP_L3: &str = "fn swap(a: u64, b: u64) -> (u64, u64)\n req true\n ens result.0 == b && result.1 == a\n fx pure\n{ (b, a) }\n"; diff --git a/forge/tests/vec_completeness_conformance.rs b/forge/tests/vec_completeness_conformance.rs index e84d255ee..97da2f7eb 100644 --- a/forge/tests/vec_completeness_conformance.rs +++ b/forge/tests/vec_completeness_conformance.rs @@ -152,7 +152,7 @@ fn vec_u64_ops_certify_l3() { "REQ-8 op `{needle}` not emitted on TVecU64:\n{emitted}" ); } - // REQ-8 the no-OOB insert guard (`i <= len`) is present (load-bearing). + // REQ-8 the no-OOB insert guard (`i <= len`) is present (required). assert!( emitted.contains("i <= old(self).data.len(),"), "REQ-8 insert's `i <= len` no-OOB guard absent (would be vacuous):\n{emitted}" diff --git a/lean/Thermite.lean b/lean/Thermite.lean index c61db5e63..84d6f65b5 100644 --- a/lean/Thermite.lean +++ b/lean/Thermite.lean @@ -74,30 +74,22 @@ import Thermite.Faithfulness -- (hand-translated, the gap an exporter closes) discharged by `smt` and kernel-checked, -- `#print axioms` = [propext, Classical.choice, Quot.sound] (standard only; the cvc5 proof -- is replayed, not oracle-trusted). The walls (toolchain v4.29.0 + full Mathlib + --- vendored cvc5 1.3.2; the hand-translation residual; the BitVec-reconstruction `sorry` --- excluding bitwise obligations; Verus/Z3 not emitting reconstructable certificates) are in +-- vendored cvc5 1.3.2; the hand-translation residual; Verus/Z3 not emitting +-- reconstructable certificates) are in -- `.design/verified/z3-demotion.md`. import Thermite.SmtDemo -- Layer 4 (trust-shrink), stage-3 increment REQ-7 (#349; `.design/stage3-bv-reconstruction.md` -- REQ-7 / AC-8): the AUTOMATED Rust→Lean obligation exporter's output. Where `SmtDemo` --- hand-translated two TV obligations, `forge/src/lean_smt_export.rs` now EMITS the --- `(P_prod) ⟺ (P_ref)` Lean goals — one QF_LIA scalar clause + two QF_BV `@bv` clauses --- (the bounded-integer machine-model, since lean-smt's literal BitVec reconstruction --- bit-blasts through an upstream `sorry`) — each discharged by `smt` and kernel-checked, --- `#print axioms` ⊆ {propext, Classical.choice, Quot.sound}. The file is the exporter's --- verbatim output (pinned by `golden_file_matches_exporter`); building it here makes the --- default `lake build` kernel-check the AC-8 reconstruction (the Smt toolchain already --- enters the graph via `SmtDemo`, so this adds no dependency). +-- hand-translated two TV obligations, `forge/src/lean_smt_export.rs` now emits the +-- `(P_prod) ⟺ (P_ref)` Lean goals: one QF_LIA scalar clause and three literal +-- `BitVec N` QF_BV clauses, including the full arithmetic/bitwise/shift/division term +-- surface. QF_LIA uses `smt`; QF_BV uses kernel-checked normalization lemmas. +-- `#print axioms` stays within {propext, Classical.choice, Quot.sound}. The file is +-- generated verbatim and pinned by `golden_file_matches_exporter`. import Thermite.SmtExport --- Layer 4 (trust-shrink), stage-3 REQ-7/REQ-8 (#356, "Path B"): the bit-vector ⟷ --- bounded-integer model FAITHFULNESS metatheorem. The exporter renders a `@bvN` clause --- over the bounded-integer machine-model (not `BitVec N`, whose `smt` reconstruction --- bit-blasts through an upstream `sorry`). `Thermite.BvModel` proves — KERNEL-CHECKED, --- core-only, `#print axioms` ⊆ {propext, Classical.choice, Quot.sound} — that the two --- denotations agree (`frmInt_iff_frmBV`), so the exporter's `by smt`-discharged int-model --- `↔` certifies the genuine bit-vector clause (`tv_equiv_faithful`). This discharges the --- REQ-8 `render_bv_prop` faithfulness obligation for the renderable fragment IN OUR OWN --- SPINE — no dependency on lean-smt's (stalled) literal QF_BV reconstruction. +-- Layer 4 reference theorem (#356): the older bounded-integer model remains proved +-- equivalent to `BitVec N`. The active exporter now emits literal `BitVec N`, so this +-- module is a supplementary semantic cross-check rather than a bridge in the export path. import Thermite.BvModel -- The stabilization spine prerequisite (increment (ii), #240, ref #203; -- `.design/verified/proof-backends.md` §4/§6.1): the `stabilizes`/`stabilizesProp` diff --git a/lean/Thermite/BvModel.lean b/lean/Thermite/BvModel.lean index f4f0dbe8d..1544c71a0 100644 --- a/lean/Thermite/BvModel.lean +++ b/lean/Thermite/BvModel.lean @@ -3,36 +3,27 @@ metatheorem (`.design/stage3-bv-reconstruction.md` REQ-7/REQ-8; issue #356). ───────────────────────────────────────────────────────────────────────────── - What this closes. + What this proves. ───────────────────────────────────────────────────────────────────────────── - `forge/src/lean_smt_export.rs` renders a `@bvN` machine-semantics clause NOT over - Lean `BitVec N` (lean-smt's literal BitVec `smt` reconstruction bit-blasts through an - upstream `sorry`; see `.design/verified/z3-demotion.md`) but over the **bounded-integer - machine-model**: each `N`-bit variable is an `Int`, each wrapping op `a ⊕ b` is - `(a ⊕ b) % 2^N`, and each unsigned comparison is the integer comparison. That model - is QF_LIA, which `smt` reconstructs kernel-clean. - - The residual trust question (the `render_bv_prop` faithfulness obligation REQ-8 names): - IS the bounded-integer model a faithful rendering of the fixed-width bit-vector - semantics? This module answers YES with a KERNEL-CHECKED theorem — not by replaying - the literal SMT-LIB2 query (which needs the stalled lean-smt bv reconstruction), but by - proving the two denotations agree. With cvc5/Z3's int-model `↔` reconstructed in the - kernel (the exporter's `by smt` goal) AND this faithfulness theorem, a `@bv` clause's - truth is kernel-grounded end to end, with no solver in the trust base for the fragment. + This module relates `BitVec N` to the bounded-integer machine model used by an + earlier exporter path: each variable is an `Int` in `[0, 2^N)`, wrapping arithmetic + reduces modulo `2^N`, and unsigned comparisons use the integer order. + + The main result is a kernel-checked proof that the two denotations agree. The active + exporter now emits literal `BitVec N`, so this is a supplementary semantic cross-check + rather than a bridge in the reconstruction path. Mathlib-free / Smt-free: rests only on Lean-core `BitVec` lemmas, so it builds in the core spine and is covered by `scripts/lean-axiom-probe.sh` (CI, no cvc5 needed) — a - strictly stronger trust position than the `Smt`-importing `Thermite.SmtExport`. + small independent check beside the `Smt`-importing `Thermite.SmtExport`. - The fragment below mirrors `lean_smt_export.rs`'s `render_term` / `render_prop` arms - (the Rust-emitter ⟷ Lean-AST correspondence is inspection-tier, as for the whole - exporter — `.design/verified/exporter-surface-correspondence.md` scope/limits). + The fragment covers wrapping `+`/`-`/`*`, unsigned comparisons, and boolean + connectives. -/ namespace Thermite.BvModel -/-- The renderable QF_BV TERM fragment — mirrors `render_term` in `lean_smt_export.rs` - (single-segment variables, integer literals, `+`/`-`/`*`). -/ +/-- The arithmetic term fragment: variables, literals, and wrapping `+`/`-`/`*`. -/ inductive Tm where | var (i : Nat) | lit (n : Nat) @@ -41,8 +32,7 @@ inductive Tm where | mul (a b : Tm) deriving Repr -/-- The renderable QF_BV PROPOSITION fragment — mirrors `render_prop` (comparisons + - boolean connectives). -/ +/-- Comparisons and boolean connectives over the arithmetic term fragment. -/ inductive Frm where | tt | ff @@ -59,7 +49,7 @@ inductive Frm where variable {w : Nat} -/-! ## The fixed-width bit-vector semantics (what a `@bvN` clause MEANS, REQ-2). +/-! ## The fixed-width bit-vector semantics (what a `@bvN` clause means, REQ-2). Every operator is its `BitVec w` (2's-complement / unsigned) machine counterpart; the comparisons are the unsigned bit-vector relations (`BitVec`'s `<`/`≤` are `ult`/`ule`). -/ @@ -84,7 +74,7 @@ def frmBV (ρ : Nat → BitVec w) : Frm → Prop | .or a b => frmBV ρ a ∨ frmBV ρ b | .not a => ¬ frmBV ρ a -/-! ## The bounded-integer machine-model (what `lean_smt_export.rs` EMITS to `smt`). +/-! ## The bounded-integer machine model. Each variable is an `Int` (range `[0, 2^w)` in the emitted goal's hypotheses); each wrapping operation reduces `% 2^w` (Lean `Int.emod` lands in `[0, 2^w)`); comparisons @@ -160,9 +150,8 @@ theorem tmInt_eq_toNat (ρ : Nat → BitVec w) (t : Tm) : -- goal: (↑p - ↑q) % ↑(2^w) = (↑(2^w) - ↑q + ↑p) % ↑(2^w) exact (emod_sub_bridge _ _ _).symm -/-- PROPOSITION faithfulness (the `render_bv_prop` obligation): the bounded-integer model - of a clause, at the `toNat` valuation, holds IFF its bit-vector denotation holds. So - the exporter's `by smt`-discharged int-model goal certifies the genuine `@bv` clause. -/ +/-- Proposition faithfulness: at the `toNat` valuation, the bounded-integer model + agrees with the bit-vector denotation. -/ theorem frmInt_iff_frmBV (ρ : Nat → BitVec w) (f : Frm) : frmInt w (toNatσ ρ) f ↔ frmBV ρ f := by induction f with @@ -190,10 +179,7 @@ theorem frmInt_iff_frmBV (ρ : Nat → BitVec w) (f : Frm) : | or a b iha ihb => simp only [frmInt, frmBV]; rw [iha, ihb] | not a ih => simp only [frmInt, frmBV]; rw [ih] -/-- The corollary the exporter relies on (REQ-8): if the bounded-integer model proves the - translation-validation equivalence `(P_prod) ⟺ (P_ref)` (the `by smt` goal), then the - genuine bit-vector clauses are equivalent too — the int-model `↔` is faithful to the - `@bv` semantics. -/ +/-- A bounded-integer production/reference equivalence transfers to `BitVec`. -/ theorem tv_equiv_faithful (ρ : Nat → BitVec w) (prod ref : Frm) (h : frmInt w (toNatσ ρ) prod ↔ frmInt w (toNatσ ρ) ref) : frmBV ρ prod ↔ frmBV ρ ref := by @@ -204,9 +190,8 @@ theorem tv_equiv_faithful (ρ : Nat → BitVec w) (prod ref : Frm) The faithfulness theorems must rest only on the standard axiom set `{propext, Classical.choice, Quot.sound}` — no `sorryAx`, no custom axiom. This is - the kernel-checked discharge of the `render_bv_prop` faithfulness obligation for the - renderable fragment. Probed in-file (and built by `scripts/lean-axiom-probe.sh`); - NOT added to the fixed universal-pillar THEOREM list (that is a gate action). -/ + an independent check of the bounded-integer reference model. Probed in-file and built + by `scripts/lean-axiom-probe.sh`; not added to the fixed universal-pillar theorem list. -/ #print axioms tmInt_eq_toNat #print axioms frmInt_iff_frmBV #print axioms tv_equiv_faithful diff --git a/lean/Thermite/Exec.lean b/lean/Thermite/Exec.lean index c10b50aba..329b272bf 100644 --- a/lean/Thermite/Exec.lean +++ b/lean/Thermite/Exec.lean @@ -488,7 +488,7 @@ theorem exec_ref_sound : ∀ (e : ExecExpr) (env : ExecEnv), theorem exec_ref_sound_eq (e : ExecExpr) (env : ExecEnv) : execRefValue e env = execDenote e env := exec_ref_sound e env -/-! ## The overflow-obligation treatment is genuine (not silently unbounded) +/-! ## The overflow-obligation treatment is (not silently unbounded) These witness that the bounded model is real: an arithmetic op that overflows its type's bound has no value (`execDenote = none`, the obligation fails), while the same op stays @@ -505,7 +505,7 @@ def envOverflow : ExecEnv := else .int ⟨.u64, 0⟩ slices := fun s => if s = "xs" then [⟨.u64, 10⟩, ⟨.u64, 20⟩, ⟨.u64, 30⟩] else [] } -/-- Overflow obligation is genuine. `a + b` with `a = 2^64 - 1`, `b = 1` (both `u64`) +/-- Overflow is represented as an obligation. `a + b` with `a = 2^64 - 1`, `b = 1` (both `u64`) overflows: `execDenote` is `none` (the no-overflow obligation fails, the value is not defined; a Verus exec `+` rejected because overflow is possible). A silently- unbounded model would return `some (2^64)` here, so this proves the model is bounded. -/ diff --git a/lean/Thermite/Exec/Loop.lean b/lean/Thermite/Exec/Loop.lean index 59874ea3b..d6810569e 100644 --- a/lean/Thermite/Exec/Loop.lean +++ b/lean/Thermite/Exec/Loop.lean @@ -434,14 +434,13 @@ theorem l1_while_rule_certifies_exit : -- `while_rule` then certifies the after-loop characterization at `stf`. exact ⟨stf, hrun, while_rule l1Cond l1Body l1Inv l1_preservation 4 l1State stf l1_entry_holds hrun⟩ -/-! ## Negative lemmas — the while-rule's premises are load-bearing (the teeth, the - L2/L3 mirrors) +/-! ## Negative lemmas: required while-rule premises Two faithfulness bugs the loop TV must not commit, each proven to break the rule: pinning that `while_rule` consumes `h_pres` (L2) and that the after-loop conclusion is exactly `inv ∧ ¬cond`, not a stronger over-claim (L3). -/ -/-! ### L2 — a non-preserved invariant (the `lo + 2` shape): `h_pres` is load-bearing. +/-! ### L2 — a non-preserved invariant (the `lo + 2` shape): `h_pres` is required. Mirrors the Rust L2 (`loop_teeth.rs::l2_broken_preservation_caught`): a body that steps `lo` by 2 (the production infidelity for source `lo + 1`) does not preserve @@ -512,7 +511,7 @@ theorem l2_non_preserved_invariant_admits_bad_step : simp only [execIntValue, hlo4, hn4] at hle omega -/-- L2 contrast — `while_rule` refuses the buggy loop (the teeth bite the bug). If +/-- L2 contrast: `while_rule` refuses the buggy loop. If one could supply a preservation premise `h_pres` for the buggy `l2Body`, the rule would certify its after-state; but L2 above shows no such `h_pres` exists (the premise is false at `l2State`). A hypothetical `h_pres` for `l2Body` @@ -540,8 +539,8 @@ theorem l2_no_preservation_premise_for_buggy_body : = `lo ≤ n ∧ ¬(lo < n)`, satisfied by the exit state `lo = 3 = n`. The over-claim `lo > n` is false there (`3 > 3` is false). So a production claiming the stronger `lo > n` would be refuted at the `loopDenote` exit; the conclusion - is exactly `inv ∧ ¬cond`, never the over-claim. The loop analogue of the wrong-cell / - swapped-branch teeth. (`exit_lo_gt_n` is the over-claim predicate; the witness is + is exactly `inv ∧ ¬cond`, never the over-claim. This is the loop analogue of the + wrong-cell and swapped-branch tests. (`exit_lo_gt_n` is the over-claim predicate; the witness is `l1_while_rule_certifies_exit`'s `stf`.) -/ theorem l3_exit_overclaim_refuted : ∃ stf, loopDenote l1Cond l1Body 4 l1State = some stf diff --git a/lean/Thermite/Exec/Stmt.lean b/lean/Thermite/Exec/Stmt.lean index aab546a0e..a44305375 100644 --- a/lean/Thermite/Exec/Stmt.lean +++ b/lean/Thermite/Exec/Stmt.lean @@ -426,10 +426,10 @@ theorem b1_let_chain_threads : execDenote, asInt, evalArith, rawArith, IntTy.bound, IntTy.width] decide -/-- B2 — the mutation order is load-bearing. `{ let mut s = x; s = s + 1; +/-- B2 — the mutation order is required. `{ let mut s = x; s = s + 1; s = s * 2; s }` threads `s ↦ 5 → 6 → 12` = `12`, while the reorder `s = s * 2; s = s + 1` threads `s ↦ 5 → 10 → 11` = `11`, a different result. The mutation - updates the state and the order matters (the state-sequencing teeth). -/ + updates the state, so the order matters. -/ theorem b2_mutation_order_matters : bodyDenote (.mk [ .letS "s" (.var "x"), @@ -576,8 +576,8 @@ theorem mutation_not_applied_breaks_soundness : execDenote, asInt, evalArith, rawArith, IntTy.bound, IntTy.width] decide -/-- The faithful positive counterpart (the teeth bite only the bugs): with the real - threading the body encoder is sound; `bodyRefState = bodyDenote` for the correct +/-- With the faithful threading, the body encoder is sound: + `bodyRefState = bodyDenote` for the correct `{ let mut s = x; s = s + 1; s }` body, by `body_ref_sound`. -/ theorem faithful_body_is_sound : bodyRefState @@ -601,7 +601,7 @@ theorem faithful_body_is_sound : the exec side. Its `none` arises only at failure sites (the `evalArith` overflow / div-or-shift-by-zero, the out-of-range `index`, the `asInt`/`asBool` sort mismatch, the `letS` re-shadow, the unbound `assign`, a tail-less block), and `some v` means a value: the - spine's own teeth `body_overflow_rhs_has_no_result` (`= none`) vs `body_in_range_rhs_has_result` + contrasting lemmas `body_overflow_rhs_has_no_result` (`= none`) and `body_in_range_rhs_has_result` (`= some (.int ⟨.u64, 10⟩)`). The `Option` is the bottom-distinguishing layer; the #213/#241 trap (a total denotation that forges a value at the bottom) does not exist in `S_B`. So `bodyConverges` is a definitional abbreviation over `bodyDenote`, not a new denotation. -/ diff --git a/lean/Thermite/Faithfulness.lean b/lean/Thermite/Faithfulness.lean index f1b3b971f..c25908d84 100644 --- a/lean/Thermite/Faithfulness.lean +++ b/lean/Thermite/Faithfulness.lean @@ -218,7 +218,7 @@ theorem lowering_faithful (w : FnTvWitness) : ⟨tv_meta_contract w.fuel w.contract w.contractEnv w.loweredContract w.h_tv_contract, tv_meta_body w.body w.bodyState w.loweredBody w.h_tv_body⟩ -/-! ## Non-vacuity of the capstone: `h_tv` is a load-bearing premise, the `∀` ranges freely +/-! ## Non-vacuity of the capstone: `h_tv` is a required premise, the `∀` ranges freely These witnesses show that `lowering_faithful` (and its per-layer pieces) is not a trivial tautology. The conclusion is the faithfulness equality, and `h_tv` is a premise diff --git a/lean/Thermite/PinBodyRegistry.lean b/lean/Thermite/PinBodyRegistry.lean index 2c753ea1d..4eb49f5bf 100644 --- a/lean/Thermite/PinBodyRegistry.lean +++ b/lean/Thermite/PinBodyRegistry.lean @@ -108,7 +108,7 @@ theorem omission_forces_r_zero : ∀ r, stabilizes hCall envO r → r = 0 := by rw [body_bottoms_at_every_fuel N] at this exact this.symm -/-- `ens: result == 0`, a contract the real item (h(x) = 5, result 5) violates. -/ +/-- `ens: result == 0`, a contract the item (h(x) = 5, result 5) violates. -/ def ensWrong : Expr := Expr.cmp CmpOp.eq (Expr.var "result") (Expr.intLit 0) /-- The pin: the §4 exported obligation (the #214 ∀r form, req = true), diff --git a/lean/Thermite/PinBrokenLift.lean b/lean/Thermite/PinBrokenLift.lean index e871f3673..133cb322f 100644 --- a/lean/Thermite/PinBrokenLift.lean +++ b/lean/Thermite/PinBrokenLift.lean @@ -80,7 +80,7 @@ theorem brokenLift_counterexample : ∧ sdenote twoCarrier qTrivial phi rho = false := by decide -/-- The pin: the push/lift lemma `sdenote_push_lift` is FALSE for `liftBadFrm` — +/-- The pin: the push/lift lemma `sdenote_push_lift` is false for `liftBadFrm` — there is an instance on a concrete carrier where it fails. (Contrast `SubstKit.sdenote_push_lift`, which holds for every carrier.) -/ theorem brokenLift_breaks_push_lift : diff --git a/lean/Thermite/PinCombDeriv.lean b/lean/Thermite/PinCombDeriv.lean index 6b974dd47..c405e69aa 100644 --- a/lean/Thermite/PinCombDeriv.lean +++ b/lean/Thermite/PinCombDeriv.lean @@ -64,7 +64,7 @@ def qPin : Atom → Bool /-- `0 ≤ i` (the faithful lower bound; reused verbatim from `CombDeriv`). -/ def boundLoP (t : Tm) : Atom := .rel .le (.lit usizeS) t -/-- `i ≤ len` — the OFF-BY-ONE upper bound (`.rel .le` where `.rel .lt` is meant). -/ +/-- `i ≤ len` — the OFF-BY-one upper bound (`.rel .le` where `.rel .lt` is meant). -/ def boundHiWrong (sq t : Tm) : Atom := .rel .le t (.len sq) /-- The off-by-one `forall_in` expansion. -/ @@ -88,7 +88,7 @@ theorem offbyone_breaks_demotion : fdenote qPin dom (forallInExpWrong elem f) σ0 ≠ fdenote qPin dom (forallInExp elem f) σ0 := by decide -/-- For contrast: the FAITHFUL expansion's truth matches the bounded `∀` +/-- For contrast: the faithful expansion's truth matches the bounded `∀` characterization `comb_deriv_forall_in` derives — the divergence is solely the off-by-one upper bound, not any other part of the expansion. -/ theorem faithful_matches_bounded_forall : diff --git a/lean/Thermite/PinCombPredGap.lean b/lean/Thermite/PinCombPredGap.lean index f26569635..58945062c 100644 --- a/lean/Thermite/PinCombPredGap.lean +++ b/lean/Thermite/PinCombPredGap.lean @@ -146,9 +146,9 @@ theorem divergent_registry_fails_the_hypothesis : ¬ RegistryTerminating envD eD rw [eDeep_NB_none N] at this exact absurd this (by simp) -/-! ## F.+ — the over-rejection guard: a genuine registry of the same shape converges -/ +/-! ## F.+ — the over-rejection guard: a registry of the same shape converges -/ -/-- A genuine (terminating) registry: `g(x) = 1` — the same eDeep shape, but the spec-fn +/-- A (terminating) registry: `g(x) = 1` — the same eDeep shape, but the spec-fn returns a constant instead of diverging. -/ def Rgen : Registry := fun n => if n = "f" then some ⟨["x"], Expr.intLit 1⟩ else none @@ -159,7 +159,7 @@ def envG : Env := optres := fun _ => OptResVal.none_ specs := Rgen } -/-- The genuine call resolves to `some 1` at every positive fuel (`intValNB`). -/ +/-- The call resolves to `some 1` at every positive fuel (`intValNB`). -/ theorem genuine_call_NB_one : ∀ fuel (env : Env), env.specs = Rgen → intValNB (fuel + 1) fCall env = some 1 := by intro fuel env h @@ -168,12 +168,12 @@ theorem genuine_call_NB_one : = some [env.ints "x"] from by simp only [intValArgsNB, intValNB, Option.bind]] simp only [Option.bind] -/-- The predicate body `f(x) > 0` denotes `some (1 > 0)` under the genuine registry. -/ +/-- The predicate body `f(x) > 0` denotes `some (1 > 0)` under the registry. -/ theorem pBody_NB_genuine (fuel : Nat) (env : Env) (h : env.specs = Rgen) : denoteNB (fuel + 1) pBody env = some ((1 : Int) > 0) := by simp only [pBody, denoteNB, genuine_call_NB_one fuel env h, Option.bind, intValNB] -/-- The inner `forall_in` NB-denotes to `some` of an explicit genuine proposition at +/-- The inner `forall_in` NB-denotes to `some` of an explicit proposition at positive fuel; the predicate gate now succeeds (`some`) because the genuine call resolves. The carried proposition is the spine `denote` form (agreement is reflexive). -/ theorem forall_NB_genuine_eq (fuel : Nat) (env : Env) (h : env.specs = Rgen) @@ -186,7 +186,7 @@ theorem forall_NB_genuine_eq (fuel : Nat) (env : Env) (h : env.specs = Rgen) rw [pBody_NB_genuine fuel (env.bindInt "y" 0) (by rw [Env.bindInt]; exact h)] simp only [predGateNB] -/-- The spine `forall_in(s, |y| f(x) > 0)` denotes true under the genuine registry: the +/-- The spine `forall_in(s, |y| f(x) > 0)` denotes true under the registry: the body is `1 > 0` at the (single) bound element, which holds. -/ theorem forall_denote_genuine (fuel : Nat) (env : Env) (h : env.specs = Rgen) (hs : env.seqs "s" = [0]) : @@ -200,9 +200,9 @@ theorem forall_denote_genuine (fuel : Nat) (env : Env) (h : env.specs = Rgen) simp only [fCall, intVal, h, Rgen, if_pos, Env.bindInt, intValArgs]] simp only [intVal]; omega -/-- The pin (F.+, the over-rejection guard): the same eDeep shape under the genuine +/-- The pin (F.+, the over-rejection guard): the same eDeep shape under the registry `g(x) = 1` reaches a value under `intValNB` at positive fuel, so - `RegistryTerminating envG eDeep` holds and `stabilization_exists` delivers a genuine + `RegistryTerminating envG eDeep` holds and `stabilization_exists` delivers a stabilized value. The #242 gate rejects divergence, not every predicate-body spec-call (the #241 precedent: a fix that rejected everything would be unsound the other way). The convergent value is the genuine count `1` (the single element's @@ -222,7 +222,7 @@ theorem genuine_registry_satisfies_the_hypothesis : RegistryTerminating envG eDe (by rw [Env.bindInt]; rfl))] simp only [Option.some.injEq]; omega -/-- `stabilization_exists` is dischargeable on the genuine registry, delivering a genuine - stabilized value — the resolved-side counterpart to F.2's rejection of divergence. -/ +/-- `stabilization_exists` is dischargeable on the registry and produces a + stabilized value, the resolved-side counterpart to F.2's rejection of divergence. -/ theorem genuine_stabilization_exists : ∃ v, stabilizes eDeep envG v := stabilization_exists genuine_registry_satisfies_the_hypothesis diff --git a/lean/Thermite/PinExecStateMisMap.lean b/lean/Thermite/PinExecStateMisMap.lean index 357d7fff7..d3ea7de3c 100644 --- a/lean/Thermite/PinExecStateMisMap.lean +++ b/lean/Thermite/PinExecStateMisMap.lean @@ -54,7 +54,7 @@ def stateOfDropped : State := /-- The exec body `{ xs[0] }` (index element 0 of the slice param `xs`). -/ def idxBody : Block := .mk [] (some (.index "xs" (.intLit .u64 0))) -/-- Faithful — the body converges to the genuine element `7`. With the slice mapped, +/-- Faithful — the body converges to the element `7`. With the slice mapped, `xs[0]` reads element `0` of `[⟨u32,7⟩]` = `7`, so `bodyConverges` holds. -/ theorem faithful_body_converges : bodyConverges idxBody stateOfFaithful (.int ⟨.u32, 7⟩) := by diff --git a/lean/Thermite/PinFiniteEscape.lean b/lean/Thermite/PinFiniteEscape.lean index ff420d57c..86775bc6b 100644 --- a/lean/Thermite/PinFiniteEscape.lean +++ b/lean/Thermite/PinFiniteEscape.lean @@ -33,7 +33,7 @@ inductive Two where | t1 : Two deriving DecidableEq, Repr -/-- The genuine carrier: its `enum` lists BOTH elements, and `complete` is +/-- The carrier: its `enum` lists both elements, and `complete` is discharged by `decide` — the hand-rolled finiteness witness. -/ def twoCarrier : CarrierAssign where C := Two @@ -67,14 +67,14 @@ theorem finiteEscape_pinned : incompleteEnum.all isT0 = true ∧ twoCarrier.enum.all isT0 = false := by decide -/-- The escape, stated against the genuine `∀`: the incomplete fold certifies +/-- The escape, stated against the `∀`: the incomplete fold certifies `true` even though `∀ x, isT0 x` is FALSE (it fails at `t1`). -/ theorem incompleteEnum_escapes : incompleteEnum.all isT0 = true ∧ ¬ (∀ x : Two, isT0 x = true) := by refine ⟨by decide, ?_⟩ intro h; have := h Two.t1; revert this; decide -/-! ## Tied back to `sdenote` on the genuine carrier +/-! ## Tied back to `sdenote` on the carrier On the real (complete) carrier, the `all` fold and the genuine `∀` agree — both `false` for the false claim `∀ x, x = ρ(0)`. `sdenote_all_iff` is the diff --git a/lean/Thermite/PinNNFPolarity.lean b/lean/Thermite/PinNNFPolarity.lean index cfdf6e167..746ddecc1 100644 --- a/lean/Thermite/PinNNFPolarity.lean +++ b/lean/Thermite/PinNNFPolarity.lean @@ -49,7 +49,7 @@ def body1 : Frm := .atom (.rel .eq (.var valueS 0) (.var keyS 1)) /-- `body₂` under `∃v. ∀k.`: de Bruijn `k = 0`, `v = 1`. -/ def body2 : Frm := .atom (.rel .eq (.var keyS 0) (.var valueS 1)) -/-- `(¬∃k:Key. ∀v:Value. body₁) ∧ (¬∃v:Value. ∀k:Key. body₂)`. RAW polarity hides the +/-- `(¬∃k:Key. ∀v:Value. body₁) ∧ (¬∃v:Value. ∀k:Key. body₂)`. raw polarity hides the alternation; NNF reveals the `Key ⇄ Value` cycle. -/ def phiHiddenCycle : Frm := .conj (.neg (.ex keyS (.all valueS body1))) @@ -57,13 +57,13 @@ def phiHiddenCycle : Frm := /-! ## The pin -/ -/-- The concrete counterexample: the pre-NNF classifier ADMITS the hidden-cycle formula +/-- The concrete counterexample: the pre-NNF classifier admits the hidden-cycle formula (its raw graph is empty → acyclic), while the real `admitted` REJECTS it (the post-NNF graph has the `Key ⇄ Value` alternation cycle). -/ theorem nnfPolarity_counterexample : admittedPreNnf phiHiddenCycle = true ∧ admitted phiHiddenCycle = false := by decide -/-- The pin: admission is UNSOUND for the pre-NNF classifier — it accepts a formula +/-- The pin: admission is unsound for the pre-NNF classifier — it accepts a formula (`phiHiddenCycle`) the real `admitted` rejects, because it reads quantifier polarity syntactically rather than after NNF. Computing acyclicity before NNF is therefore not a safe refactor — NNF is load-bearing. -/ diff --git a/lean/Thermite/PinRegistryTerminating.lean b/lean/Thermite/PinRegistryTerminating.lean index c3fa97ac4..6a3043e48 100644 --- a/lean/Thermite/PinRegistryTerminating.lean +++ b/lean/Thermite/PinRegistryTerminating.lean @@ -83,7 +83,7 @@ theorem divergent_call_NB_is_none : simp only [Option.bind] exact ih _ (by simp [Env.bindParams, Env.bindInt, h]) -/-! ## E.2 — the divergent registry fails the new hypothesis (the load-bearing +/-! ## E.2 — the divergent registry fails the new hypothesis (the required reversal: the cycle-4 divergence is closed). -/ /-- The pin (E.2): the divergent registry does not satisfy `RegistryTerminating`; @@ -117,7 +117,7 @@ theorem stabilization_exists_unreachable_on_divergence : rintro ⟨h, _⟩ exact divergent_registry_fails_the_hypothesis h -/-! ## E.4 — a genuine (convergent) registry still discharges (no over-rejection): +/-! ## E.4 — a (convergent) registry still discharges (no over-rejection): `g(x) = 5` converges to 5 and stabilizes to 5. -/ def Rgood : Registry := fun n => @@ -131,7 +131,7 @@ def envG : Env := def gCall : Expr := Expr.specCall "g" [Expr.var "x"] -/-- The genuine call's none-propagating denotation reaches `some 5` at every fuel ≥ 1 +/-- The call's none-propagating denotation reaches `some 5` at every fuel ≥ 1 (the body `5` is spec-call-free, so it converges immediately on resolution). -/ theorem genuine_call_NB (fuel : Nat) : intValNB (fuel + 1) gCall envG = some 5 := by @@ -141,20 +141,20 @@ theorem genuine_call_NB (fuel : Nat) : simp only [envG, Rgood, if_pos] simp only [gCall, intValNB, hres, hargs, Option.bind, intValNB] -/-- The pin (E.4a): the genuine registry converges to 5; the fix did not +/-- The pin (E.4a): the registry converges to 5; the fix did not over-reject, and a real dec-valid item still supplies `RegistryTerminating`. -/ theorem genuine_registry_converges : Converges gCall envG 5 := ⟨1, fun fuel hfuel => by obtain ⟨k, rfl⟩ := Nat.exists_eq_add_of_le hfuel rw [Nat.add_comm]; exact genuine_call_NB k⟩ -/-- The pin (E.4b): so the genuine registry satisfies `RegistryTerminating`, and +/-- The pin (E.4b): so the registry satisfies `RegistryTerminating`, and `stabilization_exists` discharges it to a genuine stabilized value (5, by the agreement lemma) rather than a bottom-poisoned artifact. -/ theorem genuine_registry_stabilizes : stabilizes gCall envG 5 := converges_imp_stabilizes genuine_registry_converges -/-- The pin (E.4c): the genuine value is 5, by uniqueness; the contract +/-- The pin (E.4c): the value is 5, by uniqueness; the contract `ens: result == g(x)` is now about 5, not a bottom. -/ theorem genuine_registry_value_is_five : ∀ v, stabilizes gCall envG v → v = 5 := fun _ hv => diff --git a/lean/Thermite/PinRelaxRefute.lean b/lean/Thermite/PinRelaxRefute.lean index 7289262f8..3973b1aae 100644 --- a/lean/Thermite/PinRelaxRefute.lean +++ b/lean/Thermite/PinRelaxRefute.lean @@ -52,14 +52,14 @@ theorem int_clause_holds (a : Unit → ℤ) : 0 ≤ ePoly.eval a := by exact mul_nonneg (by omega) (by omega) · exact mul_nonneg (by omega) (by omega) -/-- The real relaxation FAILS: at `x = 1/2`, `e = (1/2)² − 1/2 = −1/4 < 0`. -/ +/-- The real relaxation fails: at `x = 1/2`, `e = (1/2)² − 1/2 = −1/4 < 0`. -/ theorem real_relax_fails : ¬ ∀ x : Unit → ℝ, 0 ≤ ePoly.eval x := by intro h have hx := h (fun _ => (1 : ℝ) / 2) simp only [ePoly, PExpr.eval] at hx norm_num at hx -/-- The pin: the CONVERSE of `r_relax_sound` is FALSE. A failed real relaxation does NOT +/-- The pin: the CONVERSE of `r_relax_sound` is false. A failed real relaxation does NOT imply a failed integer clause — `ePoly` is valid over ℤ yet its relaxation fails over ℝ. So a relax route that read "real relaxation failed" as "clause false / emit a Counterexample" would be unsound; the integrality check + `RealWitness` escalation @@ -70,7 +70,7 @@ theorem relax_converse_unsound : intro h exact h ePoly real_relax_fails int_clause_holds -/-- For contrast: the SOUND direction `r_relax_sound` holds vacuously here — `ePoly`'s +/-- For contrast: the sound direction `r_relax_sound` holds vacuously here — `ePoly`'s relaxation does not hold, so its hypothesis is unmet, confirming the gap is solely the illegitimate converse. (Stated as the instantiated implication.) -/ theorem r_relax_sound_on_ePoly (a : Unit → ℤ) : diff --git a/lean/Thermite/PinRestratDropSide.lean b/lean/Thermite/PinRestratDropSide.lean index f9b005865..037dbd9a2 100644 --- a/lean/Thermite/PinRestratDropSide.lean +++ b/lean/Thermite/PinRestratDropSide.lean @@ -46,12 +46,12 @@ def ρ0 : Subst := fun _ => .lit usizeS /-- `A` — a true sub-formula (`boolLit true`, read as `true` by `qBool`). -/ def aT : Frm := .atom (.qfree (.boolLit true)) -/-- `B` — the cycle-closing conjunct, FALSE in this model (`boolLit false`). -/ +/-- `B` — the cycle-closing conjunct, false in this model (`boolLit false`). -/ def bF : Frm := .atom (.qfree (.boolLit false)) -/-- The fresh restratify abstraction token `p` — a DISTINCT leaf, `true` in this model. -/ +/-- The fresh restratify abstraction token `p` — a distinct leaf, `true` in this model. -/ def pAbs : Thermite.Expr := .boolLit true -/-- The original (would-be-restratified) formula `φ = A ∧ B`: TRUE ∧ FALSE = FALSE. -/ +/-- The original (would-be-restratified) formula `φ = A ∧ B`: true ∧ false = false. -/ def phi : Frm := .conj aT bF /-! ## The pin -/ @@ -63,7 +63,7 @@ theorem dropSide_counterexample : fdenote qBool dom (restrat pAbs phi) ρ0 = true ∧ fdenote qBool dom phi ρ0 = false := by decide -/-- The pin: with `Side` DROPPED, "φ' certified ⇒ φ certified" is UNSOUND — there is a +/-- The pin: with `Side` dropped, "φ' certified ⇒ φ certified" is unsound — there is a model where φ' holds but φ does not. -/ theorem dropSide_breaks_certification : ¬ ∀ (A B : Frm), @@ -75,13 +75,13 @@ theorem dropSide_breaks_certification : have hf : fdenote qBool dom (aT.conj bF) ρ0 = false := dropSide_counterexample.2 exact absurd (hc.symm.trans hf) (by decide) -/-- The contrast: under the SAME model the genuine `Side(φ', φ) = p ⇒ B` is FALSE +/-- The contrast: under the SAME model the `Side(φ', φ) = p ⇒ B` is false (`true ⇒ false`), so `restrat_conservative`'s `Side` hypothesis is UNMET — the real discipline does not fire, and the unsound step is blocked exactly by R-SIDE-1. -/ theorem side_is_false_here : fdenote qBool dom (Side pAbs phi) ρ0 = false := by decide -/-- And WITH the discharged `Side`, `restrat_conservative` would correctly require it — +/-- And with the discharged `Side`, `restrat_conservative` would correctly require it — here `Side` is false, so the (sound) certificate of φ is correctly WITHHELD. The pin therefore shows the gap is solely the dropped obligation. -/ theorem conservative_withholds_without_side : diff --git a/lean/Thermite/PinStabilization.lean b/lean/Thermite/PinStabilization.lean index 93fe272f2..e5ba23de8 100644 --- a/lean/Thermite/PinStabilization.lean +++ b/lean/Thermite/PinStabilization.lean @@ -81,7 +81,7 @@ theorem body_stabilizes_to_5 : stabilizes fCall envA 5 := by theorem rbody_at_hint_fuel_is_bottom : intVal 1 fCall envA = 0 := by simp [fCall, intVal, intValArgs, envA, Ra, Env.bindParams, Env.bindInt] -/-- `ens: result == 0` — a contract the real item (result = 5) violates. -/ +/-- `ens: result == 0` — a contract the item (result = 5) violates. -/ def ensWrong : Expr := Expr.cmp CmpOp.eq (Expr.var "result") (Expr.intLit 0) /-- The pin (A): the §4 displayed obligation, with `rbody` rendered via the only diff --git a/lean/Thermite/PinStratCapture.lean b/lean/Thermite/PinStratCapture.lean index d3efec977..f0d8b6035 100644 --- a/lean/Thermite/PinStratCapture.lean +++ b/lean/Thermite/PinStratCapture.lean @@ -64,7 +64,7 @@ def encAtomCap : Atom → Atom | .rel ρ t u => .rel ρ (encTmCap t) (encTmCap u) | .qfree e => .qfree e -/-- The capture-broken formula encoder: every binder REUSES the name `0` (no +/-- The capture-broken formula encoder: every binder reuses the name `0` (no fresh-name discipline), so a nested binder shadows its parent. -/ def sencodeCap : Frm → Tok | .atom a => .atom (encAtomCap a) @@ -75,7 +75,7 @@ def sencodeCap : Frm → Tok | .all s φ => .all s 0 true (sencodeCap φ) -- BUG: name 0, not the fresh level | .ex s φ => .ex s 0 true (sencodeCap φ) -/-- The witness sentence `∀x:usize. ∃y:usize. x = c0` — depends only on the OUTER +/-- The witness sentence `∀x:usize. ∃y:usize. x = c0` — depends only on the outer `x` (de Bruijn index 1 under the two binders); FALSE over `dom` (`c1 ≠ c0`). -/ def phiCap : Frm := .all usizeS (.ex usizeS (.atom (.rel .eq (.var usizeS 1) c0))) @@ -87,7 +87,7 @@ theorem capture_counterexample : tokDenote qEq dom (sencodeCap phiCap) σ0 = true ∧ fdenote qEq dom phiCap σ0 = false := by decide -/-- The pin: encoder soundness is FALSE for the capturing encoder — there is a +/-- The pin: encoder soundness is false for the capturing encoder — there is a formula on a concrete domain where its token disagrees with the source. -/ theorem capture_breaks_soundness : ¬ ∀ (φ : Frm) (σ ρ : Subst), diff --git a/lean/Thermite/PinStratFlip.lean b/lean/Thermite/PinStratFlip.lean index ae2862ae4..5099424f8 100644 --- a/lean/Thermite/PinStratFlip.lean +++ b/lean/Thermite/PinStratFlip.lean @@ -57,7 +57,7 @@ def sencodeFlipAt (d : Nat) : Frm → Tok def sencodeFlip (φ : Frm) : Tok := sencodeFlipAt 0 φ -/-- The witness sentence `∃x:usize. x = c0` — TRUE over `dom` (`c0 ∈ dom`). -/ +/-- The witness sentence `∃x:usize. x = c0` — true over `dom` (`c0 ∈ dom`). -/ def phiFlip : Frm := .ex usizeS (.atom (.rel .eq (.var usizeS 0) c0)) /-! ## The pin -/ @@ -68,7 +68,7 @@ theorem flip_counterexample : tokDenote qEq dom (sencodeFlip phiFlip) σ0 = false ∧ fdenote qEq dom phiFlip σ0 = true := by decide -/-- The pin: encoder soundness is FALSE for the flipped encoder — there is a +/-- The pin: encoder soundness is false for the flipped encoder — there is a formula on a concrete domain where its token disagrees with the source. -/ theorem flip_breaks_soundness : ¬ ∀ (φ : Frm) (σ ρ : Subst), diff --git a/lean/Thermite/PinStratSelfLoop.lean b/lean/Thermite/PinStratSelfLoop.lean index 123f12e75..bd40224f6 100644 --- a/lean/Thermite/PinStratSelfLoop.lean +++ b/lean/Thermite/PinStratSelfLoop.lean @@ -44,13 +44,13 @@ def admittedNoSelf (φ : Frm) : Bool := /-! ## The pin -/ -/-- The concrete counterexample: the strip-self classifier ADMITS the `a[a[i]]` +/-- The concrete counterexample: the strip-self classifier admits the `a[a[i]]` self-loop (`ex_selfLoop`), while the real `admitted` REJECTS it — the self-edge, which `stripSelf` discards, is the entire reason for the rejection. -/ theorem selfLoop_counterexample : admittedNoSelf ex_selfLoop = true ∧ admitted ex_selfLoop = false := by decide -/-- The pin: admission is UNSOUND for the strip-self classifier — it accepts a formula +/-- The pin: admission is unsound for the strip-self classifier — it accepts a formula (`ex_selfLoop`) that the real `admitted` rejects, so the two classifiers disagree. Stripping reflexive edges is therefore not a safe refactor of `acyclic`. -/ theorem stripSelf_breaks_admission : diff --git a/lean/Thermite/PinWhileComposition.lean b/lean/Thermite/PinWhileComposition.lean index a06a39e07..ebdf5bc4d 100644 --- a/lean/Thermite/PinWhileComposition.lean +++ b/lean/Thermite/PinWhileComposition.lean @@ -107,7 +107,7 @@ theorem skip_composition_certifies_wrong_ens : refine ⟨skip_reads_entry, ?_⟩ simp only [ensWrong, denote, intVal, bindResult, baseEnv, Env.bindInt, if_pos] -/-- Direction 2 (the faithful refutation): the genuine composition refutes the wrong +/-- Direction 2 (the faithful refutation): the composition refutes the wrong `ens`. The faithful `whileBodyConverges` binds the exit value `result = 3` (`faithful_whileBodyConverges`), at which `ens: result == 0` denotes false (`3 ≠ 0`). The faithful loop-exit value is the one the contract is checked against (`while_compose` diff --git a/lean/Thermite/PinWhileDecShape.lean b/lean/Thermite/PinWhileDecShape.lean index cabcb62f2..db0139044 100644 --- a/lean/Thermite/PinWhileDecShape.lean +++ b/lean/Thermite/PinWhileDecShape.lean @@ -87,7 +87,7 @@ theorem cond_true_at_s0 : condBool l1Cond s0 = some true := by simp only [condBool, l1Cond, s0, execDenote, asInt, cmpVal, bind, Option.bind] decide -/-- The genuine `lo = lo + 1` step from `s0` lands at a state whose `lo` cell is `1` +/-- The `lo = lo + 1` step from `s0` lands at a state whose `lo` cell is `1` (the concrete decode, the `Exec/Loop.lean` L2-witness pattern). -/ theorem step_lo_cell : (blockThread l1Body s0).map (fun s => s.env.vars "lo") = some (.int ⟨.usize, 1⟩) := by diff --git a/lean/Thermite/SmtExport.lean b/lean/Thermite/SmtExport.lean index 4e57c1e0c..abd45efdb 100644 --- a/lean/Thermite/SmtExport.lean +++ b/lean/Thermite/SmtExport.lean @@ -3,18 +3,14 @@ (`.design/stage3-bv-reconstruction.md` REQ-7 / AC-8). DO NOT EDIT BY HAND. Each theorem is a per-clause translation-validation obligation `(P_prod) ⟺ (P_ref)` - emitted by the automated Rust→Lean exporter — the step `Thermite.SmtDemo` performed - by hand. It is discharged by the lean-smt `smt` tactic (cvc5 reconstruction, pinned - @ 7d1d8239) and KERNEL-CHECKED; the `#print axioms` after each must report a subset - of {propext, Classical.choice, Quot.sound} (no Smt oracle, no sorryAx, no - Lean.ofReduceBool) for the fragment to count as reconstruction-supported. - - QF_BV clauses are rendered over the range-bounded integer machine-model (bv var -> - Int with 0 ≤ x < 2^N, wraparound op -> `% 2^N`, unsigned cmp -> Int cmp): lean-smt's - literal BitVec reconstruction bit-blasts through an upstream `sorry` - (.design/verified/z3-demotion.md), so the integer model is the reconstruction- - supported QF_BV encoding. Regenerate via the `golden_file_matches_exporter` test - with THERMITE_REGEN_SMT_EXPORT=1. + emitted by the automated Rust→Lean exporter. QF_LIA uses lean-smt/cvc5. QF_BV is + rendered directly as `BitVec N` and proved from kernel-checked normalization lemmas. + The `#print axioms` after each theorem must report a subset of + {propext, Classical.choice, Quot.sound}. + + The literal QF_BV renderer covers wrapping arithmetic, unsigned comparisons, + bitwise operations, shifts, unsigned division, and remainder. Regenerate via the + `golden_file_matches_exporter` test with THERMITE_REGEN_SMT_EXPORT=1. -/ import Smt @@ -24,12 +20,19 @@ theorem thermite_smt_lia_arith_cmp (a b c : Int) : ((a - b) ≤ c) ↔ (¬ (c < (a - b))) := by smt #print axioms thermite_smt_lia_arith_cmp -theorem thermite_smt_bv64_le_not_lt (a b : Int) (h0lo : 0 ≤ a) (h0hi : a < 18446744073709551616) (h1lo : 0 ≤ b) (h1hi : b < 18446744073709551616) : - (a ≤ b) ↔ (¬ (b < a)) := by smt [h0lo, h0hi, h1lo, h1hi] +theorem thermite_smt_bv64_le_not_lt (a b : BitVec 64) : + (a ≤ b) ↔ (¬ (b < a)) := by + simp #print axioms thermite_smt_bv64_le_not_lt -theorem thermite_smt_bv8_add_comm (a b c : Int) (h0lo : 0 ≤ a) (h0hi : a < 256) (h1lo : 0 ≤ b) (h1hi : b < 256) (h2lo : 0 ≤ c) (h2hi : c < 256) : - (((a + b) % 256) = c) ↔ (((b + a) % 256) = c) := by smt [h0lo, h0hi, h1lo, h1hi, h2lo, h2hi] +theorem thermite_smt_bv8_add_comm (a b c : BitVec 8) : + ((a + b) = c) ↔ ((b + a) = c) := by + simp [BitVec.add_comm] #print axioms thermite_smt_bv8_add_comm +theorem thermite_smt_bv8_full_terms (a b c : BitVec 8) : + (((if c = (0#8) then (~~~(0#8)) else ((((((~~~a) &&& b) ||| (a ^^^ b)) <<< c) >>> b) / c)) % b) ≠ ((a * b) + c)) ↔ (¬ (((if c = (0#8) then (~~~(0#8)) else ((((((~~~a) &&& b) ||| (a ^^^ b)) <<< c) >>> b) / c)) % b) = (c + (b * a)))) := by + simp [BitVec.add_comm, BitVec.mul_comm] +#print axioms thermite_smt_bv8_full_terms + end Thermite.SmtExport diff --git a/lean/Thermite/Soundness.lean b/lean/Thermite/Soundness.lean index 0d112cbf5..c26de1737 100644 --- a/lean/Thermite/Soundness.lean +++ b/lean/Thermite/Soundness.lean @@ -227,9 +227,9 @@ theorem refIntValArgs_eq : ∀ (fuel : Nat) (args : List Expr) (env : Env), and the integer-operand equality `refIntVal_eq_intVal`, which itself carries the #176 arithmetic round-trip, the #177 cast round-trip, and the #178 `@`-view/`subrange`/ byte-view rewrites (via `refVal_eq`), not a definitional collapse. See - `eq_le_infidelity_*` (the `==`-vs-`<=` teeth), `cast_paren_drop_breaks_soundness` (the - #122/#146 cast-paren teeth), and `byteview_misdispatch_breaks_soundness` (the #127 - byte-view-dispatch teeth) below. + `eq_le_infidelity_*` (the `==`-versus-`<=` case), + `cast_paren_drop_breaks_soundness` (the #122/#146 cast-parentheses case), and + `byteview_misdispatch_breaks_soundness` (the #127 byte-view dispatch case) below. -/ theorem ref_sound : ∀ (fuel : Nat) (e : Expr) (env : Env), refDenote fuel e env ↔ denote fuel e env -- `Expr` is mutually inductive (with `RangeArg`/`MatchArm`), and #181 adds a `specCall` that @@ -379,8 +379,8 @@ theorem ref_sound_arms : ∀ (fuel : Nat) (scrut : OptResVal) (arms : List Match | (apply Prod.Lex.right; omega) end -/-- The integer-term meanings coincide (the projection of `refVal_eq` used by the teeth/positive - lemmas below). -/ +/-- The integer-term meanings coincide (the projection of `refVal_eq` used by the + negative and positive lemmas below). -/ theorem refIntVal_eq_intVal (fuel : Nat) (e : Expr) (env : Env) : refIntVal fuel e env = intVal fuel e env := (refVal_eq fuel e env).1 @@ -393,9 +393,9 @@ theorem refSeqVal_eq_seqVal (fuel : Nat) (e : Expr) (env : Env) : theorem ref_sound_eq (fuel : Nat) (e : Expr) (env : Env) : refDenote fuel e env = denote fuel e env := propext (ref_sound fuel e env) -/-! ## Negative sanity lemma 1 — the comparison teeth (`==` ≠ `<=`) +/-! ## Negative sanity lemma 1: `==` encoded as `<=` - The #170 teeth, retained: an encoder that mapped `Eq → "<="` (the + The #170 regression case: an encoder that mapped `Eq → "<="` (the `==`-vs-`<=` infidelity) would not satisfy soundness at a concrete `env`. -/ /-- A faulty encoder operator map: `Eq` mis-mapped to the `<=` token (the @@ -416,7 +416,7 @@ def refDenoteFaultyCmp (fuel : Nat) (op : CmpOp) (a b : Expr) (env : Env) : Prop /-- A concrete environment: integer names `a := 1`, `b := 2`, `n := -1` (everything else `0`); sequence name `s := [10, 20, 30]` (a `String`'s bytes; everything else - the empty sequence) — the witness sequence for the #127 byte-view-dispatch teeth + the empty sequence). This is the witness sequence for the #127 byte-view-dispatch case (its bytes differ at adjacent indices, so a wrong index / wrong method is observable). -/ def envAB : Env := { ints := fun s => if s = "a" then 1 else if s = "b" then 2 @@ -426,7 +426,7 @@ def envAB : Env := -- a `Some`-valued result carrying the integer payload 7; everything else `None`). optres := fun s => if s = "result" then OptResVal.some_ 7 else OptResVal.none_ -- The spec-fn registry slot (#181): `envAB` carries no spec fn (the comparison/cast/byte-view/ - -- combinator/match teeth do not call one). The #181 spec-fn teeth use `envSpec` below. + -- combinator and match cases do not call one). The #181 spec-fn cases use `envSpec` below. specs := fun _ => none } /-- **Teeth (negative sanity, the `==`-vs-`<=` case, #170).** At `envAB` the faulty @@ -438,7 +438,7 @@ theorem eq_le_infidelity_breaks_soundness : ↔ denote 0 (Expr.cmp CmpOp.eq (Expr.var "a") (Expr.var "b")) envAB) := by simp [refDenoteFaultyCmp, encOpFaulty, tokRel, denote, intVal, refIntVal, envAB] -/-! ## Negative sanity lemma 2 — the #122/#146 cast-paren teeth (the retired class) +/-! ## Negative sanity lemma 2: dropped cast parentheses (#122/#146) The dispatch's explicit requirement: demonstrate that an encoder that drops the cast paren — emitting `(n - 1) as nat` as the bare `n - 1 as nat`, which Verus/Rust @@ -484,23 +484,22 @@ theorem cast_paren_drop_breaks_soundness : simp [castInnerFaithful, castInnerParenDropped, refIntVal, tokCast, tokArith, encCast, encArith, castDenote, arithDenote, envAB] -/-- The faithful counterpart, for contrast: with the real `refIntVal` (the +/-- With the faithful `refIntVal` (the parenthesized cast) the `(n - 1) as nat` clause is sound — it equals the source - `intVal` (the whole-inner cast), by `refIntVal_eq_intVal`. Confirms the teeth bite - only the paren-drop, not the faithful encoder. -/ + `intVal` (the whole-inner cast), by `refIntVal_eq_intVal`. -/ theorem cast_faithful_intval_matches_source : refIntVal 0 (Expr.cast (Expr.arith ArithOp.sub (Expr.var "n") (Expr.intLit 1)) CastTy.nat) envAB = intVal 0 (Expr.cast (Expr.arith ArithOp.sub (Expr.var "n") (Expr.intLit 1)) CastTy.nat) envAB := refIntVal_eq_intVal _ _ _ -/-- The faithful counterpart for the comparison teeth, retained from #170: with the - real `encOp` the `a == b` clause is sound (both `1 = 2`, false), by `ref_sound`. -/ +/-- The faithful comparison case retained from #170: with `encOp`, the `a == b` + clause is sound (both `1 = 2`, false), by `ref_sound`. -/ theorem eq_faithful_is_sound : refDenote 0 (Expr.cmp CmpOp.eq (Expr.var "a") (Expr.var "b")) envAB ↔ denote 0 (Expr.cmp CmpOp.eq (Expr.var "a") (Expr.var "b")) envAB := ref_sound _ _ _ -/-! ## Negative sanity lemma 3 — the #127 byte-view-dispatch teeth (the retired class) +/-! ## Negative sanity lemma 3: byte-view misdispatch (#127) The dispatch's explicit requirement (the #178 point): demonstrate that an encoder that mis-dispatches the byte-view — the #127 name-collision bug, where the encoder @@ -563,10 +562,9 @@ theorem byteview_misdispatch_breaks_soundness : simp [byteAtFaithful, byteAtWrongMethod, refIntVal, refSeqVal, byteView, seqIdx, encByteAt, encLen, envAB] -/-- The faithful counterpart, for contrast: with the real byte-view dispatch the +/-- With the faithful byte-view dispatch, the `s.byte_at(0)` clause is sound — its encoder meaning equals the source `intVal` - (the 0-th byte), by `refIntVal_eq_intVal`. Confirms the teeth bite only the - misdispatch, not the faithful encoder. -/ + (the 0-th byte), by `refIntVal_eq_intVal`. -/ theorem byteat_faithful_intval_matches_source : refIntVal 0 (Expr.byteAt (Expr.strVar "s") (Expr.intLit 0)) envAB = intVal 0 (Expr.byteAt (Expr.strVar "s") (Expr.intLit 0)) envAB := @@ -586,15 +584,15 @@ theorem subrange_index_faithful_matches_source : (Expr.intLit 1)) envAB := refIntVal_eq_intVal _ _ _ -/-! ## Negative sanity lemma 4 — the wrong-combinator teeth (#179) +/-! ## Negative sanity lemma 4: wrong combinator (#179) The dispatch's explicit requirement (a): demonstrate that an encoder that emitted the wrong combinator — `forall_in` (a bounded `∀`) lowered as `exists_in` (a bounded `∃`) — does not satisfy soundness at a concrete sequence. The two quantifier forms differ (`∀ i, .. → p(s[i])` vs `∃ i, .. ∧ p(s[i])`) precisely when some element satisfies the - predicate and some does not. This is the combinator analogue of the `==`-vs-`<=` teeth: + predicate and some does not. As in the `==`-versus-`<=` case, the encoder's choice of which frozen `verus_l3` quantifier (`encode_call`'s - `lookup(name)` dispatch, referencing the right combinator) is load-bearing. + `lookup(name)` dispatch must reference the right combinator. Source clause: `forall_in(s, |x| x ≤ 15)`, i.e. `Expr.comb forallIn (strVar "s") none none (some (Pred.mk "x" (x ≤ 15)))`. @@ -643,7 +641,7 @@ theorem wrong_combinator_breaks_soundness : simpa [predLe15Body, denote, intVal, Env.bindInt, seqVal] using this simp [seqIdx, envAB] at hAt1 -/-! ## Negative sanity lemma 5 — the #145 arg-kind teeth (the retired class) +/-! ## Negative sanity lemma 5: wrong argument kind (#145) The dispatch's explicit requirement (b): demonstrate the #145 (`divergence_index_ combinator`) bug — `forall_below`/`forall_from`'s `ArgKind::Index` bound `n` (a scalar @@ -665,7 +663,7 @@ theorem wrong_combinator_breaks_soundness : threading `ArgKind::Index` as a scalar (not a `@`-view) is what `ref_sound`'s `comb` case pins (its `forallBelow` arm uses `refIntVal_eq_intVal` on the scalar index). -/ -/-- A concrete env for the #145 teeth: the index var `n` is the scalar `1`, while `n`'s +/-- A concrete environment for #145: the index variable `n` is the scalar `1`, while `n`'s slice `@`-view (the buggy reading) is `[10, 20, 30]` (length `3`); `s := [10, 20, 30]`. The scalar value (1) and the view-length (3) differ, so a slice-`@`-viewed index is observable. -/ @@ -722,8 +720,7 @@ theorem index_argkind_slice_view_breaks_soundness : /-- The faithful positive counterpart, for contrast: with the real combinator dispatch + the scalar index threading the `forall_below(s, n, |x| x ≤ 15)` clause is sound — its - encoder meaning is equivalent to the source, by `ref_sound`. Confirms the #179/#145 - teeth bite only the wrong-combinator / slice-viewed-index, not the faithful encoder. -/ + encoder meaning is equivalent to the source, by `ref_sound`. -/ theorem forall_below_faithful_is_sound : refDenote 0 (Expr.comb CombName.forallBelow (Expr.strVar "s") @@ -733,14 +730,13 @@ theorem forall_below_faithful_is_sound : none (some (Expr.var "n")) (some predLe15)) envIdx := ref_sound _ _ _ -/-! ## Negative sanity lemma 6 — the #180 match-arm-swap teeth (the C7 match-in-ens class) +/-! ## Negative sanity lemma 6: swapped match arms (#180) The dispatch's explicit requirement (a): demonstrate that an encoder that swapped the match arm bodies (the `Some`/`None` bodies exchanged — `encode_match` emitting each arm's body under the wrong pattern) does not satisfy soundness at a concrete `OptResVal`. This is the - match-in-ens analogue of the `==`-vs-`<=` / wrong-combinator teeth: which arm body goes under - which pattern (`encode_match` pairing `encode_pattern(arm.pattern)` with `encode(arm.body)`) is - load-bearing. + match-in-ens analogue of the comparison and wrong-combinator cases. `encode_match` + must pair `encode_pattern(arm.pattern)` with `encode(arm.body)`. Source clause: `match result { Some(v) => v == 7, None => false }`, i.e. `Expr.match_ (optResVar "result") @@ -781,15 +777,14 @@ theorem match_arm_swap_breaks_soundness : refDenoteArms, denoteArms, scrutVal, OptResVal.variant, OptResVal.payload, Env.bindInt, intVal, envAB] -/-- The faithful positive counterpart, for contrast: with the real `encode_match` (each body under +/-- With the faithful `encode_match` (each body under its own pattern) the `match result { Some(v) => v == 7, None => false }` clause is sound — its - encoder meaning is equivalent to the source, by `ref_sound`. Confirms the teeth bite only the - arm-swap, not the faithful encoder. -/ + encoder meaning is equivalent to the source, by `ref_sound`. -/ theorem match_faithful_is_sound : refDenote 0 matchSomeClause envAB ↔ denote 0 matchSomeClause envAB := ref_sound _ _ _ -/-! ## Negative sanity lemma 7 — the #180 wrong-`is`-variant teeth (the C7 `is` class) +/-! ## Negative sanity lemma 7: wrong `is` variant (#180) The dispatch's explicit requirement (b): demonstrate that an encoder that emitted the wrong `is`-variant — `result is Some` lowered as `result is None` (`ref_encode.rs`'s `Expr::Is` arm @@ -819,9 +814,8 @@ theorem is_wrong_variant_breaks_soundness : simp [isSomeClause, isNoneWrong, refDenote, denote, scrutVal, OptResVal.isVariant, OptResVal.variant, envAB] -/-- The faithful positive counterpart, for contrast: with the real `is`-variant the `result is - Some` clause is sound (both `(Some 7).isVariant Some = true`), by `ref_sound`. Confirms the - teeth bite only the wrong variant, not the faithful encoder. -/ +/-- With the faithful `is` variant, the `result is Some` clause is sound + (both `(Some 7).isVariant Some = true`), by `ref_sound`. -/ theorem is_faithful_is_sound : refDenote 0 isSomeClause envAB ↔ denote 0 isSomeClause envAB := ref_sound _ _ _ @@ -843,12 +837,11 @@ theorem match_result_faithful_is_sound : envAB := ref_sound _ _ _ -/-! ## Negative sanity lemma 8 — the #181 wrong-arg-order teeth (the spec-fn-call class) +/-! ## Negative sanity lemma 8: wrong spec-function argument order (#181) The dispatch's explicit requirement (a): demonstrate that an encoder that emitted a spec-fn call's args in the wrong order — `foo(a, b)` lowered as `foo(b, a)` for a non-commutative body — does not - satisfy soundness. The spec-fn-call analogue of the `==`-vs-`<=` teeth: the per-arg `encode_call_arg` - pairing (which encoded arg goes to which param position) is load-bearing. + satisfy soundness. The per-argument `encode_call_arg` pairing must preserve parameter order. Registry: `sub_fn(p, q) -> int { p - q }` — a non-commutative body (`p - q ≠ q - p` in general). Source clause `sub_fn(a, b)` at `a := 1, b := 2` denotes `1 - 2 = -1`; the wrong `sub_fn(b, a)` @@ -858,7 +851,7 @@ theorem match_result_faithful_is_sound : is what makes the arg order observable). -/ def subFn : SpecFn := SpecFn.mk ["p", "q"] (Expr.arith ArithOp.sub (Expr.var "p") (Expr.var "q")) -/-- A second spec fn `add_fn(p, q) = p + q` — used for the wrong-resolution teeth (a call that +/-- A second spec fn `add_fn(p, q) = p + q`, used for the wrong-resolution case (a call that resolves to `add_fn` where the source resolves to `sub_fn` is a different meaning). -/ def addFn : SpecFn := SpecFn.mk ["p", "q"] (Expr.arith ArithOp.add (Expr.var "p") (Expr.var "q")) @@ -904,7 +897,7 @@ theorem specfn_arg_order_breaks_soundness : simp [subCallFaithful, subCallArgSwapped, intVal, refIntVal, refIntValArgs, intValArgs, envSpec, subFn, Env.bindParams, Env.bindInt, arithDenote, tokArith, encArith] -/-! ## Negative sanity lemma 9 — the #181 wrong-registry-resolution teeth (the spec-fn-call class) +/-! ## Negative sanity lemma 9: wrong spec-function registry resolution (#181) The dispatch's explicit requirement (b): demonstrate that an encoder that resolved a spec-fn call to the wrong spec fn — emitting `add_fn(a, b)` where the source calls `sub_fn(a, b)` (the @@ -945,18 +938,18 @@ theorem specfn_nested_resolution_value : specialized to a `specCall`. Stated `∀ fuel` (the fuel-uniform statement, not a fuel-cap dodge): at the nested-resolution witness `g(p)` it holds at fuel `2` (where it unfolds to `4`) and at fuel `0` (where both bottom to `True`), because the source and encoder share the fuel + registry. - Confirms the faithful spec-fn-call encoder (args in order, name resolved correctly) is sound; the - teeth above bite only the arg-swap / wrong-resolution. -/ + This confirms that the faithful spec-function encoder (arguments in order, name + resolved correctly) is sound. -/ theorem specfn_call_faithful_is_sound (fuel : Nat) : refDenote fuel (Expr.specCall "g" [Expr.var "p"]) envSpec ↔ denote fuel (Expr.specCall "g" [Expr.var "p"]) envSpec := ref_sound _ _ _ -/-! ## Negative sanity lemma 10 — the #182 `count_where` teeth (wrong-predicate + off-by-one count) +/-! ## Negative sanity lemma 10: `count_where` wrong predicate and off-by-one count (#182) The dispatch's explicit requirement (a): demonstrate that a `count_where` encoded with a wrong predicate or an off-by-one count fails soundness. `count_where` is a value-combinator (`intVal`), - so the teeth are an inequality of counts (not an `Iff` of `Prop`s). + so the negative result is an inequality of counts rather than an `Iff` of `Prop`s. Source clause: `count_where(s, |x| x ≤ 15)` at `envAB` (`s := [10, 20, 30]`) — exactly one element (`10`) is ≤ 15, so the faithful count is `1`. @@ -997,10 +990,9 @@ theorem count_where_off_by_one_breaks_soundness : countWhereVal, countWhereVal_cons, denote, Env.bindInt, envAB] rw [h]; decide -/-- The faithful positive counterpart, for contrast (#182): with the real `count_where` encoding the +/-- With the faithful `count_where` encoding (#182), the `count_where(s, |x| x ≤ 15)` clause's encoder meaning equals the source count (`1`), by - `refIntVal_eq_intVal`. Confirms the teeth bite only the corrupted predicate / off-by-one, not the - faithful encoder; and that the recursive count is non-vacuous (the value `1`, not a bottom). -/ + `refIntVal_eq_intVal`. The recursive count is non-vacuous (the value `1`, not a bottom). -/ theorem count_where_faithful_intval_matches_source : refIntVal 0 countWhereClause envAB = intVal 0 countWhereClause envAB := refIntVal_eq_intVal _ _ _ @@ -1012,7 +1004,7 @@ theorem count_where_value_is_one : simp [countWhereClause, predLe15, predLe15Body, intVal, seqVal, countWhereVal, countWhereVal_cons, denote, Env.bindInt, envAB] -/-! ## Negative sanity lemma 11 — the #182 `permutation_of` multiset-vs-set teeth (the key fidelity) +/-! ## Negative sanity lemma 11: `permutation_of` multiset versus set semantics (#182) The dispatch's explicit requirement (b): demonstrate that `permutation_of` mis-modelled as set equality (membership) instead of multiset (counts) fails. The canonical witness: `a := [1,1,2]`, @@ -1021,7 +1013,8 @@ theorem count_where_value_is_one : `permEq` — `a.to_multiset() ≠ b.to_multiset()`), while a set-based model wrongly says true. This is the fidelity check that `permutation_of`'s `verus_l3` is `to_multiset()` equality, not set equality. -/ -/-- A fresh env for the `permutation_of` multiset-vs-set teeth (#182): `a := [1,1,2]`, `b := [1,2,2]` +/-- An environment for the `permutation_of` multiset-versus-set case (#182): + `a := [1,1,2]`, `b := [1,2,2]` (the canonical same-set / different-multiset witness). Everything else empty / `None` / no spec. -/ def envPerm : Env := { ints := fun _ => 0 @@ -1040,7 +1033,7 @@ def permClause : Expr := /-- The (wrong) set-equality model of `permutation_of` — membership, not counts: `∀ x, (x ∈ a ↔ x ∈ b)`. This is the infidelity the dispatch names: modelling `to_multiset()` equality as set equality. At `a := [1,1,2]`, `b := [1,2,2]` it is true (both have set `{1,2}`), whereas the faithful `permEq` - (counts) is false — the multiset-vs-set teeth. -/ + (counts) is false. -/ def permSetModel (a b : List Int) : Prop := ∀ x : Int, (x ∈ a ↔ x ∈ b) @@ -1069,15 +1062,14 @@ theorem permutation_set_model_breaks_soundness : rw [envPerm_a, envPerm_b] at h1 simp at h1 -/-- The faithful positive counterpart, for contrast (#182): with the real `permutation_of` (the - multiset `permEq`) the clause is sound — its encoder meaning is equivalent to the source, by - `ref_sound`. Confirms the teeth bite only the set-model infidelity, not the faithful encoder. -/ +/-- With the faithful `permutation_of` (the multiset `permEq`), the + clause is sound: its encoder meaning is equivalent to the source by `ref_sound`. -/ theorem permutation_faithful_is_sound : refDenote 0 permClause envPerm ↔ denote 0 permClause envPerm := ref_sound _ _ _ -/-- A faithful positive witness that `permutation_of` is non-vacuous and is satisfied by a genuine - permutation (#182): `[1,2,3]` is a permutation of `[3,1,2]` (same multiset) — the source +/-- A positive witness that `permutation_of` is non-vacuous (#182): `[1,2,3]` is a + permutation of `[3,1,2]` (the same multiset), so the source `permutation_of` is true here (every count agrees), showing `permEq` is not trivially false. -/ def envPermTrue : Env := { ints := fun _ => 0 diff --git a/lean/Thermite/Stabilize.lean b/lean/Thermite/Stabilize.lean index 8cf579ae6..8a5e1ad6c 100644 --- a/lean/Thermite/Stabilize.lean +++ b/lean/Thermite/Stabilize.lean @@ -650,7 +650,7 @@ noncomputable def intValNB : Nat → Expr → Env → Option Int | none => some True) s) | _, Expr.intLit n, _ => some n | _, Expr.var x, env => some (env.ints x) - -- The bool-sorted catch-all: `some 0`, a genuine value (matches `intVal`'s `0`; the + -- The bool-sorted catch-all: `some 0`, a value (matches `intVal`'s `0`; the -- `specCall` arms above already peeled off both fuels, so no `specCall` reaches here). | _, _, _ => some 0 termination_by fuel e _ => (fuel, sizeOf e) diff --git a/lean/Thermite/Strat/Denote.lean b/lean/Thermite/Strat/Denote.lean index b54fbd085..4ac85e6eb 100644 --- a/lean/Thermite/Strat/Denote.lean +++ b/lean/Thermite/Strat/Denote.lean @@ -71,7 +71,7 @@ def sdenote (𝓒 : CarrierAssign) (q : QOracle) : Frm → Env 𝓒.C → Bool finiteness datum in the semantics. They would FAIL if the carrier were not finite (or if `enum` were incomplete); `PinFiniteEscape` pins exactly that. -/ -/-- The `all` fold upgrades to a genuine `∀` through the completeness witness. -/ +/-- The `all` fold upgrades to a `∀` through the completeness witness. -/ theorem sdenote_all_iff (𝓒 : CarrierAssign) (q : QOracle) (φ : Frm) (ρ : Env 𝓒.C) : sdenote 𝓒 q (Frm.all φ) ρ = true ↔ ∀ x : 𝓒.C, sdenote 𝓒 q φ (cons x ρ) = true := by simp only [sdenote, List.all_eq_true] @@ -79,7 +79,7 @@ theorem sdenote_all_iff (𝓒 : CarrierAssign) (q : QOracle) (φ : Frm) (ρ : En · intro h x; exact h x (𝓒.complete x) · intro h x _; exact h x -/-- The `ex` fold upgrades to a genuine `∃` through the completeness witness. -/ +/-- The `ex` fold upgrades to a `∃` through the completeness witness. -/ theorem sdenote_ex_iff (𝓒 : CarrierAssign) (q : QOracle) (φ : Frm) (ρ : Env 𝓒.C) : sdenote 𝓒 q (Frm.ex φ) ρ = true ↔ ∃ x : 𝓒.C, sdenote 𝓒 q φ (cons x ρ) = true := by simp only [sdenote, List.any_eq_true] diff --git a/lean/Thermite/Strat/Fragment.lean b/lean/Thermite/Strat/Fragment.lean index 96732b7ef..3d54aed9d 100644 --- a/lean/Thermite/Strat/Fragment.lean +++ b/lean/Thermite/Strat/Fragment.lean @@ -102,7 +102,7 @@ def idxGrammar (φ : Frm) : Bool := idxGrammarAt 0 φ /-! ## The executable classifier and the declarative fragment -/ -/-- The admission classifier (metatheory §3.1). Computed on the NEGATION NORMAL FORM so +/-- The admission classifier (metatheory §3.1). Computed on the negation NORMAL FORM so every binder's polarity — and hence the E1 edges — is syntactic (`Strat/Nnf.lean`). -/ def admitted (φ : Frm) : Bool := finCarrier φ && idxGrammar φ && acyclic (sortGraph (nnf φ)) diff --git a/lean/Thermite/Strat/Graph.lean b/lean/Thermite/Strat/Graph.lean index 78cd02b46..3a4fee0a7 100644 --- a/lean/Thermite/Strat/Graph.lean +++ b/lean/Thermite/Strat/Graph.lean @@ -83,7 +83,7 @@ def reach (G : Graph) : List Sort₂ → Sort₂ → Sort₂ → Bool | [], a, b => hasEdge G a b | v :: vs, a, b => reach G vs a b || (reach G vs a v && reach G vs v b) -/-- Soundness: a positive `reach` yields a genuine transitive-closure path. -/ +/-- Soundness: a positive `reach` yields a transitive-closure path. -/ theorem reach_sound {G : Graph} : ∀ (allowed : List Sort₂) (a b : Sort₂), reach G allowed a b = true → TC G a b | [], a, b, h => TC.base h @@ -93,7 +93,7 @@ theorem reach_sound {G : Graph} : ∀ (allowed : List Sort₂) (a b : Sort₂), · exact reach_sound vs a b h · exact TC.trans (reach_sound vs a v h1) (reach_sound vs v b h2) -/-- Cut a chain at the FIRST occurrence of `v`: a prefix `a → v` not re-using `v`. -/ +/-- Cut a chain at the first occurrence of `v`: a prefix `a → v` not re-using `v`. -/ theorem chain_prefix {G : Graph} {a b ms} (h : Chain G a b ms) : ∀ v, v ∈ ms → ∃ ms1, Chain G a v ms1 ∧ v ∉ ms1 ∧ (∀ x ∈ ms1, x ∈ ms) := by induction h with @@ -115,7 +115,7 @@ theorem chain_prefix {G : Graph} {a b ms} (h : Chain G a b ms) : · exact List.mem_cons_self .. · exact List.mem_cons_of_mem _ (hsub x h) -/-- Cut a chain at the LAST occurrence of `v`: a suffix `v → b` not re-using `v`. -/ +/-- Cut a chain at the last occurrence of `v`: a suffix `v → b` not re-using `v`. -/ theorem chain_suffix {G : Graph} {a b ms} (h : Chain G a b ms) : ∀ v, v ∈ ms → ∃ ms2, Chain G v b ms2 ∧ v ∉ ms2 ∧ (∀ x ∈ ms2, x ∈ ms) := by induction h with @@ -260,7 +260,7 @@ def sortGraph (φ : Frm) : Graph := let es := edgesFrm [] φ { nodes := nodesOf es, edges := es } -/-- `sortGraph` is well-formed: its node set is COMPLETE for its edges (every edge +/-- `sortGraph` is well-formed: its node set is complete for its edges (every edge endpoint is a node), so `acyclic`'s Warshall closure over `nodes` is exhaustive. This is the kernel realisation of metatheory §5's `sortGraph_complete`: the E2 edges diff --git a/lean/Thermite/Strat/RefEncode.lean b/lean/Thermite/Strat/RefEncode.lean index 36d2bd3f2..f2df55c5d 100644 --- a/lean/Thermite/Strat/RefEncode.lean +++ b/lean/Thermite/Strat/RefEncode.lean @@ -108,7 +108,7 @@ def wfFrm (d : Nat) : Frm → Bool | .all _ φ => wfFrm (d + 1) φ | .ex _ φ => wfFrm (d + 1) φ -/-! ## Well-formedness of the ENCODER OUTPUT (the fresh-name + MBQI discipline) +/-! ## Well-formedness of the ENCODER output (the fresh-name + MBQI discipline) `tokWf d Φ` checks that every binder name is `≥ d` and the body is `tokWf (name+1)` — so binder names strictly increase down every path (hence pairwise diff --git a/lean/Thermite/Strat/Restratify.lean b/lean/Thermite/Strat/Restratify.lean index 600c5fffa..ad2afa63f 100644 --- a/lean/Thermite/Strat/Restratify.lean +++ b/lean/Thermite/Strat/Restratify.lean @@ -47,13 +47,13 @@ namespace Thermite.Strat.Cls /-! ## The rewrite, the abstraction, and the side obligation -/ -/-- The fresh OPAQUE boolean abstraction leaf standing in for an excised sub-formula. +/-- The fresh opaque boolean abstraction leaf standing in for an excised sub-formula. A `qfree` atom is opaque to the classifier (no sorts, no graph edges — `edgesAtom` returns `[]`), so substituting it for a cycle-closing conjunct deletes that conjunct's edges from the sort graph. -/ def absLeaf (e : Thermite.Expr) : Frm := .atom (.qfree e) -/-- The restratify rewrite (metatheory §6). On a conjunction `A ∧ B` whose RIGHT +/-- The restratify rewrite (metatheory §6). On a conjunction `A ∧ B` whose right conjunct `B` closes the alternation cycle, excise `B` and replace it with the fresh abstraction `p = absLeaf e` — yielding the admissible `A ∧ p`. Any other shape is returned unchanged (the kv repair is the §6 worked instance; the classifier reports @@ -62,7 +62,7 @@ def restrat (e : Thermite.Expr) : Frm → Frm | .conj A _ => .conj A (absLeaf e) | φ => φ -/-- The implication side obligation `Side(φ', φ)` (R-SIDE-1). Parameterised by the +/-- The implication side obligation `Side(φ', φ)` (R-side-1). Parameterised by the abstraction token `e` introduced into φ' and the ORIGINAL φ (from which the excised conjunct `B` is read): the obligation that the abstraction `p = absLeaf e` soundly stands for `B`, i.e. `p ⇒ B`. Discharging it IN-CAGE (it is admissible — @@ -71,14 +71,14 @@ def Side (e : Thermite.Expr) : Frm → Frm | .conj _ B => .imp (absLeaf e) B | φ => φ -/-! ## T4-R — conservativity (R-SIDE-1): the certificate bridge +/-! ## T4-R — conservativity (R-side-1): the certificate bridge For EVERY model `(q, dom)` and environment `ρ`, the rewritten formula φ' TOGETHER WITH a discharged `Side` re-establishes the original φ. This is the soundness of using restratify: certifying φ' and discharging `Side` (both in-cage) certifies φ. The proof is a Boolean tautology — modus ponens on the abstraction's denotation. -/ -/-- **T4-R conservativity.** `restrat_conservative` consumes BOTH φ' and `Side`: +/-- **T4-R conservativity.** `restrat_conservative` consumes both φ' and `Side`: `fdenote φ' ∧ fdenote Side ⇒ fdenote φ`, for all `(q, dom, ρ)`. Dropping either hypothesis breaks it — the `Side` hypothesis is precisely what `PinRestratDropSide` shows is load-bearing. -/ @@ -96,7 +96,7 @@ theorem restrat_conservative (e : Thermite.Expr) (q : Atom → Bool) (dom : List · exact absurd (hp.symm.trans hpf) (by decide) · exact hB -/-! ## T4-R — admissibility: φ' and `Side` are both IN-CAGE +/-! ## T4-R — admissibility: φ' and `Side` are both in-cage The whole point of the split: the originally-rejected φ becomes TWO in-cage obligations. Demonstrated on the §6 kv worked example. -/ @@ -109,12 +109,12 @@ def kvAbs : Thermite.Expr := .boolLit true `ex_kvCycle` to an ADMITTED formula (the `Value → Key` edge is gone with B). -/ theorem restrat_admits : admitted (restrat kvAbs ex_kvCycle) = true := by decide -/-- For contrast: the original kv cycle is REJECTED (`Strat/Fragment.lean` +/-- For contrast: the original kv cycle is rejected (`Strat/Fragment.lean` `ex_kvCycle_rejected`), so `restrat` genuinely moves it into the cage. -/ theorem restrat_moves_into_cage : admitted ex_kvCycle = false ∧ admitted (restrat kvAbs ex_kvCycle) = true := by decide -/-- **T4-R `side_admitted`.** The side obligation `Side(φ', φ)` is itself ADMITTED — +/-- **T4-R `side_admitted`.** The side obligation `Side(φ', φ)` is itself admitted — so it can be DISCHARGED IN-CAGE (it carries only the `Value → Key` edge from B, no cycle). This is what makes the restratify split usable: both products are in-cage. -/ theorem side_admitted : admitted (Side kvAbs ex_kvCycle) = true := by decide diff --git a/lean/Thermite/Strat/SubstKit.lean b/lean/Thermite/Strat/SubstKit.lean index 977e914b2..ccc53740d 100644 --- a/lean/Thermite/Strat/SubstKit.lean +++ b/lean/Thermite/Strat/SubstKit.lean @@ -181,7 +181,7 @@ theorem substAtom_liftAtom (c : Nat) (s : Tm) (a : Atom) : | eq t u => simp only [liftAtom, substAtom, substTm_liftTm] | qf e => rfl -/-! ## The two load-bearing lemmas (note §4, verbatim shapes) +/-! ## The two required lemmas (note §4, verbatim shapes) `q` is fixed context; the formula `φ` is the induction target; the cutoff / index / value / environment are quantified after it. The `atom` case factors diff --git a/lean/lake-manifest.json b/lean/lake-manifest.json index 8f499f8f2..578c73d97 100644 --- a/lean/lake-manifest.json +++ b/lean/lake-manifest.json @@ -5,10 +5,10 @@ "type": "git", "subDir": null, "scope": "", - "rev": "7d1d8239e78daa5197f9a71948776c4627049f5f", + "rev": "ee6d36b97a70f6b378c8ad851a938ed79b321c65", "name": "smt", "manifestFile": "lake-manifest.json", - "inputRev": "7d1d8239e78daa5197f9a71948776c4627049f5f", + "inputRev": "ee6d36b97a70f6b378c8ad851a938ed79b321c65", "inherited": false, "configFile": "lakefile.lean"}, {"url": "https://github.com/leanprover-community/mathlib4.git", diff --git a/lean/lakefile.toml b/lean/lakefile.toml index c2e257d8a..c5fd23574 100644 --- a/lean/lakefile.toml +++ b/lean/lakefile.toml @@ -12,14 +12,12 @@ name = "thermite" defaultTargets = ["Thermite"] -# Pinned to a SHA (not the `main` branch) so a manifest refresh cannot float the -# dependency upstream unobserved (trust-audit finding F4). This is the commit -# `lake-manifest.json` already locks; pinning it here makes the requirement and the -# lockfile agree, so the Lean CI job's `lake build` is reproducible. +# Pin lean-smt to the proof from upstream PR #227. It closes the +# `BitVec.eq_eq_beq` axiom while keeping the Lean 4.29 dependency set. [[require]] name = "smt" git = "https://github.com/ufmg-smite/lean-smt.git" -rev = "7d1d8239e78daa5197f9a71948776c4627049f5f" +rev = "ee6d36b97a70f6b378c8ad851a938ed79b321c65" [[lean_lib]] name = "Thermite" diff --git a/scripts/lean-axiom-probe.sh b/scripts/lean-axiom-probe.sh index e30d435cf..36032a847 100755 --- a/scripts/lean-axiom-probe.sh +++ b/scripts/lean-axiom-probe.sh @@ -123,13 +123,9 @@ IMPORTS=( "Thermite.PinStratSelfLoop" "Thermite.PinNNFPolarity" "Thermite.PinRelaxRefute" - # Stage-3 REQ-7/REQ-8 (#356, Path B): the bit-vector ⟷ bounded-integer model - # faithfulness metatheorem (`Thermite.BvModel`). Core-only (no Mathlib, no Smt — unlike - # `Thermite.SmtExport`), so it builds in CI and is added as a BUILD target here: a - # `sorry` or broken proof fails the Lean job. The three faithfulness theorems - # (`tmInt_eq_toNat`/`frmInt_iff_frmBV`/`tv_equiv_faithful`) are axiom-probed IN-FILE - # (the SubstKit/SPIKE-1 convention); promoting `tv_equiv_faithful` into the gated - # THEOREMS list below is the REQ-8/G3 trust-flip action, not done here. + # Stage-3 reference model (#356): the core-only BitVec ⟷ bounded-integer + # faithfulness theorems. The active exporter now emits literal BitVec terms, but this + # independent model remains in the no-sorry build and keeps its in-file axiom probes. "Thermite.BvModel" ) # The five load-bearing universal-theorem pillars + the two relax-route spine lemmas diff --git a/tests/golden/combinators/README.md b/tests/golden/combinators/README.md index 0a999ec10..27e1d9511 100644 --- a/tests/golden/combinators/README.md +++ b/tests/golden/combinators/README.md @@ -1,33 +1,19 @@ -# SpecTherm combinator oracle (`tests/golden/combinators/`) +# SpecTherm combinator fixtures -The hand-derived external anchor for `thermite-spec` (issue #2), per -`.design/spec/spectherm-combinators.md`. Referenced by the -`thermite-spec/src/combinators.rs` route in `tooling/spec-routes.toml`. +These files are the external fixtures for `thermite-spec`, governed by +`.design/spec/spectherm-combinators.md`. -## Files +- `registry.json` defines the v0.1 combinator names, arities, argument kinds, + and result kinds consumed by the validator. +- `accept.json` contains valid contract expressions covering all eight + combinators and a specification-function call. +- `reject.json` contains parseable programs that the validator must reject, + together with the expected error category. -- **`registry.json`** — the FROZEN v0.1 SpecTherm combinator set (name, arity, - arg-kinds, result). The registry the validator consumes. SMT triggers and - Verus(L3)/executable(L1) forms are deferred to issue #4. -- **`accept.json`** — Thermite programs that PARSE and the validator must - ACCEPT (valid SpecTherm). Covers all 8 combinators in contract positions + - a spec-fn call in a contract. -- **`reject.json`** — Thermite programs that PARSE but the validator must - REJECT, each with the expected `SpecError` cause (unknown combinator, wrong - arity, wrong arg-kind). These step outside the §4.2 cage. +The expected values are derived from `thermite-design.md` §4.2 and the fixture +programs. They are not generated from `thermite-spec` output (goal.md +R-CHAR-3). -## R-CHAR-3 - -Every expected value here is derived from `thermite-design.md` §4.2 + the -verbatim corpus — never from `thermite-spec`'s own output. The validator is -the artifact under test; this is the truth it is tested against. A builder -implementing the validator must MATCH these fixtures, never edit them to fit -its output. - -## Note on the reject `expected` variant names - -`reject.json`'s `expected` strings mirror the `SpecError` variants named in -`.design/spec/spectherm-combinators.md` REQ-4. The builder MAY choose -different variant identifiers, but the REJECT outcome and the documented cause -must hold; the conformance test should assert rejection + the cause, not a -brittle exact string if the design doc and code agree on a rename. +The error names in `reject.json` follow the variants in the current design. +Tests should check the documented cause rather than a brittle string when an +intentional variant rename keeps the same behavior. diff --git a/thermite-lower/src/l1.rs b/thermite-lower/src/l1.rs index f308ca3dc..4304d9800 100644 --- a/thermite-lower/src/l1.rs +++ b/thermite-lower/src/l1.rs @@ -182,7 +182,7 @@ pub fn lower_l1(program: &Program) -> Result { // A boundary fn (ffi-boundary.md REQ-4) lowers to the L1 wrapper: a // `req`-check, a call to the foreign target binding `result`, then the // `ens`-checks; the foreign body is not lowered or verified. An - // in-language fn lowers with its real body. + // in-language fn lowers with its body. Item::Fn(f) if f.boundary.is_some() => lower_boundary_fn_l1(f, &variants)?, Item::Fn(f) => lower_fn_l1(f, &variants, &inv_structs)?, // Basis Stage 1c (`.design/basis/01-adts.md` REQ-8/REQ-9): a `struct` @@ -1437,7 +1437,7 @@ pub(crate) fn lower_stmt_l1( /// Lower an `Expr` in exec position to plain Rust (REQ-3). `depth` bounds /// recursion (REQ-9-equivalent; mirrors `lower.rs`'s guard). A combinator call /// lowers to a call of its L1 fn (the name is unchanged; its body is emitted by -/// `emit_combinator_l1_defs`), with a closure argument becoming a real Rust +/// `emit_combinator_l1_defs`), with a closure argument becoming a Rust /// closure. Every clause is a real `bool`/value expression over real values. pub(crate) fn lower_expr_exec( expr: &Expr, @@ -1612,7 +1612,7 @@ pub(crate) fn lower_expr_exec( Ok(format!("{r}.{name}")) } Expr::Closure { params, body } => { - // A real Rust closure (REQ-3); the corpus closures are `u32`-typed + // A Rust closure (REQ-3); the corpus closures are `u32`-typed // slice-element predicates (matching the registry `l1` `impl Fn(u32) // -> bool` parameter). let b = lower_expr_exec(body, d, span, variants)?; @@ -1727,7 +1727,7 @@ pub(crate) fn lower_expr_exec( // A raw quantifier `forall`/`exists` (`.design/stage2-stratified-cage.md` // REQ-0) is a SPEC-only formula — it has no executable meaning, so it never // belongs in an L1 exec-body position. (Even its spec lowering is deferred to - // REQ-8.) Refuse honestly with the established "outside the v0.1 mapping" + // REQ-8.) Refuse with the established "outside the v0.1 mapping" // error rather than emit anything. No corpus places a quantifier in exec // position, so this path is unreachable for the existing goldens. Expr::Quantifier { .. } => Err(LowerError::Unsupported { diff --git a/thermite-lower/src/l2.rs b/thermite-lower/src/l2.rs index 4f630e9e2..28e72f284 100644 --- a/thermite-lower/src/l2.rs +++ b/thermite-lower/src/l2.rs @@ -22,8 +22,8 @@ //! //! Governing design: `.design/lower/l2-kani.md`. //! Reference (real `cargo kani 0.67.0` runs, hand-grounded): `conformance/sum.th` -//! → `VERIFICATION:- SUCCESSFUL` at `N = 4`/`unwind(5)`; `conformance/binary_search.th` -//! → `VERIFICATION:- SUCCESSFUL` at `N = 4`/`unwind(6)`. +//! → `verification:- successful` at `N = 4`/`unwind(5)`; `conformance/binary_search.th` +//! → `verification:- successful` at `N = 4`/`unwind(6)`. //! //! ## The bound is type-driven, not name-driven (REQ-2) //! diff --git a/thermite-lower/src/lower.rs b/thermite-lower/src/lower.rs index 1bff3a2c2..f7e23e2a9 100644 --- a/thermite-lower/src/lower.rs +++ b/thermite-lower/src/lower.rs @@ -714,7 +714,7 @@ pub fn lower(program: &Program) -> Result { // program calls `parse_u64` / names `all_digits`/`is_digit` (`program_uses_- // parse`), before any fn references it. Empty otherwise (byte-stable). The // emitted form is the grounded `5 verified, 0 errors` parse (no - // `assume`/`external_body`/`admit`, R-DEFER-9 — the round-trip is a real proof; + // `assume`/`external_body`/`admit`, R-DEFER-9 — the round-trip is a proof; // a broken `Some(0)` fails). `parse_be` is shared with the numfmt round-trip; it // is emitted here only when numfmt did not already emit it (dedup). let parse_defs = emit_parse_defs(program)?; @@ -763,7 +763,7 @@ pub fn lower(program: &Program) -> Result { // Cluster C4 (`.design/basis/07-strings.md` REQ-8, issue #94): the generated // round-trip spec fns `parse_le`/`pow10` return `nat`, so when the program names // them they join `nat_fns` — an `Eq` against `parse_le(...)` (the round-trip - // `ens parse_le(result) == n`) coerces the scalar `u64` side `as nat` exactly as + // `ens parse_le(result) == n`) coerces the scalar `u64` side `as nat` as // a hand-written ADT-fold-sum does. Added only when numfmt is in use (byte-stable // for the non-numfmt corpus). if program_uses_numfmt(program) { @@ -774,7 +774,7 @@ pub fn lower(program: &Program) -> Result { // Cluster C5 (`.design/basis/07-strings.md` REQ-15, issue #102): the generated // `count_sep` spec fn returns `nat`, so when the program uses a C5 op it joins // `nat_fns` — `split`'s `ens result.len() == 1 + count_sep(s@, sep)` coerces the - // scalar `result.len()` side `as nat` exactly as a hand-written ADT-fold-sum does. + // scalar `result.len()` side `as nat` as a hand-written ADT-fold-sum does. // (The other C5 spec fns — `occurs_at`/`contains_sub`/`sep_free`/`is_space` — // return `bool`, so they do not join `nat_fns`.) Added only when a C5 op is in use // (byte-stable for the non-C5 corpus). @@ -2945,7 +2945,7 @@ fn lower_external_body_fn( /// Lower the §7 equivalent-mutant equivalence obligation for one survivor /// (`.design/forge/equivalent-mutants.md` REQ-1): given a fn `f` (its `req`, -/// params, return type, and real body `f.body`) and a surviving mutant's body +/// params, return type, and body `f.body`) and a surviving mutant's body /// `mutant_body`, emit a complete Verus source file that asks Verus to prove /// that, under `f`'s `req`, the mutant body's observable result equals the real /// body's result for all inputs. A `verus` run that verifies (`0 errors`) is a @@ -3110,7 +3110,7 @@ pub fn lower_equivalence_obligation( /// ``` /// /// A verified harness (`ensures eq` proved, `0 errors`) proves that no input -/// satisfying `req` distinguishes the mutant from the real body given the callee +/// satisfying `req` distinguishes the mutant from the body given the callee /// contracts, so the survivor is a true equivalent (modulo the contracts the §9 /// edifice already trusts) and is excluded (REQ-2 polarity, unchanged). A weak callee /// contract that cannot pin `real == mutant` leaves `eq` unprovable, so the survivor @@ -4799,7 +4799,7 @@ fn emit_map_wrappers(program: &Program) -> Result { /// `contains_key` (`ens result == spec_contains_key(k)`), the no-OOB /// `get -> Option` (absent → `None`, not a wrong value), and the /// append-under-`!contains_key` `insert` with the `final(self)` &mut postcondition. -/// No `assume`/`external_body` (R-DEFER-9): every contract is real verus map +/// No `assume`/`external_body` (R-DEFER-9): every contract is verus map /// reasoning threaded over vstd's verified `Vec::push`/`Vec::index`/`Vec::len`. v1 /// grounds Copy keys (`Map`, OQ-4); a non-Copy key is the existing /// `LowerError::Unsupported` via `tmap_name`. @@ -5063,7 +5063,7 @@ fn ty_reaches_string(ty: &Type) -> bool { /// For the per-item sub-program weave (forge `#86`): a `forge check` /// per-item sub-program may be a struct decl alone (`struct Buf { text: String, /// cursor: u64 }`) whose only `String` reference is a field type, so the struct -/// and enum field arms below are load-bearing, not a `continue`. Mirrors the way +/// and enum field arms below are required, not a `continue`. Mirrors the way /// `reachable_adt_deps` weaves the struct decls a String-bearing item reaches. fn program_uses_string(program: &Program) -> bool { for item in &program.items { @@ -6040,7 +6040,7 @@ const GENERATED_NUMFMT_SPEC_FNS: &[&str] = &["parse_le", "parse_be", "pow10"]; /// == n`; the loop invariant is the round-trip partial accumulator `parse_le(data@) /// + m*pow10(data.len()) == n` with `decreases m`; the per-iteration step is /// discharged by `lemma_parse_push` + `by(nonlinear_arith)`. no `assume`/ -/// `external_body`/`admit` (R-DEFER-9) — the round-trip is a real proof. +/// `external_body`/`admit` (R-DEFER-9) — the round-trip is a proof. fn emit_numfmt_defs(program: &Program) -> Result { if !program_uses_numfmt(program) { return Ok(String::new()); @@ -6196,15 +6196,15 @@ fn emit_numfmt_defs(program: &Program) -> Result { // data.len() - i`), and `lemma_parse_be_reverse` closes the contract. out.push('\n'); out.push_str("pub fn u64_to_string(n: u64) -> (result: TString)\n"); - // The round-trip is the gold standard; `result.data.len() >= 1` is the honest + // The round-trip is the gold standard; `result.data.len() >= 1` is the // floor that contractually forbids the empty string (every decimal has at least // one digit, including 0 -> "0"). The round-trip alone admits "" for 0 // (`parse_be([]) == 0`), so the len floor is what catches a dropped zero-guard - // (blocker #97; without the guard this `ens` fails verus, R-DEFER-9 — real teeth). + // (blocker #97; without the guard this `ens` fails verus, R-DEFER-9). out.push_str(" ensures\n"); out.push_str(" parse_be(result.data@) == n as nat,\n"); out.push_str(" result.data.len() >= 1,\n"); - // `result.data.len() <= 20` — the honest upper floor (REQ-8): a u64 is < 10^20 + // `result.data.len() <= 20` — the upper floor (REQ-8): a u64 is < 10^20 // (pow10(20) > u64::MAX), so its decimal has at most 20 digits. This bounds the // formatted-number length from above so a caller's bounded `concat` (the §4.2 cage // CAP precondition `self.len() + b.len() <= CAP`) discharges when one operand is @@ -6495,7 +6495,7 @@ fn reserve_generated_names(src: &str) -> String { /// `parse_u64` call (the surface `parse_u64(s)`) or an `all_digits`/`is_digit` /// reference in a contract. Either requires the generated parse definitions in /// scope. empty otherwise (byte-stable for the non-parse corpus). The walk reuses -/// the `each_subexpr` full-tree traversal exactly as `program_uses_numfmt`. +/// the `each_subexpr` full-tree traversal as `program_uses_numfmt`. pub(crate) fn program_uses_parse(program: &Program) -> bool { // #127 — shape key: a call whose name is a generated parse def name but which // resolves to a user `spec fn` (declared with a `String`/`&String` param, the @@ -6853,7 +6853,7 @@ fn expr_uses_bytes_eq(expr: &Expr, shadow: &[&str]) -> bool { /// `bytes_eq` (`program_uses_bytes_eq`). The exact forms are grounded verbatim /// (real `verus 0.2026.05.24`): the `Seq` low-peel recursion `bytes_eq` + the /// core induction `lemma_bytes_eq_from_pointwise` (the explicit `#[trigger] a[ai + -/// k]` is load-bearing — auto-inference fails on the arithmetic index), the cheap +/// k]` is required — auto-inference fails on the arithmetic index), the cheap /// converse `lemma_bytes_eq_to_pointwise`, the subrange corollary /// `lemma_bytes_eq_from_subrange`, and the no-arg quantified-equivalence /// `lemma_bytes_eq_bridge` (the one-call citation form: its `=~=` plants the @@ -6873,7 +6873,7 @@ fn expr_uses_bytes_eq(expr: &Expr, shadow: &[&str]) -> bool { /// verified, 1 errors`) — the subrange-index equality needs the manual trigger. /// This is a body-fill within the pinned signature, not a change to any pinned /// statement shape; all four lemmas + the `slice_id`/`insert_str` pins verify (`14 -/// verified, 0 errors`), the head/tail-swap mutant FAILS (`13 verified, 1 errors`). +/// verified, 0 errors`), the head/tail-swap mutant fails (`13 verified, 1 errors`). fn emit_bytes_eq_defs(program: &Program) -> Result { if !program_uses_bytes_eq(program) { return Ok(String::new()); @@ -6891,7 +6891,7 @@ fn emit_bytes_eq_defs(program: &Program) -> Result { ); out.push_str("}\n"); // the core induction: pointwise window equality ==> bytes_eq. The explicit - // `#[trigger] a[ai + k]` is load-bearing (auto-inference fails on the arith index). + // `#[trigger] a[ai + k]` is required (auto-inference fails on the arith index). out.push_str( "pub proof fn lemma_bytes_eq_from_pointwise(a: Seq, b: Seq, ai: int, bi: int, n: int)\n", ); @@ -7163,7 +7163,7 @@ fn lower_expr(expr: &Expr, ctx: Ctx, depth: usize, span: Span) -> Result a.data@`, // the `string_as_byteview` / `is_string` `.data@` rule); args 2/3/4 are the // surface integer offsets/length (`ai`/`bi`/`n`, each a `u64`-shaped surface - // expression — `0`, `b.cursor`, `a.len()`), cast `as int` exactly as + // expression — `0`, `b.cursor`, `a.len()`), cast `as int` as // `occurs_at`'s arg 2 (Verus does no implicit `u64 -> int` in a spec-fn arg // position). Keyed on the callee name `bytes_eq` + the arg index (>= 2). A // literal / already-`as int` arg passes through (`lower_index_arg` avoids the @@ -7342,7 +7342,7 @@ fn lower_expr(expr: &Expr, ctx: Ctx, depth: usize, span: Span) -> Result nat` unchanged (`r.len()`). Keyed on the method // name `get` in spec position only; exec `get`/`push`/`len` (a fn body) // lower verbatim to the verified vstd-backed exec methods. The index - // cast `as int` is appended exactly as `lower_index_arg` does for a + // cast `as int` is appended as `lower_index_arg` does for a // combinator index, avoiding a double-cast on an already-`as int` arg. if ctx.is_spec() && name == "get" && args.len() == 1 { let idx = lower_index_arg(&args[0], ctx, d, span)?; @@ -7378,7 +7378,7 @@ fn lower_expr(expr: &Expr, ctx: Ctx, depth: usize, span: Span) -> Result Option` is named in a // contract only via the C7 spec-`match`-in-`ens` over the result (the - // `match result { Some(v) => …, None => … }` form), exactly as the + // `match result { Some(v) => …, None => … }` form), as the // grounded `insert_then_get` round-trip threads — not a spec-fn rewrite. if ctx.is_spec() && name == "contains_key" && args.len() == 1 { let arg = lower_expr(&args[0], ctx, d, span)?; @@ -7570,7 +7570,7 @@ fn lower_expr(expr: &Expr, ctx: Ctx, depth: usize, span: Span) -> Result Result. φ` / `exists (x : S) in // . φ` (`.design/stage2-stratified-cage.md` REQ-0/REQ-8). This is the - // PRODUCTION quantifier emission REQ-8 owns ("production quantifier emission in - // thermite-lower (the real Rust lowering of stratified clauses to the SMT + // production quantifier emission REQ-8 owns ("production quantifier emission in + // thermite-lower (the Rust lowering of stratified clauses to the SMT // surface)"). The faithfulness of this emission against the independent // stratified reference encoder (`thermite_tv::strat_ref_encode`) is the // two-phase TV's job (REQ-8); soundness of the encoded surface is the kernel @@ -7675,7 +7675,7 @@ fn lower_expr(expr: &Expr, ctx: Ctx, depth: usize, span: Span) -> Result.len()` (the Verus bounded-quantifier idiom // the combinator expansions also use, `Ast.lean` `forall_in(s,p) = ∀ i, 0 ≤ i // < s.len() → p(s[i])`). `forall` guards with `==>`, `exists` with `&&` (the - // standard bounded forms). It is TRIGGER-FREE (the MBQI surface T1-S's + // standard bounded forms). It is TRIGGER-free (the MBQI surface T1-S's // `strat_ref_wf` certifies: no `#[trigger]` pattern restricting instantiation) // — matching `Strat/RefEncode.lean`'s `sencode`, which sets every quantifier // `triggerFree := true`. The body lowers in the same ctx with `x` in scope. @@ -8176,7 +8176,7 @@ fn binop(op: BinOp) -> &'static str { /// pinned standard-Rust precedence (`surface-grammar.md` REQ-10) closely enough to /// decide parenthesization of nested binaries during emission (REQ-3 — preserve /// the AST's grouping). The #92 tiers (modulo at `* /`, shifts, `&`, `^`, `|`) -/// slot between `+ -` and comparison exactly as the parser threads them. +/// slot between `+ -` and comparison as the parser threads them. fn precedence(op: BinOp) -> u8 { match op { BinOp::Or => 1, @@ -8219,7 +8219,7 @@ fn lower_binary_operand( // of a generic argument list (`u32<33, …>`), a hard parse error ("expected // `,`"). The fix is the dual of the #122 cast-inner paren: parenthesize the // whole cast when it is the left operand of a `<`-leading op (`(x as u32) < - // 33`). Keyed on (left operand is a `Cast`) AND (parent op begins with `<`), so + // 33`). Keyed on (left operand is a `Cast`) and (parent op begins with `<`), so // the corpus/goldens — whose casts feed `==`/`*`/`+`/`-`/`>`(`acc as nat ==`, // `i as u64 * …`, `xs.len() as u64 * …`) — stay byte-identical (no churn). The // independent `thermite_tv::ref_encode` already parenthesizes every cast, so @@ -8338,7 +8338,7 @@ fn lower_fn_body( /// exactly req conjuncts (no invented bound — the `requires` is itself /// discharged from the fn's `req` at the assert site). A product whose /// variables are not all req-bounded params is skipped (`req_expr_upper_bound` -/// returns `None`), so the honest obligation stands — no fabricated assert. A +/// returns `None`), so the obligation stands — no fabricated assert. A /// variable shadowed by a `let`/assignment in the body is also skipped (the /// req conjunct would refer to the param, not the rebound local — `is_rebound`). /// @@ -8361,7 +8361,7 @@ fn req_bounded_mul_asserts(f: &FnItem, body: &Block) -> Result, Lowe // Drop any bound whose variable is rebound (shadowed/mutated) anywhere in // the body: the `req v <= C` refers to the immutable param, but a rebound // local of the same name would make the emitted `requires v <= C` refer to - // the local — an honest verus failure, but a spurious one. Skip such names. + // the local — an verus failure, but a spurious one. Skip such names. bounds.retain(|name, _| !block_rebinds(body, name)); if bounds.is_empty() { return Ok(vec![]); @@ -8781,7 +8781,7 @@ fn lower_stmt(stmt: &Stmt, ctx: Ctx, indent: usize) -> Result bool { } Expr::Cast { expr, .. } | Expr::Ref { expr, .. } => expr_mentions(expr, name), // Basis Stage 1a (`.design/basis/01-adts.md`): dead-in-1a ADT - // expressions, but the honest predicate value is to descend — a name + // expressions, but the predicate value is to descend — a name // could be mentioned in a struct-literal field value, an `is` // scrutinee, or a deref operand, so we must not silently answer `false`. Expr::StructLit { fields, .. } => fields.iter().any(|(_, v)| expr_mentions(v, name)), @@ -9163,7 +9163,7 @@ fn push_lemma_for(specfn: &str) -> String { } /// template (overflow): if the loop body assigns `acc = acc + slice[idx] as T` -/// and an invariant bounds `acc <= idx as T * BOUND`, emit the +/// and an invariant bounds `acc <= idx as T * bound`, emit the /// `by(nonlinear_arith)` discharge with the in-scope invariant/precondition /// hypotheses as `requires`. Keys on shape: an `Assign` whose value is /// `acc + (slice[idx] cast)`, plus a product-bound invariant on the same `acc`. @@ -9177,7 +9177,7 @@ fn nonlinear_overflow_assert( let Some((accvar, idxvar)) = find_accumulator_growth(body) else { return Ok(None); }; - // Find the product-bound invariant `acc <= idx as T * BOUND`. + // Find the product-bound invariant `acc <= idx as T * bound`. let Some((bound_factor, bound_ty)) = find_product_bound(invs, &accvar, &idxvar, spec_fn_param_types) else { @@ -9628,13 +9628,14 @@ fn spec_dec(dec: &Clause, params: &[Param], spec_fn_param_types: &[(&str, &[Prim mod exec_expr_tests { //! `lower_exec_expr` per-expr exec lowering (`.design/verified/exec-tv.md` //! REQ-2 prerequisite, blocker #152). These pin the faithful production exec - //! shapes the exec-TV teeth (`thermite-tv/tests/exec_teeth.rs` E1–E4) wrap as + //! shapes that the exec-TV negative tests (`thermite-tv/tests/exec_teeth.rs` + //! E1–E4) wrap as //! `P_production` — proving the exec `Ctx` is reachable for a standalone expr //! (the #1 feasibility unknown) and that the #122 inner-paren + #146 cast-`<` - //! outer-paren disciplines fire in exec position. The teeth-test (in the + //! outer-paren disciplines fire in exec position. The negative test (in the //! independent `thermite-tv`, no `thermite-lower` dep) cannot import this, so //! these tests are the cross-crate bridge that the faithful strings it hardcodes - //! do match the real production lowering (R-CHAR-3 — the faithful column traces + //! do match the production lowering (R-CHAR-3 — the faithful column traces //! to production here, the reference to `exec_encode`). use super::*; use thermite_syntax::ast::{BinOp, Expr, IndexArg, PrimType, Type}; @@ -9705,17 +9706,17 @@ mod exec_expr_tests { mod exec_body_tests { //! `lower_exec_body` per-body straight-line exec lowering //! (`.design/verified/exec-stmt-tv.md` REQ-3, blocker #161; epic #158). These - //! pin the faithful production exec body shapes the body-TV teeth + //! pin the faithful production exec body shapes that the body-TV negative tests //! (`thermite-tv/tests/body_teeth.rs` B1-B4) wrap as `P_production` - proving //! the body exec `Ctx` is reachable for a standalone straight-line `Block` (the //! #161 feasibility unknown: `lower_block_inner` is private + fn-context-bound, //! reached here through the minimal `Ctx::exec()` frame) and that the //! `let`/`mut`-let / assignment / `if`-statement / tail thread the same exec - //! path the fn body uses. The teeth-test (in the independent `thermite-tv`, no + //! path the fn body uses. The negative test (in the independent `thermite-tv`, no //! `thermite-lower` dep) cannot import this, so these tests are the cross-crate - //! bridge that the faithful strings it hardcodes do match the real production + //! bridge that the faithful strings it hardcodes do match the production //! lowering (R-CHAR-3 - the faithful column traces to production here, the - //! reference to `exec_stmt_encode`). A loop body is an honest `Err` (the frozen + //! reference to `exec_stmt_encode`). A loop body is an `Err` (the frozen //! 2.2.1-vs-2.2.2 boundary), never a silent lowering. use super::*; use thermite_syntax::ast::{BinOp, Block, Clause, Expr, LoopKind, LoopNode, Stmt}; @@ -9829,7 +9830,7 @@ mod exec_body_tests { } // frozen-subset honesty (REQ-1): a body containing a `Stmt::Loop` is out of the - // 2.2.1 straight-line slice (step 2.2.2). `lower_exec_body` returns an honest + // 2.2.1 straight-line slice (step 2.2.2). `lower_exec_body` returns an // `Err` (via `lower_stmt`'s `Stmt::Loop` arm), never a silent / wrong lowering. #[test] fn loop_body_is_err_not_silent() { diff --git a/thermite-lower/tests/adt_lower_conformance.rs b/thermite-lower/tests/adt_lower_conformance.rs index 94373b4ba..456f69653 100644 --- a/thermite-lower/tests/adt_lower_conformance.rs +++ b/thermite-lower/tests/adt_lower_conformance.rs @@ -108,7 +108,7 @@ fn lower_and_verify(name: &str) -> String { // // REQ-8: `deposit` lowers to a `pub struct Account` + the `well_formed` invariant // predicate, with the invariant threaded (OQ-3 automatic threading) into -// `requires`/`ensures`; real verus verifies it (L3). The cert oracle says L3, +// `requires`/`ensures`; verus verifies it (L3). The cert oracle says L3, // pure, non-vacuous. #[test] @@ -196,7 +196,7 @@ fn deposit_matches_cert_oracle_stable_subset() { // ---- AC-2: enum + match + is → Verus enum/match/is, verifies (L3) ---------- // // REQ-9: `is_circle` lowers to a Verus `enum Shape`, an enum-qualified `match`, -// and the `s is Circle` discriminant test; real verus verifies it (L3). The cert +// and the `s is Circle` discriminant test; verus verifies it (L3). The cert // oracle (`conformance/shape.cert.json`) says L3, pure, non-vacuous. #[test] @@ -254,7 +254,7 @@ fn is_circle_matches_cert_oracle_stable_subset() { // REQ-10: `list_sum` lowers to a Verus recursive `enum List` with `Box` at // the recursive occurrence, and `spec fn sum_list` carries `decreases l` (the // datatype value, Verus's built-in structural order) recursing through `*t`; -// real verus verifies it (terminates + totals). No fn cert (a spec-fn-only +// verus verifies it (terminates + totals). No fn cert (a spec-fn-only // program), so the oracle is verus itself. #[test] @@ -438,7 +438,7 @@ fn assert_no_cheats(emitted: &str, name: &str) { // // The ADT additions are purely additive (new `Item`/`Expr`/`Pattern`/`Type` and // new error arms; no existing node reshapes). The non-ADT corpus must still lower -// to Verus that real verus verifies, and the key contract substrings must be +// to Verus that verus verifies, and the key contract substrings must be // present (no weakening). Verification is by verus rather than a byte-match of the // emitted source against the golden — the verify-not-byte-match practice the // existing `lower_conformance.rs` uses (the goldens are design-authored diff --git a/thermite-lower/tests/adt_schemes_conformance.rs b/thermite-lower/tests/adt_schemes_conformance.rs index 8e22fb043..5bf7a7271 100644 --- a/thermite-lower/tests/adt_schemes_conformance.rs +++ b/thermite-lower/tests/adt_schemes_conformance.rs @@ -220,7 +220,7 @@ fn multiplier_instance_cites_the_generated_law_no_fresh_induction() { } /// Negative control (AC-2 / R-DEFER-9, §7): the generated law minus its per-node -/// premise fails verus — the per-node premise is load-bearing, the induction is +/// premise fails verus — the per-node premise is required, the induction is /// not vacuous. We take the emitted lowering, strip the `forall|…| f(x, acc) <= /// acc + b` premise line from `fold_bound_list`, and assert verus reports an /// error (the oracle `multiplier` expectation: dropping the premise → 1 error). diff --git a/thermite-lower/tests/boundary_gate_verified.rs b/thermite-lower/tests/boundary_gate_verified.rs index 6bf00f555..e97c7dcc3 100644 --- a/thermite-lower/tests/boundary_gate_verified.rs +++ b/thermite-lower/tests/boundary_gate_verified.rs @@ -36,7 +36,7 @@ const EXTERNAL_BODY: &str = "#[verifier::external_body]"; /// onto the AST (the parser does not stack two attributes, but a `FnItem` can /// carry both flags, and the production gate sees both). /// -/// - `(false, false)` — a regular in-language fn with a real body. +/// - `(false, false)` — a regular in-language fn with a body. /// - `(true, *)` — a `#[boundary("ext::ext_id")]` fn (foreign body, `;`). /// - `(false, true)` — a `#[slag(...)]` fn with a fiat body. fn fn_source(has_boundary: bool, has_slag: bool) -> String { @@ -118,7 +118,7 @@ fn lower_fn_emits_external_body_iff_proved_predicate() { /// The §9 soundness corollary made observable (REQ-9): the regular fn (neither /// flag) takes the fully-proved-body arm — its emitted source contains no -/// `#[verifier::external_body]` and carries the fn's real body (`{`), so the +/// `#[verifier::external_body]` and carries the fn's body (`{`), so the /// lying-regular-body laundering R-DEFER-9 forbids is structurally impossible. #[test] fn regular_fn_is_fully_proved_never_external_body() { diff --git a/thermite-lower/tests/bytes_eq_conformance.rs b/thermite-lower/tests/bytes_eq_conformance.rs index 557e2b432..4c4e33806 100644 --- a/thermite-lower/tests/bytes_eq_conformance.rs +++ b/thermite-lower/tests/bytes_eq_conformance.rs @@ -21,8 +21,8 @@ //! shifted-suffix) each certify L3 with one `lemma_bytes_eq_bridge` citation, zero //! per-conjunct glue. //! AC-16: the length-preserving head/tail-swap mutant fails verus (non-vacuity, -//! R-DEFER-9) — the pins are content teeth a length pin cannot fake; and without the -//! REQ-19 citation the pins fail (the bridge is load-bearing, not decorative). +//! R-DEFER-9). The content pins catch mutations that preserve length; without the +//! REQ-19 citation the pins fail (the bridge is required, not decorative). //! //! The verus checks skip when verus is absent (the `string_conformance.rs` //! precedent) rather than panic on a missing solver. `tests/` is not anti-pattern-gated, @@ -139,7 +139,7 @@ fn bytes_eq_demo_emits_def_and_lemmas_and_citation() { "the prove-once lemma {lemma} is emitted (REQ-18):\n{emitted}" ); } - // The load-bearing explicit trigger on the arithmetic index (REQ-18 recorded finding). + // The required explicit trigger on the arithmetic index (REQ-18 recorded finding). assert!( emitted.contains("#[trigger] a[ai + k] == b[bi + k]"), "the load-bearing explicit #[trigger] a[ai + k] (REQ-18):\n{emitted}" @@ -256,7 +256,7 @@ fn bytes_eq_demo_matches_cert_oracle() { ); } -// ---- AC-13/AC-14: the emitted lowering verifies under real verus (L3) -------- +// ---- AC-13/AC-14: the emitted lowering verifies under verus (L3) -------- #[test] fn bytes_eq_demo_verifies_l3_under_real_verus() { @@ -285,7 +285,7 @@ fn bytes_eq_demo_verifies_l3_under_real_verus() { // // The length-preserving swap (`tail.concat(ins).concat(head)`) keeps every length // identity but breaks the byte-content pins — the design's `15 verified, 1 errors` -// mutant, here through the real pipeline (the body is mutated in the lowered source, +// mutant, here through the pipeline (the body is mutated in the lowered source, // the contract + the prove-once lemmas unchanged). If verus still passed the mutant, // the pins would be vacuous (a length pin a content pin cannot distinguish), R-DEFER-9. diff --git a/thermite-lower/tests/collections_conformance.rs b/thermite-lower/tests/collections_conformance.rs index a2d1b900a..7f6a6d76b 100644 --- a/thermite-lower/tests/collections_conformance.rs +++ b/thermite-lower/tests/collections_conformance.rs @@ -152,7 +152,7 @@ fn vec_demo_lowers_wrapper_and_verifies_l3() { ); assert_no_cheats(&emitted, "vec_demo"); - // The external truth: real verus verifies the emitted output (R-CODE-4 — exit + // The external truth: verus verifies the emitted output (R-CODE-4 — exit // status checked, never swallowed). match verify("vec_demo_collections", &emitted) { Some((ok, output)) => { diff --git a/thermite-lower/tests/divergence_bytes_eq_l1_empty_window.rs b/thermite-lower/tests/divergence_bytes_eq_l1_empty_window.rs index a74e8bc3d..5544d5f37 100644 --- a/thermite-lower/tests/divergence_bytes_eq_l1_empty_window.rs +++ b/thermite-lower/tests/divergence_bytes_eq_l1_empty_window.rs @@ -4,7 +4,7 @@ //! //! Authority — `.design/basis/07-strings.md` REQ-20 (the L1 exec twin): the twin //! is "a bounds-checked byte-compare loop over the runtime `TString`s computing -//! the **SAME value as the spec body**"; the sanctioned exception is that "an +//! the **same value as the spec body**"; the sanctioned exception is that "an //! out-of-bounds runtime **index** is a check failure, not UB". The spec body //! (REQ-18, emitted verbatim by `emit_bytes_eq_defs`) is //! @@ -24,7 +24,7 @@ //! `forge build` binary panics at runtime on that very same certified `ens` //! ("thermite L1 contract violation [ens]"). A verus-proven postcondition //! failing its own always-active runtime check is the check/build -//! value-divergence REQ-20's "SAME value" clause exists to forbid (distinct +//! value-divergence REQ-20's "same value" clause exists to forbid (distinct //! from #280, which is a compile failure on the `&`-field spelling). //! //! Expected value derivation (R-CHAR-3): `bytes_eq(_, _, 5, 5, 0)` = `true` is diff --git a/thermite-lower/tests/divergence_combinator_private_vs_pub_open_spec_fn.rs b/thermite-lower/tests/divergence_combinator_private_vs_pub_open_spec_fn.rs index c9109fa1a..bf37486cb 100644 --- a/thermite-lower/tests/divergence_combinator_private_vs_pub_open_spec_fn.rs +++ b/thermite-lower/tests/divergence_combinator_private_vs_pub_open_spec_fn.rs @@ -17,7 +17,7 @@ //! real-verus proof, so the emitted sub-program for a validator-legal item //! must verify; plus `.design/lower/verus-lowering.md` REQ-8's own grounding //! finding (cited by 2f327b63 itself): "a `pub open` body may refer only to -//! `pub` items". The assertion is outcome-anchored (real verus on the emitted +//! `pub` items". The assertion is outcome-anchored (verus on the emitted //! unit, the conformance-harness pattern), not anchored to any particular fix //! direction; never copied from lowerer output. //! diff --git a/thermite-lower/tests/divergence_effects.rs b/thermite-lower/tests/divergence_effects.rs index fc3d85219..f794c242a 100644 --- a/thermite-lower/tests/divergence_effects.rs +++ b/thermite-lower/tests/divergence_effects.rs @@ -2,7 +2,7 @@ //! (`thermite-lower/src/effects.rs`, commit 5b0967f). //! //! Authority: `thermite-design.md` §4.1 — "Effect rows compose: a caller's row -//! must subsume EVERY callee's row, checked at compile time"; and §9 (trust +//! must subsume every callee's row, checked at compile time"; and §9 (trust //! invariant under composition). Governing contract: //! `.design/lower/effect-subsumption.md` REQ-2/REQ-3 + OQ-2 (direct per-call-site //! checking composes to transitive correctness only if every reachable call site @@ -67,7 +67,7 @@ fn fn_with_body(name: &str, fx: EffectRow, body: Block) -> Item { // Divergence 1 (crosslink #38): a callee invoked in a `while` loop condition // escapes the subsumption check. // -// §4.1: "a caller's row must subsume EVERY callee's row". A `while { }` +// §4.1: "a caller's row must subsume every callee's row". A `while { }` // evaluates `` at runtime before each iteration; a `Call` inside it is a // reachable callee. `ast.rs` models this as `LoopKind::While(Box)`, and // `lower.rs` itself lowers the condition (`LoopKind::While(c)` arm). But diff --git a/thermite-lower/tests/divergence_golden_intlit.rs b/thermite-lower/tests/divergence_golden_intlit.rs index 94bc991c9..4d27d842b 100644 --- a/thermite-lower/tests/divergence_golden_intlit.rs +++ b/thermite-lower/tests/divergence_golden_intlit.rs @@ -1,12 +1,12 @@ //! Divergence: the lowering golden oracle for `sum.th` is stale with respect to //! the current emitter on the `1_000_000` literal. //! -//! Authority (goal.md §B / "Why the critic still has teeth"): the Verus/L1 +//! Authority: goal.md §B. The Verus/L1 //! golden files under `tests/golden/` are the external lowering oracle — the -//! emitted source must match the golden. ast.md REQ-6 CRITICAL note pins the +//! emitted source must match the golden. ast.md REQ-6 critical note pins the //! intended form: lowering "continues to emit the numeric `value` (e.g. -//! `1000000`), NOT the raw — so the `tests/golden/lower/*.verus.rs` files do -//! NOT change". So the authority says both (a) the emitter emits `1000000` and +//! `1000000`), not the raw — so the `tests/golden/lower/*.verus.rs` files do +//! not change". So the authority says both (a) the emitter emits `1000000` and //! (b) the golden contains `1000000`. //! //! Reality (a2c0f73): the emitter emits `1000000` (correct, AC-1b), but the @@ -23,10 +23,10 @@ //! divergence on `main` is ours, no "pre-existing safe to defer"). The fix is //! the generator's: either regenerate the golden's executable expression to //! the emitted `1000000`, or, if the design intends the golden to keep the -//! verbatim separators, amend ast.md's CRITICAL note (R-SPEC-4). The critic +//! verbatim separators, amend ast.md's critical note (R-SPEC-4). The critic //! does not choose; it pins the disagreement. //! -//! Expected values are hand-derived from ast.md REQ-6 CRITICAL note (`1000000`, +//! Expected values are hand-derived from ast.md REQ-6 critical note (`1000000`, //! the `_`-stripped value), never copied from the emitter (R-CHAR-3). use std::path::PathBuf; diff --git a/thermite-lower/tests/divergence_intlit_lower.rs b/thermite-lower/tests/divergence_intlit_lower.rs index 7bc445720..192397475 100644 --- a/thermite-lower/tests/divergence_intlit_lower.rs +++ b/thermite-lower/tests/divergence_intlit_lower.rs @@ -1,9 +1,9 @@ //! Adversarial audit of #37 — the regression probe: lowering emits the //! numeric `value` in the executable expression, not the verbatim `raw`. //! -//! ast.md REQ-6 CRITICAL note: "the thermite-lower lowering … continues to -//! emit the numeric `value` (e.g. `1000000`), NOT the raw — so the -//! `tests/golden/lower/*.verus.rs` files do NOT change". This test pins that +//! ast.md REQ-6 critical note: "the thermite-lower lowering … continues to +//! emit the numeric `value` (e.g. `1000000`), not the raw — so the +//! `tests/golden/lower/*.verus.rs` files do not change". This test pins that //! invariant directly on a fresh `_`-bearing literal. //! //! Critic note on assertion shape: the L1 lowering also embeds the diff --git a/thermite-lower/tests/divergence_l1.rs b/thermite-lower/tests/divergence_l1.rs index 729506fd7..af27cf92e 100644 --- a/thermite-lower/tests/divergence_l1.rs +++ b/thermite-lower/tests/divergence_l1.rs @@ -4,7 +4,7 @@ //! R-DEFER-9 (R-CHAR-3 — expected values hand-derived or from the corpus, never //! copied from toolchain output). These are not in the builder's //! `l1_conformance.rs`; they probe corners that file may have missed: -//! a real runtime violation on a fresh program (not the corpus corrupted body), +//! a runtime violation on a fresh program (not the corpus corrupted body), //! release-profile (`-O`) check survival, generality on a renamed program, and //! combinator edge cases (`exists_in` short-circuit, `n > len`, `permutation_of` //! duplicates). @@ -78,7 +78,7 @@ fn lower_str(src: &str) -> String { // program (not the corpus corrupted-body). Authority: l1-runtime-checks.md // REQ-2 / AC-1 ("a violating body fires the violation handler … observable, not // silent"); §6 ("Violations detected at the call site"); R-DEFER-9 (the check -// must be a real obligation, not a no-op). +// must be a obligation, not a no-op). // // Program: a fn whose `ens result == 0` is violated by a body returning its // input. (Clause order req-before-ens satisfied with a trivially-true `req`.) diff --git a/thermite-lower/tests/divergence_lower.rs b/thermite-lower/tests/divergence_lower.rs index 45801faa1..578f3e882 100644 --- a/thermite-lower/tests/divergence_lower.rs +++ b/thermite-lower/tests/divergence_lower.rs @@ -9,11 +9,11 @@ //! These probes author new Thermite programs that are structurally identical to //! the corpus shapes but use different names/predicates, lower them, and assert //! the emitted proof aids reference the new names (shape-derivation) and that the -//! emitted Verus verifies with the real binary (REQ-8: verify, don't byte-match). +//! emitted Verus verifies with the binary (REQ-8: verify, don't byte-match). //! //! Expected behavior traces to verus-lowering.md REQ-7 ("derives the needed -//! proof aids from the program's AST/contract SHAPE — never from its identity") -//! and AC-1/AC-2 ("proof aids are shape-general (REQ-7), NOT per-program +//! proof aids from the program's AST/contract shape — never from its identity") +//! and AC-1/AC-2 ("proof aids are shape-general (REQ-7), not per-program //! hardcoded"). A template that emits a canned `lemma_sum_push` / `haystack` //! blob for a renamed-but-structurally-identical program is the over-fitting //! divergence (R-CHAR-3: the expected new name comes from the input program, not @@ -21,7 +21,7 @@ //! //! Audit result (loop 4): all probes pass — the templates derive aids from //! shape (emitting `lemma_tally_push`/`data@`/`left`/`key`, not the corpus -//! identities) and the renamed programs verify under real verus. These are +//! identities) and the renamed programs verify under verus. These are //! retained as committed regression evidence of the generality claim, not as //! divergence pins (no divergence found). diff --git a/thermite-lower/tests/divergence_spec_call_cast_proof_aid_paths.rs b/thermite-lower/tests/divergence_spec_call_cast_proof_aid_paths.rs index c04d75b86..f268f8c6a 100644 --- a/thermite-lower/tests/divergence_spec_call_cast_proof_aid_paths.rs +++ b/thermite-lower/tests/divergence_spec_call_cast_proof_aid_paths.rs @@ -1,7 +1,7 @@ //! Divergence pins (crosslink #229) — the #225/#227 type-directed spec-call //! narrowing cast (`Ctx::spec_call_param_cast` fed by `spec_fn_param_type_map`) //! is still not threaded to every spec-context lowering site. #227 (commit -//! `1c59e4fd`) claims "narrowing target = callee's declared param type at ALL +//! `1c59e4fd`) claims "narrowing target = callee's declared param type at all //! six spec-context entry points", but the proof-aid emission paths re-lower //! contract clauses with a bare un-threaded `Ctx::spec_seq()`: //! diff --git a/thermite-lower/tests/divergence_struct_inv_cast_lt.rs b/thermite-lower/tests/divergence_struct_inv_cast_lt.rs index aa15c1cc1..cc00ce230 100644 --- a/thermite-lower/tests/divergence_struct_inv_cast_lt.rs +++ b/thermite-lower/tests/divergence_struct_inv_cast_lt.rs @@ -10,7 +10,7 @@ //! //! The struct type-invariant path (`lower_inv_expr` → `lower_inv_operand`, the //! REQ-8 `well_formed()` predicate, `.design/lower/...` struct invariants) has its -//! own operand-parenthesizer, which #148 extended with the SAME cast-`<` guard +//! own operand-parenthesizer, which #148 extended with the same cast-`<` guard //! (`lower_inv_operand` in `thermite-lower/src/lower.rs`: a `Cast` left operand of //! an `is_lt_leading` parent is parenthesized — R-DEFER-8, the convention is now //! uniform across every emission site). So `} inv (x as u32) < cap` lowers to the diff --git a/thermite-lower/tests/equivalence_obligation.rs b/thermite-lower/tests/equivalence_obligation.rs index 5ec6381fe..acf75c264 100644 --- a/thermite-lower/tests/equivalence_obligation.rs +++ b/thermite-lower/tests/equivalence_obligation.rs @@ -5,7 +5,7 @@ //! rendered into a Verus equivalence obligation that verifies for the equivalent //! case and fails (counterexample) for the distinguishing case. The decision is //! the real `verus` verdict (R-DEFER-9 — exclude only on a proof), so this test -//! shells the real binary. It skips when verus is absent (mirroring +//! shells the binary. It skips when verus is absent (mirroring //! `lower_conformance.rs`), never panics. //! //! Expected verdicts are hand-derived from the design's *Ground the path* diff --git a/thermite-lower/tests/l2_conformance.rs b/thermite-lower/tests/l2_conformance.rs index 5cab7b4c8..1a2c08acc 100644 --- a/thermite-lower/tests/l2_conformance.rs +++ b/thermite-lower/tests/l2_conformance.rs @@ -3,7 +3,7 @@ //! (`.design/lower/l2-kani.md` AC-1/AC-2/AC-3/AC-5). For each corpus program: //! parse it, `lower_l2` it, write the emitted harness into a throwaway cargo //! crate, run `cargo kani --output-format terse`, and assert -//! `VERIFICATION:- SUCCESSFUL` (the contract holds for all inputs up to the +//! `verification:- successful` (the contract holds for all inputs up to the //! bound) at the design-pinned bound `N = 4` (`unwind(5)` for `sum`'s //! `while`, `unwind(6)` for `binary_search`'s `loop`). //! @@ -11,7 +11,7 @@ //! kani-spawning tests skip (a diagnostic + early return, not `#[ignore]`) //! when kani is absent, mirroring the verus-absent skip in `lower_conformance.rs` //! (`.design/lower/l2-kani.md` REQ-8). The pure emitter shape assertions (no kani -//! spawn) run unconditionally. Expected `VERIFICATION:- SUCCESSFUL` / the +//! spawn) run unconditionally. Expected `verification:- successful` / the //! counterexample markers trace to the grounded real-kani runs (R-CHAR-3 — Kani's //! own format, not forge's output). `unwrap`/`expect` are fine here (`tests/` is //! not anti-pattern-gated). diff --git a/thermite-lower/tests/req_bounded_mul_aid.rs b/thermite-lower/tests/req_bounded_mul_aid.rs index cfa5f41e8..c838cec30 100644 --- a/thermite-lower/tests/req_bounded_mul_aid.rs +++ b/thermite-lower/tests/req_bounded_mul_aid.rs @@ -5,9 +5,9 @@ //! overflow obligation, even `n * n` under `req n <= 30` (probed live against //! verus 0.2026.05.24). The lowerer emits, at the start of the block that //! contains the product, one -//! `assert((EXPR) <= BOUND) by(nonlinear_arith) requires ;` +//! `assert((EXPR) <= bound) by(nonlinear_arith) requires ;` //! whose `requires` are exactly the req conjuncts the bound depends on (no -//! invented bound) and whose `BOUND` is the syntactic product of those conjuncts' +//! invented bound) and whose `bound` is the syntactic product of those conjuncts' //! constants. The aid can only fail, never prove a false thing (R-DEFER-9). //! //! Expected values are hand-derived from the design contract + the user's diff --git a/thermite-lower/tests/string_conformance.rs b/thermite-lower/tests/string_conformance.rs index d3c44e257..c8d129adc 100644 --- a/thermite-lower/tests/string_conformance.rs +++ b/thermite-lower/tests/string_conformance.rs @@ -166,7 +166,7 @@ fn string_demo_lowers_wrapper_and_verifies_l3() { ); assert_no_cheats(&emitted, "string_demo"); - // The external truth: real verus verifies the emitted output (R-CODE-4 — exit + // The external truth: verus verifies the emitted output (R-CODE-4 — exit // status checked, never swallowed). match verify("string_demo_strings", &emitted) { Some((ok, output)) => { diff --git a/thermite-skill/src/generate.rs b/thermite-skill/src/generate.rs index 78b84cab4..c514e6917 100644 --- a/thermite-skill/src/generate.rs +++ b/thermite-skill/src/generate.rs @@ -13,15 +13,15 @@ //! Forge command set, (4) the ladder semantics, (5) the slag rules, (6) the //! Stage-1 forge tier (the seven verdicts, per-clause routing, covenant authoring, //! and the forge-tier verbs + burn receipt; `.design/stage1-forge-tier.md`) — into -//! one deterministic `String`. The SURFACE INVENTORY is DYNAMIC by two +//! one deterministic `String`. The surface INVENTORY is DYNAMIC by two //! compiler-backed mechanisms (REQ-8): (i) **registry-driven** — section (2) //! iterates `thermite_spec::all()` and (2b) iterates //! `thermite_spec::schemes::all()`, so a new registry entry auto-appears (REQ-2, //! REQ-9); (ii) **exhaustive-match-driven** — section (1)'s construct inventory -//! is rendered by an EXHAUSTIVE `match` (no `_` wildcard) over the definitional +//! is rendered by an exhaustive `match` (no `_` wildcard) over the definitional //! enums `thermite_syntax::{Type,Expr,Item,Pattern,Effect}` (+ `BinOp`/ -//! `PrimType`), so a NEW variant FAILS TO COMPILE until its skill arm is added -//! (REQ-10 — the compiler is the freshness enforcer). The explanatory PROSE (the +//! `PrimType`), so a new variant fails TO compile until its skill arm is added +//! (REQ-10 — the compiler is the freshness enforcer). The explanatory prose (the //! framing, the ladder, the slag rules, the forge verb table) stays curated, //! guarded by the freshness + budget tests (REQ-11). No I/O, no env, no //! wall-clock, no RNG — a pure function of the compiled-in text, the static @@ -95,7 +95,7 @@ pub fn token_count(s: &str) -> usize { /// The sections appear in `thermite-design.md` §10 order: (1) surface grammar, /// (2) the SpecTherm combinator library, (2b) the recursion-scheme library, (3) /// the Forge command set, (4) the ladder semantics, (5) the slag rules, (6) the -/// Stage-1 forge tier. Section (1)'s construct inventory is EXHAUSTIVE-MATCH-driven +/// Stage-1 forge tier. Section (1)'s construct inventory is exhaustive-MATCH-driven /// over the `thermite_syntax` enums (REQ-10), (2) is registry-driven from /// `thermite_spec::all()` (REQ-2), (2b) is registry-driven from /// `thermite_spec::schemes::all()` (REQ-9); the curated prose (the framing, ladder, @@ -149,7 +149,7 @@ forge tier (verdicts, routing, covenants, proofs). /// One rendered surface-construct entry (REQ-10): the per-variant fragment an /// exhaustive-`match` renderer emits for a single language construct — a concise /// grammar `fragment`, a one-line `description`, and a tiny `example`. The text -/// is a deterministic function of the VARIANT (not of any payload value), so the +/// is a deterministic function of the variant (not of any payload value), so the /// rendered inventory is pure (R-CODE-5, AC-6). struct SkillFragment { /// The grammar fragment for this construct (e.g. `&[T]`, `match e { … }`). @@ -161,10 +161,10 @@ struct SkillFragment { } impl SkillFragment { - /// Render this fragment as one markdown bullet WITH its example (the per-construct + /// Render this fragment as one markdown bullet with its example (the per-construct /// row of the REQ-10 inventory): the grammar fragment + description, then a tiny /// example. Used (via [`render_inventory_complete_examples`]) for the `Type` arms - /// whose example is a COMPLETE, copy-pasteable item — chiefly the `fn log() -> () + /// whose example is a complete, copy-pasteable item — chiefly the `fn log() -> () /// req true ens true fx pure { }` that the §10 parse-clean pin guards. Every other /// inventory (items, expressions, primitive scalars, operators, patterns, effects) /// renders via [`to_bullet_terse`](SkillFragment::to_bullet_terse) to stay under @@ -178,7 +178,7 @@ impl SkillFragment { ) } - /// Render this fragment as one markdown bullet WITHOUT its example — the + /// Render this fragment as one markdown bullet without its example — the /// fragment + description only. The budget-tightening form (`thermite-design.md` /// §2.2: the ≤ 6,000-token hard gate) for the leaf inventories whose `fragment` /// already shows the surface syntax (`a + b`, `read(path)`, `[head, ..tail]`), so @@ -194,14 +194,14 @@ impl SkillFragment { } } -/// Render ONE `Type` variant's surface fragment (REQ-10). +/// Render one `Type` variant's surface fragment (REQ-10). /// -/// EXHAUSTIVE `match` over `thermite_syntax::ast::Type` with NO `_` wildcard arm: +/// exhaustive `match` over `thermite_syntax::ast::Type` with no `_` wildcard arm: /// adding a new `Type` variant (e.g. the deferred `Type::Map`, ast.rs REQ-2) -/// makes this `match` non-exhaustive, a HARD `rustc` `E0004` compile error in +/// makes this `match` non-exhaustive, a hard `rustc` `E0004` compile error in /// `thermite-skill`, until its arm is added — the compiler is the freshness /// enforcer (REQ-8, AC-10(i)). Payload is field-elided (`{ .. }` / `(_)`); the -/// elision does not weaken exhaustiveness (the compiler checks the VARIANT set). +/// elision does not weaken exhaustiveness (the compiler checks the variant set). fn render_type_arm(ty: &Type) -> SkillFragment { match ty { Type::Prim(_) => SkillFragment { @@ -230,8 +230,8 @@ fn render_type_arm(ty: &Type) -> SkillFragment { example: "-> Wrapper", }, // Cluster C7 (`.design/basis/09-option-result.md` REQ-1/REQ-2): the built-in - // optional / fallible primitives are dedicated `Type` nodes (NOT a - // string-named `Generic`), so each renders ITS OWN surface fragment — the + // optional / fallible primitives are dedicated `Type` nodes (not a + // string-named `Generic`), so each renders ITS own surface fragment — the // construct + payload-in-contract surface an agent reads. Type::Option(_) => SkillFragment { fragment: "Option", @@ -244,9 +244,9 @@ fn render_type_arm(ty: &Type) -> SkillFragment { example: "-> Result", }, // Cluster C12 (`.design/basis/13-map.md` REQ-1/REQ-5): the bounded verified - // key-value primitive `Map` — the SECOND two-type-arg node. insert/get/ - // contains_key/len; get returns Option (absent key -> None, NOT a wrong - // value); insert carries fx alloc. Renders its OWN surface fragment. + // key-value primitive `Map` — the second two-type-arg node. insert/get/ + // contains_key/len; get returns Option (absent key -> None, not a wrong + // value); insert carries fx alloc. Renders its own surface fragment. Type::Map(_, _) => SkillFragment { fragment: "Map", description: "a bounded verified key-value map (insert/get/contains_key/len; get -> Option, absent -> None; fx alloc)", @@ -283,8 +283,8 @@ fn render_type_arm(ty: &Type) -> SkillFragment { } } -/// Render ONE `PrimType` leaf's surface fragment (REQ-10): the exhaustive `match` -/// over the closed primitive set so a NEW primitive also compile-forces an entry. +/// Render one `PrimType` leaf's surface fragment (REQ-10): the exhaustive `match` +/// over the closed primitive set so a new primitive also compile-forces an entry. fn render_prim_arm(prim: PrimType) -> SkillFragment { match prim { PrimType::U32 => SkillFragment { @@ -310,8 +310,8 @@ fn render_prim_arm(prim: PrimType) -> SkillFragment { } } -/// Render ONE `Item` variant's surface fragment (REQ-10): exhaustive `match` over -/// `thermite_syntax::ast::Item`, NO `_` arm — a new top-level item kind +/// Render one `Item` variant's surface fragment (REQ-10): exhaustive `match` over +/// `thermite_syntax::ast::Item`, no `_` arm — a new top-level item kind /// compile-forces a skill entry (REQ-8, AC-10). fn render_item_arm(item: &Item) -> SkillFragment { match item { @@ -346,8 +346,8 @@ fn render_item_arm(item: &Item) -> SkillFragment { } } -/// Render ONE `Expr` variant's surface fragment (REQ-10): exhaustive `match` over -/// `thermite_syntax::ast::Expr`, NO `_` arm — a new expression form compile-forces +/// Render one `Expr` variant's surface fragment (REQ-10): exhaustive `match` over +/// `thermite_syntax::ast::Expr`, no `_` arm — a new expression form compile-forces /// a skill entry (REQ-8, AC-10). fn render_expr_arm(expr: &Expr) -> SkillFragment { match expr { @@ -443,8 +443,8 @@ fn render_expr_arm(expr: &Expr) -> SkillFragment { example: "let s: String = \"hello\";", }, // Cluster C9-B (`.design/basis/10-recursion-tuples.md` REQ-5/REQ-8): the - // tuple construction + the projection access form. Projection (NOT - // destructuring) is the v1 tuple access; it reads in BOTH exec and contract + // tuple construction + the projection access form. Projection (not + // destructuring) is the v1 tuple access; it reads in both exec and contract // (`ens result.0 == b`). Expr::Tuple(_) => SkillFragment { fragment: "(a, b, ..)", @@ -468,8 +468,8 @@ fn render_expr_arm(expr: &Expr) -> SkillFragment { } } -/// Render ONE `BinOp` leaf's surface fragment (REQ-10): exhaustive `match` so a -/// NEW operator compile-forces a skill entry. Comparisons are non-associative +/// Render one `BinOp` leaf's surface fragment (REQ-10): exhaustive `match` so a +/// new operator compile-forces a skill entry. Comparisons are non-associative /// (`a < b < c` is a parse error). fn render_binop_arm(op: BinOp) -> SkillFragment { match op { @@ -566,8 +566,8 @@ fn render_binop_arm(op: BinOp) -> SkillFragment { } } -/// Render ONE `UnaryOp` leaf's surface fragment (REQ-10, #92): exhaustive `match` -/// so a NEW prefix operator compile-forces a skill entry. There is ONE +/// Render one `UnaryOp` leaf's surface fragment (REQ-10, #92): exhaustive `match` +/// so a new prefix operator compile-forces a skill entry. There is one /// `UnaryOp::Not` (the prefix `!`), whose meaning is per the operand type /// (logical-not on `bool`, bitwise-not on an integer; ast.md OQ-4); it binds /// tighter than every binary operator (`surface-grammar.md` REQ-10). @@ -586,8 +586,8 @@ fn unaryop_inventory() -> [UnaryOp; 1] { [UnaryOp::Not] } -/// Render ONE `Pattern` variant's surface fragment (REQ-10): exhaustive `match` -/// over `thermite_syntax::ast::Pattern`, NO `_` arm. +/// Render one `Pattern` variant's surface fragment (REQ-10): exhaustive `match` +/// over `thermite_syntax::ast::Pattern`, no `_` arm. fn render_pattern_arm(pat: &Pattern) -> SkillFragment { match pat { Pattern::Wildcard => SkillFragment { @@ -631,8 +631,8 @@ fn render_pattern_arm(pat: &Pattern) -> SkillFragment { } } -/// Render ONE `Effect` atom's surface fragment (REQ-10): exhaustive `match` over -/// `thermite_syntax::ast::Effect`, NO `_` arm — a new effect atom compile-forces +/// Render one `Effect` atom's surface fragment (REQ-10): exhaustive `match` over +/// `thermite_syntax::ast::Effect`, no `_` arm — a new effect atom compile-forces /// a skill entry (REQ-8, AC-10). A caller's row must subsume every callee's row. fn render_effect_arm(effect: &Effect) -> SkillFragment { match effect { @@ -684,11 +684,11 @@ fn render_effect_arm(effect: &Effect) -> SkillFragment { } } -/// The representative `Type` variants the REQ-10 inventory enumerates. ONE value +/// The representative `Type` variants the REQ-10 inventory enumerates. one value /// per `Type` variant — the `match` in `render_type_arm` is what the compiler -/// checks for exhaustiveness; this list is what the OUTPUT covers. Payload is the +/// checks for exhaustiveness; this list is what the output covers. Payload is the /// cheapest legal filler (the arm text is payload-independent, AC-6). If a new -/// `Type` variant is added, `render_type_arm`'s `match` fails to compile FIRST +/// `Type` variant is added, `render_type_arm`'s `match` fails to compile first /// (REQ-8); this list is then extended to render it. fn type_inventory() -> Vec { vec![ @@ -710,19 +710,19 @@ fn type_inventory() -> Vec { // Cluster C7 (`.design/basis/09-option-result.md` REQ-1/REQ-2): one // representative each of the built-in `Option` / `Result` nodes so // the REQ-10 inventory renders their fragments (the `match` in - // `render_type_arm` is the exhaustiveness oracle; this list is the OUTPUT + // `render_type_arm` is the exhaustiveness oracle; this list is the output // cover). The payload is the cheapest legal filler. Type::Option(Box::new(Type::Unit)), Type::Result(Box::new(Type::Unit), Box::new(Type::Unit)), // Cluster C12 (`.design/basis/13-map.md` REQ-1/REQ-5): a representative // `Map` node so the REQ-10 inventory renders its fragment (the `match` - // in `render_type_arm` is the exhaustiveness oracle; this list is the OUTPUT + // in `render_type_arm` is the exhaustiveness oracle; this list is the output // cover). The two args are the cheapest legal filler. Type::Map(Box::new(Type::Unit), Box::new(Type::Unit)), // Cluster C9-B (`.design/basis/10-recursion-tuples.md` REQ-5/REQ-7): a // representative n-tuple type so the REQ-10 inventory renders its fragment // (the `match` in `render_type_arm` is the exhaustiveness oracle; this list - // is the OUTPUT cover). Arity 2 — the minimal legal tuple. + // is the output cover). Arity 2 — the minimal legal tuple. Type::Tuple(vec![Type::Unit, Type::Unit]), ] } @@ -770,7 +770,7 @@ fn item_inventory() -> Vec { // representative non-recursive item (the additive-field ripple). dec: None, body: Some(empty_block()), - // #193 (`.design/forge/goal-repl.md` REQ-4): the open body holes. EMPTY + // #193 (`.design/forge/goal-repl.md` REQ-4): the open body holes. empty // for this representative complete skill-inventory item (the additive // `FnItem.holes` ripple — a skill example is never a holed item). holes: Vec::new(), @@ -875,7 +875,7 @@ fn expr_inventory() -> Vec { // Cluster C9-B (`.design/basis/10-recursion-tuples.md` REQ-5/REQ-8): one // representative each of the tuple construction + the projection node so the // REQ-10 inventory renders their fragments (the `match` in `render_expr_arm` - // is the exhaustiveness oracle; this list is the OUTPUT cover). + // is the exhaustiveness oracle; this list is the output cover). Expr::Tuple(vec![*unit(), *unit()]), Expr::TupleProj { receiver: unit(), @@ -946,7 +946,7 @@ fn effect_inventory() -> Vec { } /// Render a labelled construct sub-section in the TERSE form — a heading + one -/// fragment+description bullet per construct, NO worked example +/// fragment+description bullet per construct, no worked example /// ([`SkillFragment::to_bullet_terse`]). The budget-tightening renderer /// (`thermite-design.md` §2.2) for the leaf inventories (primitive scalars, /// expressions, operators, patterns, effect atoms) whose `fragment` already shows @@ -959,8 +959,8 @@ fn render_inventory_terse(label: &str, fragments: &[SkillFragment]) -> String { s } -/// Render a labelled inventory that keeps a fragment's worked example ONLY when the -/// example is a COMPLETE, standalone item — a `fn`/`spec fn` with a body and no `..` +/// Render a labelled inventory that keeps a fragment's worked example only when the +/// example is a complete, standalone item — a `fn`/`spec fn` with a body and no `..` /// placeholder ([`is_complete_example`]) — and renders every other (snippet) example /// terse. This is the budget-aware middle ground for the `Type` inventory: it keeps /// the copy-pasteable, parse-clean `fn log() -> () req true ens true fx pure { }` @@ -980,7 +980,7 @@ fn render_inventory_complete_examples(label: &str, fragments: &[SkillFragment]) s } -/// Is `example` a COMPLETE, standalone item — a `fn`/`spec fn` with a body and no +/// Is `example` a complete, standalone item — a `fn`/`spec fn` with a body and no /// `..` placeholder? Mirrors the §10 parse-clean pin's `is_complete_item` /// (`thermite-skill/tests/divergence_unit_arm_example.rs`): only such examples are /// standalone-parseable programs worth a full worked bullet; signature snippets and @@ -992,14 +992,14 @@ fn is_complete_example(example: &str) -> bool { /// Section (1) — the surface grammar. The narrative SCAFFOLDING (the /// contract-first framing, the mandatory clause order, the loop `inv`/`dec` -/// rule, the one-call-syntax rule, the "removed from Rust" motivation) is CURATED -/// PROSE (REQ-11, sourced from `thermite-design.md` §4/§4.2/§4.4). The CONSTRUCT +/// rule, the one-call-syntax rule, the "removed from Rust" motivation) is curated +/// prose (REQ-11, sourced from `thermite-design.md` §4/§4.2/§4.4). The CONSTRUCT /// INVENTORY — the type / item / expression / operator / pattern / effect forms — -/// is rendered by EXHAUSTIVE `match`es over the definitional enums (REQ-10), so a +/// is rendered by exhaustive `match`es over the definitional enums (REQ-10), so a /// new language construct compile-forces a skill entry (REQ-8). The exact set is /// `render_*_arm` over [`type_inventory`]/[`item_inventory`]/[`expr_inventory`]/ /// [`binop_inventory`]/[`pattern_inventory`]/[`prim_inventory`]/ -/// [`effect_inventory`] — the OUTPUT covers every current variant, the COMPILER +/// [`effect_inventory`] — the output covers every current variant, the COMPILER /// guarantees no variant can be added without an arm. fn render_grammar() -> String { let mut s = String::from( @@ -1169,7 +1169,7 @@ fn render_result_kind(kind: ResultKind) -> &'static str { /// (`sorted`/`forall_in`/`forall_below`/`forall_from`) take their examples from /// the `binary_search` contract (`thermite-design.md` §4.1); the §4.2-named four /// (`exists_in`/`count_where`/`permutation_of`/`disjoint`) take a hand-written -/// illustrative example. Examples are a SKILL concern, not a registry field, so +/// illustrative example. Examples are a skill concern, not a registry field, so /// they live here, not in `CombinatorSig`. A combinator added to the registry /// without a mapping falls back to a generic example (so the renderer never /// panics — R-CODE-2) and the coverage test still pins its name + the example @@ -1189,11 +1189,11 @@ fn example_for(name: &str) -> &'static str { } /// Section (2) — the SpecTherm combinator library. MACHINE-RENDERED from -/// `thermite_spec::all()` (REQ-2): for EVERY entry and ONLY those entries, the +/// `thermite_spec::all()` (REQ-2): for every entry and only those entries, the /// surface signature (name + arg-kinds + result) + one usage example. Adding a /// combinator to the frozen registry makes it auto-appear here; removing one /// auto-drops it (§10 anti-drift). The verbose Verus(L3)/L1 bodies the registry -/// also carries are NOT rendered — the skill teaches the surface signature, not +/// also carries are not rendered — the skill teaches the surface signature, not /// the lowering bodies. fn render_combinators() -> String { let mut s = String::from( @@ -1240,7 +1240,7 @@ fn render_one_combinator(sig: &CombinatorSig) -> String { } /// The generator-side example table for the recursion schemes (REQ-9 / OQ-2): -/// one tiny usage example per scheme name, keyed by `name`. Examples are a SKILL +/// one tiny usage example per scheme name, keyed by `name`. Examples are a skill /// concern, not a registry field, so they live here, not in `SchemeSig` (the /// `example_for` combinator precedent). A scheme added to the registry without a /// mapping falls back to a generic example (so the renderer never panics — @@ -1297,12 +1297,12 @@ fn render_one_scheme(sig: &SchemeSig) -> String { } /// Section (2b) — the recursion-scheme library. MACHINE-RENDERED from -/// `thermite_spec::schemes::all()` (REQ-9): for EVERY entry and ONLY those +/// `thermite_spec::schemes::all()` (REQ-9): for every entry and only those /// entries, the surface call shape (name + positional args + the trailing step /// closure) + result kind + one example. Adding a scheme to the frozen registry /// makes it auto-appear; removing one auto-drops it (§10 anti-drift — the /// `render_combinators` precedent, REQ-2). The generated lowering symbols -/// (`fold_` etc.) are NOT rendered — the skill teaches the surface call. +/// (`fold_` etc.) are not rendered — the skill teaches the surface call. fn render_schemes() -> String { let mut s = String::from( "\n\ @@ -1324,7 +1324,7 @@ The schemes (call shape, result, then one example each): s } -/// Section (3) — the Forge command set. CURATED from `thermite-design.md` +/// Section (3) — the Forge command set. curated from `thermite-design.md` /// Appendix B (the v0.1 command surface) + §5.1 framing (REQ-3). fn render_forge() -> String { String::from( @@ -1366,7 +1366,7 @@ a hole is `.?N`); `edit`/`fill` take addresses, not string matches. ) } -/// Section (4) — the ladder semantics. CURATED from `thermite-design.md` §6 +/// Section (4) — the ladder semantics. curated from `thermite-design.md` §6 /// (REQ-3), INCLUDING the L0/slag clarification (slag → L1 with `slag: true`; /// L0 is the body-proof aspect). fn render_ladder() -> String { @@ -1404,7 +1404,7 @@ boundary. ) } -/// Section (5) — the slag rules. CURATED from `thermite-design.md` §8 (REQ-3): +/// Section (5) — the slag rules. curated from `thermite-design.md` §8 (REQ-3): /// mandatory non-empty `reason`/`owner`/`review`, contract still enforced at L1, /// `grep slag` as the complete inventory, the polarity inversion. fn render_slag() -> String { @@ -1441,7 +1441,7 @@ non-verification is the exotic add-on that costs more keystrokes and visibility. ) } -/// Section (6) — the Stage-1 forge tier. CURATED from the SHIPPED forge code +/// Section (6) — the Stage-1 forge tier. curated from the SHIPPED forge code /// (`.design/stage1-forge-tier.md`): the seven cert-level verdicts /// ([`forge::verdict::CertVerdict`]) + the agent action per verdict; the per-clause /// relax/in-cage/lemma routing ([`forge::relax::classify_fn`], the `nlsat`/`verus`/ @@ -1449,7 +1449,7 @@ non-verification is the exotic add-on that costs more keystrokes and visibility. /// `witness { inhabit; falsify N }` covenant-before-burn gate, /// [`forge::covenant_engine`]); and the forge-tier verbs + the L3/L4 burn receipt /// ([`forge::burn::BurnReceipt`]). Curated prose (REQ-11), guarded by the budget + -/// the v2 coverage test (`forge_tier_markers_present`). The seven verdict NAMES are +/// the v2 coverage test (`forge_tier_markers_present`). The seven verdict names are /// the closed `CertVerdict` set — a new verdict there is caught by that test. fn render_forge_tier() -> String { String::from( @@ -1556,7 +1556,7 @@ mod tests { #[test] fn combinator_coverage() { - // AC-2: every entry in the frozen registry appears by name AND has an + // AC-2: every entry in the frozen registry appears by name and has an // example marker. Expected source is the registry itself (R-CHAR-3 — the // anti-drift contract is "the skill mirrors all()"). let skill = generate(); @@ -1578,7 +1578,7 @@ mod tests { #[test] fn scheme_coverage() { - // AC-9: every entry in the frozen scheme registry appears by name AND + // AC-9: every entry in the frozen scheme registry appears by name and // has a call-shape marker (the registry IS the oracle — R-CHAR-3). let skill = generate(); for sig in thermite_spec::schemes::all() { @@ -1592,16 +1592,16 @@ mod tests { #[test] fn renderers_are_exhaustive_no_wildcard() { - // AC-10(i) — the STRUCTURAL no-staleness invariant. The renderer + // AC-10(i) — the structural no-staleness invariant. The renderer // functions `render_{type,expr,item,pattern,effect,binop,prim}_arm` are - // EXHAUSTIVE `match`es with NO `_` wildcard arm over their definitional + // exhaustive `match`es with no `_` wildcard arm over their definitional // enums. Rust's exhaustiveness check (E0004) makes adding a new variant a - // HARD compile error in THIS crate until the matching arm is added — so + // hard compile error in this crate until the matching arm is added — so // the skill cannot silently fall behind the language (REQ-8). // // This is enforced by the compiler, not by a runtime assertion: if a // future variant were added without a renderer arm, this whole crate - // (and thus this test) would FAIL TO BUILD. A green build is the proof. + // (and thus this test) would fail TO build. A green build is the proof. // We exercise the renderers over the full per-variant inventories so the // arms are reached, and assert each inventory is non-empty (a sanity // floor — the inventories must cover at least the shipped variants). diff --git a/thermite-skill/src/main.rs b/thermite-skill/src/main.rs index 483f7f1aa..1bf82199c 100644 --- a/thermite-skill/src/main.rs +++ b/thermite-skill/src/main.rs @@ -26,7 +26,7 @@ fn main() -> ExitCode { /// The result of a single bin invocation. A plain two-state enum (not `ExitCode`, /// which is not comparable) so the bin's logic is unit-testable (R-CHAR-3) while -/// `main` maps it to a real process exit code. +/// `main` maps it to a process exit code. #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum Outcome { Success, diff --git a/thermite-skill/tests/divergence_unit_arm_example.rs b/thermite-skill/tests/divergence_unit_arm_example.rs index 6c1c14fcc..28dc80a8c 100644 --- a/thermite-skill/tests/divergence_unit_arm_example.rs +++ b/thermite-skill/tests/divergence_unit_arm_example.rs @@ -2,22 +2,22 @@ //! //! REQ-10 / REQ-8 (`.design/skill/skill-generator.md`) make the surface //! inventory's per-construct text — each `render_*_arm`'s `{ fragment, -//! description, example }` — the agent-facing description of the REAL language +//! description, example }` — the agent-facing description of the real language //! surface. `thermite-design.md` §10 ("the skill IS the spec, no version skew") //! requires that text to be ACCURATE: an example the skill teaches must be a //! program the toolchain accepts. The compile-force mechanism (REQ-8) guarantees -//! every variant HAS an arm, but it does NOT guarantee the arm's hand-written +//! every variant HAS an arm, but it does not guarantee the arm's hand-written //! example is valid surface — and one is not. //! -//! DIVERGENCE: `render_type_arm`'s `Type::Unit` arm in +//! divergence: `render_type_arm`'s `Type::Unit` arm in //! `thermite-skill/src/generate.rs` emits the example //! `fn log() -> () ens true fx pure { }` -//! which OMITS the mandatory `req` clause. The skill's OWN curated grammar prose +//! which OMITS the mandatory `req` clause. The skill's own curated grammar prose //! (`render_grammar`) states: "mandatory clauses in this exact order … absence of //! any is a parse error" and lists `req`/`ens`/`fx`. The parser //! (`thermite_syntax::parser::parse`) rejects this example with //! `clause `req` is out of order in `log`` -//! (verified: the same fn WITH `req true` parses clean). So the skill teaches an +//! (verified: the same fn with `req true` parses clean). So the skill teaches an //! agent an example program that the toolchain itself refuses to parse — a //! §10 version-skew lie of exactly the kind REQ-8/REQ-10 exist to eliminate, just //! relocated from a curated grammar string into a per-variant arm example. @@ -27,14 +27,14 @@ //! version skew); the corpus shape (`conformance/string_demo.th` carries //! `req true` even for a trivial precondition). The expected value is "the //! skill's examples parse clean" — derived from the parser + the skill's own -//! mandatory-clause prose, NOT copied from generate.rs (R-CHAR-3). +//! mandatory-clause prose, not copied from generate.rs (R-CHAR-3). //! //! Tracking: crosslink #85. //! //! FIXED (crosslink #85): the `Type::Unit` arm now renders //! `fn log() -> () req true ens true fx pure { }` (the mandatory `req` is //! present, clauses in `req`->`ens`->`fx` order) and `THERMITE.skill.md` is -//! regenerated. This test is now the PERMANENT regression guard: EVERY complete +//! regenerated. This test is now the PERMANENT regression guard: every complete //! `fn`/`spec fn` example the skill renders must parse clean, so a rendered //! un-parseable example can never ship again (§10: the skill IS the spec). @@ -51,21 +51,21 @@ fn rendered_examples(skill: &str) -> Vec<&str> { .collect() } -/// Is `example` a COMPLETE top-level item (a `fn`/`spec fn` with a body), as +/// Is `example` a complete top-level item (a `fn`/`spec fn` with a body), as /// opposed to a signature snippet or a `..`-placeholder grammar fragment? Only /// complete items are meant to be standalone-parseable programs; fragments /// (`fn sum(..) -> u64 req .. ens .. fx pure { .. }`, bare signatures like -/// `fn f(x: &mut u64)`) deliberately are not. +/// `fn f(x: &mut u64)`) are not. fn is_complete_item(example: &str) -> bool { let e = example.trim(); (e.starts_with("fn ") || e.starts_with("spec fn ")) && !e.contains("..") && e.ends_with('}') } -/// Every COMPLETE `fn`/`spec fn` example the skill renders must be a program the +/// Every complete `fn`/`spec fn` example the skill renders must be a program the /// parser accepts. This is the regression guard for crosslink #85: the /// `Type::Unit` arm previously rendered `fn log() -> () ens true fx pure { }`, /// which OMITS the mandatory `req` and so the parser rejected with -/// `clause `req` is out of order`. The corrected arm renders the example WITH +/// `clause `req` is out of order`. The corrected arm renders the example with /// `req true`. Authority: the skill's own mandatory-clause prose /// (`req`->`ens`->`fx`, "absence of any is a parse error") + `thermite_syntax:: /// parser`; the expected value ("the skill's examples parse clean") is derived @@ -75,7 +75,7 @@ fn rendered_fn_examples_parse_clean() { let skill = generate(); // The corrected `Type::Unit` arm example must be present (so this test still - // tracks that arm specifically) AND must be a complete item we then parse. + // tracks that arm specifically) and must be a complete item we then parse. let unit_example = "fn log() -> () req true ens true fx pure { }"; assert!( skill.contains(unit_example), diff --git a/thermite-skill/tests/skill.rs b/thermite-skill/tests/skill.rs index f39b97a24..5de94b49f 100644 --- a/thermite-skill/tests/skill.rs +++ b/thermite-skill/tests/skill.rs @@ -22,7 +22,7 @@ fn skill_is_under_budget() { assert!(count > 0, "generated skill counted zero tokens"); } -/// AC-2 — every entry in the frozen registry appears by name AND carries a usage +/// AC-2 — every entry in the frozen registry appears by name and carries a usage /// example. This is REQ-2's anti-drift property: a combinator the registry adds /// or drops changes this coverage automatically (the expected set IS `all()`). #[test] @@ -47,7 +47,7 @@ fn every_combinator_appears_with_an_example() { } /// AC-9 — every entry in the frozen recursion-scheme registry appears by name -/// AND carries a usage example (the REQ-9 registry-driven anti-drift property — +/// and carries a usage example (the REQ-9 registry-driven anti-drift property — /// the AC-2 analogue for schemes). The expected set IS `schemes::all()`, so a /// scheme added or dropped changes this coverage automatically (R-CHAR-3). #[test] @@ -78,9 +78,9 @@ fn every_scheme_appears_with_an_example() { } /// AC-10(ii) — coverage: every current Stage-1–8 surface construct appears in the -/// generated skill (the OUTPUT half of the no-staleness guarantee). Expected +/// generated skill (the output half of the no-staleness guarantee). Expected /// substrings are derived from the construct's name / §4.4 — never copied back -/// from the generator (R-CHAR-3). The STRUCTURAL half (no `_` arm, so a new +/// from the generator (R-CHAR-3). The structural half (no `_` arm, so a new /// variant fails to compile — AC-10(i)) is enforced by `rustc`'s exhaustiveness /// check on `render_*_arm`: this very test crate would not compile if a renderer /// arm were missing, so a green build IS the structural proof. See the module @@ -138,11 +138,11 @@ fn surface_construct_coverage() { /// The Stage-1 forge-tier section (skill v2; umbrella `.design/thermite2-program.md` /// REQ-8 / AC-13) teaches the agent the SHIPPED forge tier. Its four mandated /// content areas must each be present, with expected strings derived from the -/// shipped forge code — NOT copied from `generate.rs` (R-CHAR-3): +/// shipped forge code — not copied from `generate.rs` (R-CHAR-3): /// -/// 1. the SEVEN cert-level verdicts — the closed `forge::verdict::CertVerdict::kind` +/// 1. the seven cert-level verdicts — the closed `forge::verdict::CertVerdict::kind` /// set is the oracle (a new verdict there must appear here too); -/// 2. the per-clause ROUTING — the `nlsat`/`verus`/`lean` engine names + the L4/L3 +/// 2. the per-clause routing — the `nlsat`/`verus`/`lean` engine names + the L4/L3 /// attribution (`forge::engine::EngineName`, `forge::manifest::Level::L4`); /// 3. COVENANT authoring — the `witness { inhabit; falsify N }` covenant-before-burn /// surface (`forge::covenant_engine`); diff --git a/thermite-spec/src/classifier.rs b/thermite-spec/src/classifier.rs index 1540fc5fe..5bd359a40 100644 --- a/thermite-spec/src/classifier.rs +++ b/thermite-spec/src/classifier.rs @@ -1,5 +1,5 @@ //! The Rust admission classifier — the **ops half** of the stage-2 stratified cage -//! (`.design/stage2-stratified-cage.md` REQ-4 / AC-4). This is a deliberately-simple +//! (`.design/stage2-stratified-cage.md` REQ-4 / AC-4). This is a simple //! re-implementation, in Rust, of the Lean kernel classifier shipped in REQ-3 (#325): //! `Thermite.Strat.Cls.admitted` (`lean/Thermite/Strat/Fragment.lean`), //! @@ -8,11 +8,11 @@ //! ``` //! //! over the **sort-typed `Cls` surface syntax** (`Sort₂` + the array-property term -//! vocabulary `Read`/`Len`/`Cast`/`IdxOp`/`Mul`/spec-fn + sorted binders), NOT REQ-1's +//! vocabulary `Read`/`Len`/`Cast`/`IdxOp`/`Mul`/spec-fn + sorted binders), not REQ-1's //! minimal semantic-spine `Frm`. The two languages are distinct by design (the #68 //! axiom-probe collision; see `Strat/Nnf.lean`'s header): this module mirrors the //! `Thermite.Strat.Cls.Frm` classifier surface so the **differential battery** -//! (`thermite-tv`'s generator → both this classifier AND `lake env lean --run` on the +//! (`thermite-tv`'s generator → both this classifier and `lake env lean --run` on the //! Lean `admitted`) can hold the two implementations to byte-equal verdicts on every //! generated formula. Any disagreement is a hard CI failure (audit check [8]); the //! `unknown`-on-admitted tripwire logs and escalates as classifier-suspect. @@ -22,10 +22,10 @@ //! Every `fn` below is a line-for-line transliteration of the Lean definition it names //! in its doc comment — `fin_sort`/`fin_carrier`, `same_width`/`has_bound_var`/ //! `idx_ok_tm`/`idx_grammar_at`, `nnf`/`nnf_neg`, `edges_tm`/`edges_atom`/`edges_frm`/ -//! `sort_graph`, and `admitted`. The ONE intentional divergence is the acyclicity +//! `sort_graph`, and `admitted`. The one intentional divergence is the acyclicity //! decision: the Lean kernel uses the exponential Roy–Warshall `reach` recursion //! (`Strat/Graph.lean`, fine for `decide` on the §3.2 micro-examples), whereas the Rust -//! side computes the SAME boolean (`acyclic G ⟺ no node reaches itself`) by a +//! side computes the same boolean (`acyclic G ⟺ no node reaches itself`) by a //! polynomial transitive-closure ([`Graph::acyclic`]); the two agree by //! `acyclic_iff_no_cycle`, and the differential battery is what witnesses the agreement //! empirically over the generated clause space. @@ -34,7 +34,7 @@ //! //! A rejection names its reason from the frozen [`RejectReason`] vocabulary //! (`infinite-carrier`/`seq-quantifier` for (R1), `index-grammar` for (R2), the named -//! `…-cycle` for (R3)). The classifier is TOTAL — `classify` always returns a definite +//! `…-cycle` for (R3)). The classifier is total — `classify` always returns a definite //! [`Verdict::Admitted`] or [`Verdict::Rejected`]; the [`Verdict::Unknown`] arm exists //! only for the differential battery's tripwire (a formula the classifier could not //! vouch for while Lean admitted it — escalate, never silently retry). @@ -376,7 +376,7 @@ fn nnf_neg(phi: &Frm) -> Frm { // =========================================================================== /// A directed graph over sorts (`Strat/Graph.lean` `structure Graph`) — an explicit -/// node list and edge list (kept with duplicates, exactly as the Lean `nodesOf` builds +/// node list and edge list (kept with duplicates, as the Lean `nodesOf` builds /// them, so the node set matches; reachability is insensitive to the duplication). struct Graph { nodes: Vec, @@ -562,7 +562,7 @@ fn sort_graph(phi: &Frm) -> Graph { // The classifier verdict + the frozen rejection vocabulary // =========================================================================== -/// Why the classifier rejected a formula — the FROZEN vocabulary (REQ-4 / AC-4). One +/// Why the classifier rejected a formula — the frozen vocabulary (REQ-4 / AC-4). One /// member per admission gate; every rejection names exactly one. The headline three the /// design names (`infinite-carrier`/`seq-quantifier`, the named cycle) are (R1) and /// (R3); `index-grammar` is the (R2) member (a bound index var under `mul` or a @@ -636,7 +636,7 @@ impl Verdict { } /// Classify a formula — the Rust mirror of `Thermite.Strat.Cls.admitted` plus the -/// rejection reason (REQ-4). Checks the three gates in the SAME order the Lean +/// rejection reason (REQ-4). Checks the three gates in the same order the Lean /// `admitted` conjunction evaluates — (R1) `finCarrier`, then (R2) `idxGrammar`, then /// (R3) `acyclic (sortGraph (nnf φ))` — so the boolean `classify(φ).is_admitted()` /// equals `admitted φ` exactly (the differential invariant), and a rejection names the @@ -838,12 +838,12 @@ fn write_frm(phi: &Frm, out: &mut String) { /// Grammar (tokens are `(`, `)`, and maximal non-paren non-space runs): /// /// ```text -/// sort := (m WIDTH) | (s SORT) | (o NAT) -/// tm := (v SORT INT) | (l SORT) | (rd SORT TM TM) | (ln TM) -/// | (ct SORT TM) | (ix TM INT) | (ml TM TM) | (a1 SORT SORT NAT TM) +/// sort := (m WIDTH) | (s sort) | (o NAT) +/// tm := (v sort INT) | (l sort) | (rd sort TM TM) | (ln TM) +/// | (ct sort TM) | (ix TM INT) | (ml TM TM) | (a1 sort sort NAT TM) /// atom := (r REL TM TM) | (qf) /// frm := (at ATOM) | (ng FRM) | (cj FRM FRM) | (dj FRM FRM) -/// | (im FRM FRM) | (al SORT FRM) | (ex SORT FRM) +/// | (im FRM FRM) | (al sort FRM) | (ex sort FRM) /// ``` pub fn parse_frm(wire: &str) -> Result { let toks = tokenize(wire); @@ -1037,7 +1037,7 @@ mod tests { Sort2::usize_s() } - // The four §3.2 worked micro-examples — the SAME concrete formulas the Lean + // The four §3.2 worked micro-examples — the same concrete formulas the Lean // `Strat/Fragment.lean` `decide`-checks (`ex_selfLoop`/`ex_castCycle`/`ex_kvCycle`/ // `ex_sortedness`), transliterated to the Rust `Frm`. The Rust verdict must match the // Lean expected admit/reject outcome — the kernel-anchored end of the differential. @@ -1174,7 +1174,7 @@ mod tests { #[test] fn mul_over_bound_var_is_index_grammar() { // `∀ i : usize. a[(i * i)] = a[(i * i)]` — a bound index var under `mul`, the - // (R2) rejection with NO graph-cycle witness. + // (R2) rejection with no graph-cycle witness. let u32s = Sort2::Mach(Mach::U32); let a_seq = || Tm::Lit(Sort2::Seq(Box::new(u32s.clone()))); let prod = Tm::Mul( diff --git a/thermite-spec/src/restratify.rs b/thermite-spec/src/restratify.rs index 281a4f3ad..bc13cd66a 100644 --- a/thermite-spec/src/restratify.rs +++ b/thermite-spec/src/restratify.rs @@ -1,9 +1,9 @@ -//! The restratification rewrite + its R-SIDE-1 certification discipline +//! The restratification rewrite + its R-side-1 certification discipline //! (`.design/stage2-stratified-cage.md` REQ-7 / AC-7), the Rust ops-half mirroring //! `lean/Thermite/Strat/Restratify.lean`'s T4-R metatheory. //! //! `restrat` breaks an admission cycle by EXCISING the cycle-closing conjunct `B` and -//! replacing it with a fresh OPAQUE abstraction `p` (an [`Atom::QFree`] leaf — opaque to +//! replacing it with a fresh opaque abstraction `p` (an [`Atom::QFree`] leaf — opaque to //! the classifier, contributing no sorts and no graph edges). On the motivating kv //! alternation cycle //! @@ -12,24 +12,24 @@ //! └────────── A ──────────┘ └────────── B ──────────┘ //! ``` //! -//! whose sort graph has BOTH `Key → Value` (from A) and `Value → Key` (from B) — a cycle, +//! whose sort graph has both `Key → Value` (from A) and `Value → Key` (from B) — a cycle, //! so `classify(φ)` is [`Verdict::Rejected`] — the rewrite yields //! //! ```text -//! φ' = restrat(φ) = A ∧ p (only Key → Value ⇒ acyclic ⇒ ADMITTED) -//! Side(φ', φ) = p ⇒ B (only Value → Key ⇒ acyclic ⇒ ADMITTED) +//! φ' = restrat(φ) = A ∧ p (only Key → Value ⇒ acyclic ⇒ admitted) +//! Side(φ', φ) = p ⇒ B (only Value → Key ⇒ acyclic ⇒ admitted) //! ``` //! -//! **R-SIDE-1 (the load-bearing discipline).** A certificate of φ' ALONE never counts for +//! **R-side-1 (the required discipline).** A certificate of φ' alone never counts for //! φ: `p` is a fresh, unconstrained abstraction, so `A ∧ p` is satisfied trivially by -//! `p := true` WITHOUT B holding. [`certify`] therefore WITHHOLDS the φ-certificate unless -//! the `Side` obligation (`p ⇒ B`, itself in-cage) is SEPARATELY discharged. This mirrors -//! the Lean `restrat_conservative`, which consumes BOTH φ' and `Side`; dropping `Side` +//! `p := true` without B holding. [`certify`] therefore WITHHOLDS the φ-certificate unless +//! the `Side` obligation (`p ⇒ B`, itself in-cage) is separately discharged. This mirrors +//! the Lean `restrat_conservative`, which consumes both φ' and `Side`; dropping `Side` //! is exactly the mis-certification `Thermite.PinRestratDropSide` exhibits. use crate::classifier::{classify, Atom, Frm, Sort2, Tm, Verdict}; -/// The fresh OPAQUE boolean abstraction leaf standing in for an excised sub-formula +/// The fresh opaque boolean abstraction leaf standing in for an excised sub-formula /// (`Strat/Restratify.lean` `absLeaf`). A `qfree` atom is opaque to the classifier — it /// contributes no sorts and no graph edges — so substituting it for a cycle-closing /// conjunct deletes that conjunct's edges from the sort graph. @@ -51,7 +51,7 @@ pub struct RestratResult { pub side: Frm, } -/// The restratify rewrite (metatheory §6). On a conjunction `A ∧ B` whose RIGHT conjunct +/// The restratify rewrite (metatheory §6). On a conjunction `A ∧ B` whose right conjunct /// `B` closes the alternation cycle, excise `B`, replace it with the fresh abstraction /// `p`, and emit `Side = p ⇒ B` (`Strat/Restratify.lean` `restrat`/`Side`). Returns /// [`None`] when `φ` is not a conjunction — there is no cycle-closing conjunct to excise @@ -69,7 +69,7 @@ pub fn restratify(phi: &Frm) -> Option { } } -/// Why a restratify-based φ-certificate was WITHHELD (R-SIDE-1). +/// Why a restratify-based φ-certificate was WITHHELD (R-side-1). #[derive(Debug, Clone, PartialEq, Eq)] pub enum WithheldReason { /// The rewrite did not apply — φ is not a conjunction, so there is no cycle-closing @@ -80,35 +80,35 @@ pub enum WithheldReason { /// `Side` is not itself in-cage, so it cannot be discharged in-cage — the split is /// not usable. SideNotInCage, - /// φ' and `Side` are both in-cage, but `Side` was NOT discharged — the φ-certificate - /// is withheld (R-SIDE-1: a φ'-only certificate never counts for φ). + /// φ' and `Side` are both in-cage, but `Side` was not discharged — the φ-certificate + /// is withheld (R-side-1: a φ'-only certificate never counts for φ). SideUndischarged, } /// The certification verdict for restratifying φ. #[derive(Debug, Clone, PartialEq, Eq)] pub enum Certification { - /// φ is certified: φ' is admitted AND `Side` was discharged in-cage. Carries the + /// φ is certified: φ' is admitted and `Side` was discharged in-cage. Carries the /// rewrite products for rendering / auditing. Certified(RestratResult), - /// φ is NOT certified — see the reason. Carries the rewrite products when the rewrite + /// φ is not certified — see the reason. Carries the rewrite products when the rewrite /// applied (for rendering), else `None`. Withheld(WithheldReason, Option), } impl Certification { - /// `true` iff φ was certified (φ' admitted AND `Side` discharged in-cage). + /// `true` iff φ was certified (φ' admitted and `Side` discharged in-cage). #[must_use] pub fn is_certified(&self) -> bool { matches!(self, Certification::Certified(_)) } } -/// Certify φ through restratification, honouring R-SIDE-1. `side_discharged` models -/// whether the caller SEPARATELY discharged the `Side` obligation in-cage; when `false`, +/// Certify φ through restratification, honouring R-side-1. `side_discharged` models +/// whether the caller separately discharged the `Side` obligation in-cage; when `false`, /// the φ-certificate is WITHHELD even though φ' is admitted — exactly the discipline /// `Thermite.Strat.Cls.restrat_conservative` enforces (it consumes the `Side` hypothesis) -/// and `Thermite.PinRestratDropSide` shows is load-bearing. +/// and `Thermite.PinRestratDropSide` shows is required. #[must_use] pub fn certify(phi: &Frm, side_discharged: bool) -> Certification { let Some(result) = restratify(phi) else { @@ -121,7 +121,7 @@ pub fn certify(phi: &Frm, side_discharged: bool) -> Certification { return Certification::Withheld(WithheldReason::SideNotInCage, Some(result)); } if !side_discharged { - // R-SIDE-1: φ' is admitted and Side is in-cage, but Side was not discharged. + // R-side-1: φ' is admitted and Side is in-cage, but Side was not discharged. return Certification::Withheld(WithheldReason::SideUndischarged, Some(result)); } Certification::Certified(result) @@ -161,7 +161,7 @@ mod tests { use super::*; use crate::classifier::RejectReason; - /// The original kv formula is REJECTED with a sort-graph cycle (mirrors + /// The original kv formula is rejected with a sort-graph cycle (mirrors /// `ex_kvCycle_rejected`). #[test] fn kv_original_rejected() { @@ -171,7 +171,7 @@ mod tests { )); } - /// The rewrite applies and BOTH products are admitted in-cage (mirrors the Lean + /// The rewrite applies and both products are admitted in-cage (mirrors the Lean /// `restrat_admits` + `side_admitted`). #[test] fn rewrite_and_side_both_admitted() { @@ -196,7 +196,7 @@ mod tests { } /// AC-7 — the withheld-certification discipline: certification is WITHHELD when `Side` - /// is undischarged (R-SIDE-1). This is the Rust mirror of `PinRestratDropSide`. + /// is undischarged (R-side-1). This is the Rust mirror of `PinRestratDropSide`. #[test] fn withheld_when_side_undischarged() { let cert = certify(&kv_example(), false); @@ -211,7 +211,7 @@ mod tests { } /// A non-conjunction has no cycle-closing conjunct to excise — the rewrite does not - /// apply, and certification is honestly withheld. + /// apply, and certification is withheld. #[test] fn non_conjunction_not_restratifiable() { let phi = Frm::Atom(Atom::QFree); diff --git a/thermite-spec/src/validator.rs b/thermite-spec/src/validator.rs index 8cce7042d..048d69c3a 100644 --- a/thermite-spec/src/validator.rs +++ b/thermite-spec/src/validator.rs @@ -201,7 +201,7 @@ const MAX_RECURSION_DEPTH: usize = 64; /// method whose round-trip a contract names (`ens parse_le(result) == n`, the /// grounded gold standard). Both are constructing ops (`fx alloc`); `from_byte` is /// an associated path-call (`String::from_byte(b)`, an `Expr::Call`), so it needs -/// no `BUILTIN_METHODS` entry. The no-OOB / round-trip teeth are proved at L3 (a +/// no `BUILTIN_METHODS` entry. The L3 proof covers the no-OOB and round-trip properties (a /// wrong digit fails, R-DEFER-9); admitting the method here only opens the cage to /// name it. /// Cluster C6 collections (`.design/basis/04-collections.md` REQ-8/REQ-12, issue @@ -254,7 +254,7 @@ const BUILTIN_METHODS: &[&str] = &[ "trim", ]; -/// The GENERATED `spec fn` names the lowerer materializes for the C4 `u64`→`String` +/// The generated `spec fn` names the lowerer materializes for the C4 `u64`→`String` /// round-trip (`.design/basis/07-strings.md` REQ-8, issue #94): `parse_be` (the /// MSB-first / human-readable decimal value of a byte sequence — the DISPLAY-form /// round-trip the surface contract names, blocker #96), `parse_le` (the LSB-first @@ -350,7 +350,7 @@ pub enum SpecError { /// A construct the contract sublanguage forbids that nonetheless parsed — /// e.g. a `MethodCall` whose callee is not a grammar built-in, or a non-call /// callee shape (REQ-4 (iv)). Distinct from `UnknownCombinator` (a free - /// `Expr::Call`) so the diagnostic names the construct precisely. + /// `Expr::Call`) so the diagnostic identifies the construct. ForbiddenCall { detail: String, span: Span }, /// A registered combinator call appearing inside another combinator's /// predicate-closure body — an anonymous nested quantifier (REQ-6). The @@ -438,7 +438,7 @@ pub enum SpecError { span: Span, }, /// An `enum` variant declared with a lowercase-initial name - /// (`.design/basis/01-adts.md` REQ-2: "Variant names MUST be UpperCamelCase + /// (`.design/basis/01-adts.md` REQ-2: "Variant names must be UpperCamelCase /// (uppercase-initial); the validator rejects a lowercase-initial variant /// declaration"). This is a soundness rule: the parser /// disambiguates a single-segment arm pattern by first-letter case @@ -722,10 +722,10 @@ impl Validator { }) .collect(); // Cluster C4 strings (`.design/basis/07-strings.md` REQ-8, issue #94): seed - // the GENERATED round-trip spec fns (`parse_le`/`pow10`) so a contract + // the generated round-trip spec fns (`parse_le`/`pow10`) so a contract // `ens parse_le(result) == n` validates inside the §4.2 cage as a named // `spec fn` call (the lowerer materializes their bodies). These are reserved - // names the lowerer owns — accepted exactly as a user-declared spec fn. + // names the lowerer owns — accepted as a user-declared spec fn. for name in GENERATED_SPEC_FNS { spec_fns.insert((*name).to_string()); } @@ -741,7 +741,7 @@ impl Validator { // REQ-8 (`.design/basis/06-provenance-and-sinks.md`): the `#[sealed]` // clean/capability struct names — the abstraction barrier the // `Expr::StructLit` walk keys off to REJECT a direct mint. Collected in - // the SAME pre-pass as `struct_fields` so a forward reference (`fn + // the same pre-pass as `struct_fields` so a forward reference (`fn // f() { Sql { … } }` before `#[sealed] struct Sql`) is seen. let mut sealed_structs: HashSet = HashSet::new(); // `.design/basis/01-adts.md` REQ-2: every `enum` variant name must be @@ -781,7 +781,7 @@ impl Validator { // ACCESS, not enum-declaration uniqueness (a separate // concern not in this REQ). A struct-shaped variant's // fields join the struct field set (REQ-6: `Field` - // access is checked against struct AND struct-variant + // access is checked against struct and struct-variant // fields). variant_to_enum.insert(variant.name.clone(), e.name.clone()); if let VariantShape::Struct(fields) = &variant.shape { @@ -950,7 +950,7 @@ impl Validator { // its contract/proof positions (`prop fn` body, `lemma`/`proof` // clauses + proof blocks, `witness` directives) are consumed by the // forge increments (2b covenant, 2c battery, 2e proof view, 3 - // library), NOT the v1 spec cage. No v1 contract walk applies here; + // library), not the v1 spec cage. No v1 contract walk applies here; // the surface is parse/address/round-trip tested in thermite-syntax. Item::Forge(_) => {} } @@ -1012,7 +1012,7 @@ impl Validator { self.walk_clause(inv); } self.walk_clause(&loop_node.dec); - // The loop BODY is still executable surface code: scan it + // The loop body is still executable surface code: scan it // structurally for further nested loops, do not cage it. self.scan_block_for_loops(&loop_node.body, loop_node.span); } @@ -1210,7 +1210,7 @@ impl Validator { match expr { // (c) grammar built-ins: literals and paths are leaves. A string // literal (`.design/basis/07-strings.md` REQ-1) is a value-carrying - // leaf admitted in a contract position exactly as an int/bool literal + // leaf admitted in a contract position as an int/bool literal // — e.g. the editor case `s == "needle"`; no sub-expression to walk. Expr::IntLit { .. } | Expr::BoolLit(_) | Expr::Path(_) | Expr::StrLit(_) => {} @@ -1348,7 +1348,7 @@ impl Validator { self.check_variant_ref(variant, span); self.walk_expr(scrutinee, span); } - // A `Box` deref `*EXPR` (REQ-3): accepted STRUCTURALLY here (the + // A `Box` deref `*EXPR` (REQ-3): accepted structurally here (the // recursive deref `sum_list(*t)` of `list_sum.th`); its `Box` SEMANTICS // are Stage 1c. Recurse the inner expression (depth-guarded). Expr::Deref(inner) => self.walk_expr(inner, span), @@ -1356,7 +1356,7 @@ impl Validator { // tuple construction `(a, b, …)` is a flat structural built-in (its // elements are recursed, depth-guarded); a projection `e.0`/`e.1` is a // flat built-in like `Field`, admitted inside the §4.2 cage — an `ens - // result.0 == b` reads a tuple element exactly as `Field` reads a + // result.0 == b` reads a tuple element as `Field` reads a // struct field. A tuple is well-formed iff its elements are. Expr::Tuple(elems) => { for e in elems { @@ -1404,7 +1404,7 @@ impl Validator { /// built-in (`Option`'s `Some`/`None` in `binary_search.th` — `Option` is no /// declared `Item::Enum`) and is left unchanged (the AC-6 no-regression /// invariant). Once identified as a declared-enum match: - /// - an arm naming a variant of a DIFFERENT/undeclared enum is `UnknownVariant`; + /// - an arm naming a variant of a different/undeclared enum is `UnknownVariant`; /// - a variant matched twice, or an arm after a catch-all, is `UnreachableArm`; /// - if no catch-all closes the match, every uncovered declared variant is /// collected into `NonExhaustiveMatch { missing }` (declaration order). @@ -1707,7 +1707,7 @@ impl Validator { // Arity is wrong; still recurse the supplied args (depth guard, // nested-content surfacing) but skip the per-position step check (the // step slot is not where we expect it). The step body, if any, is - // walked WITHOUT scheme-step mode — a malformed call is not a valid + // walked without scheme-step mode — a malformed call is not a valid // step context. for arg in args { self.walk_expr(arg, span); diff --git a/thermite-spec/tests/divergence_adt_validate.rs b/thermite-spec/tests/divergence_adt_validate.rs index c5421b1f3..368b5161e 100644 --- a/thermite-spec/tests/divergence_adt_validate.rs +++ b/thermite-spec/tests/divergence_adt_validate.rs @@ -65,7 +65,7 @@ fn parse_clean(src: &str) -> thermite_syntax::Program { /// the declaration pre-pass, before any match is considered. So the program no /// longer validates clean — the false accept is gone, with an earlier, more /// precise error. Authority: `.design/basis/01-adts.md` REQ-2 ("variant names -/// MUST be UpperCamelCase … the validator rejects a lowercase-initial variant +/// must be UpperCamelCase … the validator rejects a lowercase-initial variant /// declaration with `SpecError::InvalidVariantCasing { name, span }`"). The /// core pin holds: the program must not validate clean. /// diff --git a/thermite-spec/tests/divergence_c10_guarded_catchall.rs b/thermite-spec/tests/divergence_c10_guarded_catchall.rs index 300b0b473..d11553eac 100644 --- a/thermite-spec/tests/divergence_c10_guarded_catchall.rs +++ b/thermite-spec/tests/divergence_c10_guarded_catchall.rs @@ -6,7 +6,7 @@ //! > false) — GROUNDED: Verus rejects a guarded-only `Some` arm as //! > non-exhaustive. //! -//! and AC-3b: "a guard does NOT complete a match ... The validator MUST reject +//! and AC-3b: "a guard does not complete a match ... The validator must reject //! it (matching Verus's `error[E0004]: non-exhaustive patterns`)." //! //! Divergence: `validator::check_match_exhaustiveness` identifies the matched @@ -26,7 +26,7 @@ //! REQ-3 rule (the toolchain should pre-empt with a structured //! `NonExhaustiveMatch`, not defer to an opaque verus L0). //! -//! Authority: `.design/basis/11-ergonomics.md` REQ-3 / AC-3b ("a guard does NOT +//! Authority: `.design/basis/11-ergonomics.md` REQ-3 / AC-3b ("a guard does not //! complete a match"). The expected outcome (`NonExhaustiveMatch`) is //! hand-derived from the design rule (R-CHAR-3 — not copied from the toolchain; //! the toolchain currently returns `Ok(())`, the opposite). diff --git a/thermite-spec/tests/divergence_final2.rs b/thermite-spec/tests/divergence_final2.rs index 9dfc7c85d..cd09f18e0 100644 --- a/thermite-spec/tests/divergence_final2.rs +++ b/thermite-spec/tests/divergence_final2.rs @@ -107,7 +107,7 @@ fn f(xs: &[u32]) -> usize ); } -/// Inner loop nested inside an OUTER loop's body; the INNER loop's `inv` is +/// Inner loop nested inside an outer loop's body; the INNER loop's `inv` is /// bogus while the outer's is well-formed. The traversal must descend a loop /// body (`scan_block_for_loops(&loop_node.body)`) to reach the inner loop. #[test] diff --git a/thermite-spec/tests/divergence_nesting.rs b/thermite-spec/tests/divergence_nesting.rs index 3525ea174..1ee80e3df 100644 --- a/thermite-spec/tests/divergence_nesting.rs +++ b/thermite-spec/tests/divergence_nesting.rs @@ -103,7 +103,7 @@ fn nested_combinator_under_three_arg_outer_rejects() { ); } -/// A nested combinator inside a `spec fn` BODY's combinator closure. A spec-fn +/// A nested combinator inside a `spec fn` body's combinator closure. A spec-fn /// body is itself a fully-caged contract position (REQ-3); REQ-6's flag must be /// set when descending its combinator closures too. Expected: Err with /// `NestedCombinator`. diff --git a/thermite-syntax/src/address.rs b/thermite-syntax/src/address.rs index c3b537b1a..78136e3b8 100644 --- a/thermite-syntax/src/address.rs +++ b/thermite-syntax/src/address.rs @@ -68,7 +68,7 @@ pub enum AddrKind { /// `witness#N`, or a proof obligation `f.proof.ens#k`. The consumers (proof view /// 2e, lemma library 3) resolve these; here they are addressable + round-trip. Forge, - /// An open PROOF hole `?pN` (`.design/stage1-forge-tier.md` REQ-3) inside a + /// An open proof hole `?pN` (`.design/stage1-forge-tier.md` REQ-3) inside a /// proof block. Addressed `.proof.?pN` / `f.proof.ens#k.?pN`. Distinct /// from a body [`AddrKind::Hole`]: `forge fill` targeting a proof hole is the /// proof view (increment 2e, REQ-7), so for now this is addressable + round-trip diff --git a/thermite-syntax/src/ast.rs b/thermite-syntax/src/ast.rs index 5e900239e..fd8201fc1 100644 --- a/thermite-syntax/src/ast.rs +++ b/thermite-syntax/src/ast.rs @@ -165,15 +165,15 @@ pub struct Program { pub enum Item { Fn(FnItem), SpecFn(SpecFnItem), - /// A `struct NAME { field: TYPE, … } [inv ]` product type + /// A `struct NAME { field: type, … } [inv ]` product type /// (`.design/basis/01-adts.md` REQ-1). Struct(StructItem), - /// An `enum NAME { Variant, Variant(TYPE, …), Variant { field: TYPE, … } }` + /// An `enum NAME { Variant, Variant(type, …), Variant { field: type, … } }` /// sum type (`.design/basis/01-adts.md` REQ-2). Enum(EnumItem), /// A Stage-1 forge-tier item (`.design/stage1-forge-tier.md` REQ-3): one of the /// proof-tier surface forms parsed beside `fn` — `prop fn`, `lemma`, - /// `proof for`, `witness`. Grouped under ONE `Item` variant (with the kind + /// `proof for`, `witness`. Grouped under one `Item` variant (with the kind /// distinguished by the inner [`ForgeItem`]) so the v1 downstream consumers /// (`thermite-spec` validation, `thermite-lower` lowering, `forge check`) /// dispatch every forge-tier item through a SINGLE match arm: they have no v1 @@ -203,11 +203,11 @@ impl Item { /// A Stage-1 forge-tier item (`.design/stage1-forge-tier.md` REQ-3, increment 2a). /// The four proof-tier surface forms parsed beside `fn`. This is surface syntax /// only: the AST faithfully represents each form (with `?pN` proof holes captured -/// in proof blocks), but the SEMANTIC consumers are later increments and are not +/// in proof blocks), but the semantic consumers are later increments and are not /// built here (REQ-4 covenant / REQ-5 battery / REQ-7 proof view / REQ-9 library). #[derive(Debug, Clone, PartialEq, Eq)] pub enum ForgeItem { - /// `prop fn NAME(params) -> TYPE { body }` — a proposition (logical predicate) + /// `prop fn NAME(params) -> type { body }` — a proposition (logical predicate) /// definition, like a `spec fn` but a forge-tier proposition. PropFn(PropFnItem), /// `lemma NAME(params) req … ens … proof { … }` — a named lemma carrying a @@ -217,7 +217,7 @@ pub enum ForgeItem { /// contract clauses (`ens#k`) of an existing function `f`. Proof(ProofItem), /// `witness { inhabit (…); falsify N; }` — a covenant witness block (the - /// covenant LOGIC is increment 2b; here parsed + represented only). + /// covenant logic is increment 2b; here parsed + represented only). Witness(WitnessBlock), } @@ -235,7 +235,7 @@ impl ForgeItem { } } -/// A `prop fn NAME(params) -> TYPE { body }` proposition definition +/// A `prop fn NAME(params) -> type { body }` proposition definition /// (`.design/stage1-forge-tier.md` REQ-3). A proposition is a logical predicate — /// it mirrors [`SpecFnItem`] (params, return type, an expression body) but is a /// forge-tier definition. `dec` is the optional termination measure for a @@ -282,7 +282,7 @@ pub struct ProofItem { pub span: Span, } -/// One `CLAUSE by { … }` obligation inside a [`ProofItem`] +/// One `clause by { … }` obligation inside a [`ProofItem`] /// (`.design/stage1-forge-tier.md` REQ-3): a [`ClauseSelector`] (`ens#k`) plus the /// proof block discharging it. #[derive(Debug, Clone, PartialEq, Eq)] @@ -303,13 +303,13 @@ pub struct ClauseSelector { } /// A forge-tier proof block — the `{ … }` body of a `lemma`/`proof` form -/// (`.design/stage1-forge-tier.md` REQ-3). The block's TACTIC content is NOT +/// (`.design/stage1-forge-tier.md` REQ-3). The block's tactic content is not /// structurally parsed here (the frozen tactic battery is increment 2c, REQ-5): /// the block is captured as the verbatim source `text` plus the open proof holes /// (`?pN`) it carries, in document order, so the proof view (2e) and the battery /// (2c) can consume it next. `holes` carry [`HoleContext::Proof`]; a body hole /// `?N` inside a proof block is a structured parse error -/// (`SyntaxError::BodyHoleInProofBlock`). An OPEN proof hole blocks build and +/// (`SyntaxError::BodyHoleInProofBlock`). An open proof hole blocks build and /// certification (AC-7) once the forge consumers land. #[derive(Debug, Clone, PartialEq, Eq)] pub struct ProofBlock { @@ -322,10 +322,10 @@ pub struct ProofBlock { } /// A `witness { inhabit (…); falsify N; }` covenant witness block -/// (`.design/stage1-forge-tier.md` REQ-3/REQ-4). The covenant LOGIC — type-checking +/// (`.design/stage1-forge-tier.md` REQ-3/REQ-4). The covenant logic — type-checking /// and executing `inhabit` witnesses against `req`, running the `falsify` /// generator — is increment 2b (REQ-4); here the surface is parsed + represented + -/// round-tripped only (NO execution, NO covenant record produced). +/// round-tripped only (no execution, no covenant record produced). #[derive(Debug, Clone, PartialEq, Eq)] pub struct WitnessBlock { pub inhabits: Vec, @@ -352,7 +352,7 @@ pub struct Falsify { pub span: Span, } -/// A `struct NAME { field: TYPE, … }` product-type item, optionally carrying a +/// A `struct NAME { field: type, … }` product-type item, optionally carrying a /// type-invariant `inv ` clause (`.design/basis/01-adts.md` REQ-1). The /// `inv` reuses the existing [`Clause`] (verbatim text + parsed expr); it is /// `None` when the struct declares no invariant. Stage 1b validates field @@ -457,15 +457,15 @@ pub struct FnItem { /// `match Stmt` stay untouched. The parser records a hole here when it sees a /// `?N` in fn-body statement position (`parser.md` REQ-11). pub holes: Vec, - /// TRANSIENT refinement-type sugar (`.design/stage1-forge-tier.md` REQ-3): the + /// transient refinement-type sugar (`.design/stage1-forge-tier.md` REQ-3): the /// `x: T{P}` parameter refinements and the `-> T{P}` return refinement the - /// parser captured on this fn, BEFORE the post-parse desugar pass folds them + /// parser captured on this fn, before the post-parse desugar pass folds them /// into the contract. The pass [`crate::desugar::desugar_refinements`] runs at /// the end of [`crate::parse`] and (a) folds each parameter refinement into the /// `req` clause (`req && P` — so a caller automatically owes the refinement as a /// call-site obligation, Verus-checked), (b) appends each return refinement as /// an `ens` clause, then (c) CLEARS this vec. So in every `parse()` output this - /// is EMPTY — downstream stages (`thermite-spec` validation, lowering) see only + /// is empty — downstream stages (`thermite-spec` validation, lowering) see only /// the v1 `req`/`ens` clause shapes (REQ-3 "downstream sees only v1 clause /// shapes plus the new item kinds"). It is `Vec::new()` on every non-refined /// `FnItem` literal, mirroring the `holes: Vec::new()` / `dec: None` additive @@ -475,9 +475,9 @@ pub struct FnItem { } /// A refinement-type sugar predicate captured on a [`FnItem`] -/// (`.design/stage1-forge-tier.md` REQ-3), BEFORE the post-parse desugar pass folds +/// (`.design/stage1-forge-tier.md` REQ-3), before the post-parse desugar pass folds /// it into the contract. A `x: T{P}` parameter refinement targets the parameter; -/// a `-> T{P}` return refinement targets the result. TRANSIENT: present only +/// a `-> T{P}` return refinement targets the result. transient: present only /// between parsing and [`crate::desugar::desugar_refinements`], which folds it into /// `req`/`ens` and clears it (so it never reaches downstream stages). #[derive(Debug, Clone, PartialEq, Eq)] @@ -626,7 +626,7 @@ impl BvWidth { /// clause-level annotation in `thermite-syntax` (`.design/stage3-bv-reconstruction.md` /// REQ-1). It marks a clause for interpretation over fixed-width wraparound /// (`by(bit_vector)`, QF_BV) semantics. `nowrap` additionally requests the -/// no-overflow side obligation (REQ-5). The tag parses ONLY when the shadow-flag +/// no-overflow side obligation (REQ-5). The tag parses only when the shadow-flag /// plumbing is compiled in (the `bv` cargo feature, REQ-1's structural /// lock R-BV-1); lowering + the three locks are REQ-2..REQ-5. #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -640,7 +640,7 @@ pub struct BvTag { pub span: Span, } -/// A clause carrying its parsed expression AND the verbatim source text it was +/// A clause carrying its parsed expression and the verbatim source text it was /// built from. The `text` is the oracle string `address.rs` resolves an /// `inv`/`dec` address to (semantic-addressing.md AC-1/AC-2). /// @@ -648,7 +648,7 @@ pub struct BvTag { /// (`.design/stage3-bv-reconstruction.md` REQ-1). It is `None` for every v1/v2 /// clause and for every clause when the `bv` plumbing is not compiled in /// (the tag cannot parse there); `Some` only on an `ens`/`inv`/`req`/lemma clause -/// that carried the tag in a `bv` build. The tag sits OUTSIDE `text`, so +/// that carried the tag in a `bv` build. The tag sits outside `text`, so /// the addressing oracle string is unchanged by it. #[derive(Debug, Clone, PartialEq, Eq)] pub struct Clause { @@ -844,9 +844,9 @@ pub enum IndexArg { #[derive(Debug, Clone, PartialEq, Eq)] pub enum Expr { /// An integer literal carrying both the numeric `value` (with `_` - /// separators stripped, ast.md REQ-6 VALUE, the original semantics + /// separators stripped, ast.md REQ-6 value, the original semantics /// unchanged) and the verbatim source `raw` (separators included, ast.md - /// REQ-6 RAW, #37). `1_000_000` parses to `{ value: 1000000, raw: + /// REQ-6 raw, #37). `1_000_000` parses to `{ value: 1000000, raw: /// "1_000_000" }`. Lowering/mutation/vacuity consume `value`, not /// `raw` (no golden churn); `raw` is AST-fidelity / round-trip only. IntLit { @@ -972,9 +972,9 @@ pub enum Expr { /// sort `S`; `domain` is the `` expression it ranges over (parsed after the /// contextual `in`, e.g. a slice/carrier); `body` is the quantified formula φ. /// - /// This is the FOUNDATION increment blocking REQ-1 (the Lean `Strat/Syntax` denote + /// This is the foundation increment blocking REQ-1 (the Lean `Strat/Syntax` denote /// path) and REQ-4 (the Rust classifier): until raw `forall`/`exists` parse, the - /// classifier cannot see a quantified formula. It is deliberately DISTINCT from the + /// classifier cannot see a quantified formula. It is distinct from the /// registry-free `forall_in`/`forall_below`/`forall_from`/`sorted` COMBINATOR calls /// — those stay ordinary [`Expr::Call`] nodes and the combinator registry /// (`thermite-spec/src/combinators.rs`) is untouched as surface syntax. Surface + diff --git a/thermite-syntax/src/desugar.rs b/thermite-syntax/src/desugar.rs index 3a73275be..d073dd636 100644 --- a/thermite-syntax/src/desugar.rs +++ b/thermite-syntax/src/desugar.rs @@ -1,24 +1,24 @@ //! Post-parse desugaring passes (`.design/stage1-forge-tier.md` REQ-3, the forge -//! tier). This is the NEW post-parse pass the refinement-type sugar resolution +//! tier). This is the new post-parse pass the refinement-type sugar resolution //! (Q-DECWF note) calls for: none existed in `thermite-syntax` before. It runs at //! the end of [`crate::parse`], after the recursive-descent parser has built the //! `Program`, and rewrites the surface sugar into the v1 clause shapes so every //! downstream stage (`thermite-spec` validation, `thermite-lower` lowering, `forge`) -//! sees ONLY the v1 contract grammar plus the new forge-tier item kinds. +//! sees only the v1 contract grammar plus the new forge-tier item kinds. //! //! ## Refinement-type sugar (`x: T{P}` / `-> T{P}`) //! //! A refined parameter `x: T{P}` says "the argument is a `T` satisfying `P`"; a //! refined return `-> T{P}` says "the result is a `T` satisfying `P`". The parser -//! captures the predicates on [`crate::ast::FnItem::refinements`] (TRANSIENT). This +//! captures the predicates on [`crate::ast::FnItem::refinements`] (transient). This //! pass folds them into the function's mandatory v1 contract: //! //! - a PARAMETER refinement `P` becomes a `req` conjunct (`req` ← `req && P`). The -//! precondition is the function's own assumption AND — because Verus enforces a +//! precondition is the function's own assumption and — because Verus enforces a //! callee's `req` at every call site — the refinement automatically becomes the //! caller's proof obligation (the "call-site obligations" of REQ-3), with no //! separate mechanism. -//! - a RETURN refinement `P` becomes a new `ens` clause (the result postcondition). +//! - a return refinement `P` becomes a new `ens` clause (the result postcondition). //! //! After folding, `refinements` is cleared, so the sugar is invisible downstream. @@ -42,7 +42,7 @@ pub fn desugar_refinements(program: &mut Program) { continue; } // Take the refinements out so we can fold them by value (and so the field - // is left EMPTY — the post-condition downstream relies on). + // is left empty — the post-condition downstream relies on). let refinements = std::mem::take(&mut f.refinements); for Refinement { target, pred } in refinements { match target { diff --git a/thermite-syntax/src/lexer.rs b/thermite-syntax/src/lexer.rs index 2b22ff01e..089063500 100644 --- a/thermite-syntax/src/lexer.rs +++ b/thermite-syntax/src/lexer.rs @@ -37,9 +37,9 @@ //! //! - `?N` — a body-position hole (`.design/forge/goal-repl.md` REQ-4, #193): //! `?` + a run of ASCII digits → `Hole { number: N, proof: false }`. -//! - `?pN` — a PROOF hole (`.design/stage1-forge-tier.md` REQ-3, the forge tier): +//! - `?pN` — a proof hole (`.design/stage1-forge-tier.md` REQ-3, the forge tier): //! `?` + `p` + a run of ASCII digits → `Hole { number: N, proof: true }`. The -//! `p` sigil rides the SAME machinery (no multibyte / no new token kind); only +//! `p` sigil rides the same machinery (no multibyte / no new token kind); only //! the `proof` discriminant differs. //! //! A bare `?` with no following digit (or `?p` with no digit) is a stray-char @@ -116,7 +116,7 @@ pub enum TokKind { /// REQ-0): the raw binder production `forall (x : S) in . φ` the (R2) index /// grammar admits. A RESERVED keyword (the closed set, REQ-2) so the binder is /// recognized unambiguously at expression head. The registry-free combinator - /// identifiers `forall_in`/`forall_below`/`forall_from` are DISTINCT words (they + /// identifiers `forall_in`/`forall_below`/`forall_from` are distinct words (they /// still lex to [`TokKind::Ident`]); only the bare `forall` is reserved, leaving /// the combinator registry untouched. Forall, @@ -129,8 +129,8 @@ pub enum TokKind { // Literals / names. Ident(String), /// An integer literal carrying both the numeric `value` (with `_` - /// separators stripped, lexer.md REQ-3 VALUE, unchanged) and the verbatim - /// source `raw` (separators included, lexer.md REQ-3 RAW, #37). E.g. + /// separators stripped, lexer.md REQ-3 value, unchanged) and the verbatim + /// source `raw` (separators included, lexer.md REQ-3 raw, #37). E.g. /// `1_000_000` lexes to `{ value: 1000000, raw: "1_000_000" }`. Int { value: u128, @@ -196,7 +196,7 @@ pub enum TokKind { At, /// A structural HOLE — either a body hole `?N` (`.design/forge/goal-repl.md` - /// REQ-4, #193) or a PROOF hole `?pN` (`.design/stage1-forge-tier.md` REQ-3, + /// REQ-4, #193) or a proof hole `?pN` (`.design/stage1-forge-tier.md` REQ-3, /// the forge tier), distinguished by `proof`. `number` is the verbatim hole /// number as written (`?0`/`?p0` → `0`); it is the surface ordinal the agent /// typed, not a document-order index (the parser records holes in document @@ -428,9 +428,9 @@ fn lex_word(bytes: &[u8], i: usize) -> (Token, usize) { } /// Lex an integer literal with optional `_` separators (lexer.md REQ-3). The -/// `_` are stripped while accumulating the numeric `value` (VALUE); the verbatim +/// `_` are stripped while accumulating the numeric `value` (value); the verbatim /// source slice (separators + any `0x`/`0b` prefix included) is captured as `raw` -/// (RAW, #37). +/// (raw, #37). /// /// The radix is chosen by the prefix at the start of a digit run (lexer.md REQ-3, /// #92): `0x`/`0X` → hexadecimal, `0b`/`0B` → binary, otherwise decimal. A hex / diff --git a/thermite-syntax/src/parser.rs b/thermite-syntax/src/parser.rs index 0b719e6b6..6a61cedcf 100644 --- a/thermite-syntax/src/parser.rs +++ b/thermite-syntax/src/parser.rs @@ -141,24 +141,24 @@ pub enum SyntaxError { /// rule): break/continue are loop-control statements and have no meaning at /// a function-body top level; `keyword` is `"break"` or `"continue"`. BreakContinueOutsideLoop { keyword: String, span: Span }, - /// A body-position hole `?N` parsed OUTSIDE an exec-fn body + /// A body-position hole `?N` parsed outside an exec-fn body /// (`.design/forge/goal-repl.md` REQ-4, #193). The v1 scope pin: holes are /// exec-fn-body statement position only; a `?N` in a `spec fn` body, a clause, /// an expression, or a signature is a structural parse error (`number` is the /// verbatim hole number written). HoleOutsideFnBody { number: u32, span: Span }, - /// A proof hole `?pN` parsed OUTSIDE a proof block + /// A proof hole `?pN` parsed outside a proof block /// (`.design/stage1-forge-tier.md` REQ-3, AC-7). The forge-tier scope pin: /// proof holes are valid only inside a `lemma`/`proof`-item proof block; a /// `?pN` in fn-body statement position, a clause, an expression, or a signature /// is a structural parse error (`number` is the verbatim hole number written). ProofHoleOutsideProofBlock { number: u32, span: Span }, - /// A body hole `?N` parsed INSIDE a proof block + /// A body hole `?N` parsed inside a proof block /// (`.design/stage1-forge-tier.md` REQ-3). A proof block admits only proof /// holes `?pN`; a body hole `?N` there is the mirror error of /// `ProofHoleOutsideProofBlock` (`number` is the verbatim hole number written). BodyHoleInProofBlock { number: u32, span: Span }, - /// A `@bv` machine-semantics clause tag appeared in a build WITHOUT the + /// A `@bv` machine-semantics clause tag appeared in a build without the /// shadow-flag plumbing compiled in (`.design/stage3-bv-reconstruction.md` /// REQ-1, AC-1). This is the structural lock (R-BV-1): the feature cannot /// exist without its visibility machinery, so a build that lacks the @@ -313,7 +313,7 @@ impl std::fmt::Display for SyntaxError { impl std::error::Error for SyntaxError {} -/// The result of parsing: the recovered program AND every diagnostic, so even a +/// The result of parsing: the recovered program and every diagnostic, so even a /// partial failure yields the surviving items for tooling (parser.md REQ-4). #[derive(Debug, Clone, PartialEq, Eq)] pub struct ParseResult { @@ -402,7 +402,7 @@ struct Parser<'a> { /// (REQ-2): the parser owns presence/position; Verus owns the invariant/ /// decreases semantics (`verus-lowering.md` REQ-12). loop_depth: usize, - /// Current EXEC-fn-body nesting depth (`.design/forge/goal-repl.md` REQ-4, + /// Current exec-fn-body nesting depth (`.design/forge/goal-repl.md` REQ-4, /// #193). Incremented around the body parse of an `Item::Fn` (`parse_fn`), /// decremented after; a nested `loop`/`if`/`while` block keeps it > 0 (a hole /// in a nested block within a fn body is still in "fn-body statement position"). @@ -679,7 +679,7 @@ impl<'a> Parser<'a> { } // Stage-1 forge-tier items (`.design/stage1-forge-tier.md` REQ-3), led by - // CONTEXTUAL identifiers (not reserved keywords — like `for`/`Box`/`Vec`, + // contextual identifiers (not reserved keywords — like `for`/`Box`/`Vec`, // so they never collide with an existing program identifier): `prop fn`, // `lemma`, `proof for`, `witness`. The match peeks (and looks one ahead for // `prop fn`) without consuming, so a plain identifier named e.g. `proof` @@ -710,7 +710,7 @@ impl<'a> Parser<'a> { if self.check(&TokKind::Spec) { // Neither `#[slag]` nor `#[boundary]` attaches to a `spec fn` - // (surface-grammar Item; ffi-boundary.md "#[boundary] is NOT valid on + // (surface-grammar Item; ffi-boundary.md "#[boundary] is not valid on // a spec fn"); `#[sealed]` is a `struct`-only barrier (REQ-8). match &attr { Some(ParsedAttr::Slag(_)) => { @@ -960,7 +960,7 @@ impl<'a> Parser<'a> { })) } - /// Parse a `[#[sealed]] struct NAME { field: TYPE, … } [inv ]` item + /// Parse a `[#[sealed]] struct NAME { field: type, … } [inv ]` item /// (`.design/basis/01-adts.md` REQ-1; the seal is /// `.design/basis/06-provenance-and-sinks.md` REQ-8). The optional `inv` /// type-invariant clause follows the closing brace and reuses the existing @@ -990,7 +990,7 @@ impl<'a> Parser<'a> { })) } - /// Parse a `{ field: TYPE, … }` field-definition block, shared by `struct` + /// Parse a `{ field: type, … }` field-definition block, shared by `struct` /// items and struct-shaped enum variants (`.design/basis/01-adts.md` /// REQ-1/REQ-2). A trailing comma is permitted. fn parse_field_defs(&mut self) -> PResult> { @@ -1014,9 +1014,9 @@ impl<'a> Parser<'a> { Ok(fields) } - /// Parse an `enum NAME { Variant, Variant(TYPE, …), Variant { field: TYPE, … } + /// Parse an `enum NAME { Variant, Variant(type, …), Variant { field: type, … } /// }` item (`.design/basis/01-adts.md` REQ-2). A variant is `Unit` (bare - /// name), `Tuple` (`(TYPE, …)`), or `Struct` (`{ field: TYPE, … }`). A + /// name), `Tuple` (`(type, …)`), or `Struct` (`{ field: type, … }`). A /// trailing comma is permitted. Recursive `Box` self-refs parse via /// `parse_type` (REQ-3). fn parse_enum(&mut self, start_span: Span) -> PResult { @@ -1071,9 +1071,9 @@ impl<'a> Parser<'a> { // ---- Stage-1 forge-tier items (`.design/stage1-forge-tier.md` REQ-3) ------- - /// Parse a `prop fn NAME(params) -> TYPE [dec ] { body }` proposition + /// Parse a `prop fn NAME(params) -> type [dec ] { body }` proposition /// definition (REQ-3). Mirrors `parse_spec_fn` (it is a forge-tier definition, - /// not an exec fn): the body is parsed WITHOUT entering an exec-fn-body scope, + /// not an exec fn): the body is parsed without entering an exec-fn-body scope, /// so a `?N` body hole in a prop fn body is rejected (`HoleOutsideFnBody`), the /// same as a `spec fn` body. The optional `dec` accepts the same measure surface /// the other `dec` positions do (`dec ` / `dec lex(…)` / `dec wf `). @@ -1101,7 +1101,7 @@ impl<'a> Parser<'a> { }) } - /// Parse a `lemma NAME(params) req CLAUSE ens CLAUSE+ proof { … }` item + /// Parse a `lemma NAME(params) req clause ens clause+ proof { … }` item /// (REQ-3). The `req`/`ens` cardinality mirrors a `fn` contract (exactly one /// `req`, one-or-more `ens`) but a lemma carries no `fx` row — it is pure proof. /// The `proof { … }` block is captured verbatim (its tactic content is the @@ -1147,7 +1147,7 @@ impl<'a> Parser<'a> { }) } - /// Parse a `proof for f { CLAUSE by { … } … }` item (REQ-3): a proof discharging + /// Parse a `proof for f { clause by { … } … }` item (REQ-3): a proof discharging /// specific contract clauses (`ens#k`) of an existing function `f`. Each /// obligation is a [`ClauseSelector`] + a `by { … }` proof block (with `?pN` /// proof holes). The clauses are resolved against `f` by the proof view @@ -1209,7 +1209,7 @@ impl<'a> Parser<'a> { /// Parse a `witness { inhabit (…); falsify N; }` covenant witness block /// (REQ-3/REQ-4). Surface only: `inhabit` records an author-stated witness tuple - /// of expressions, `falsify` records the generator budget. The covenant LOGIC + /// of expressions, `falsify` records the generator budget. The covenant logic /// (type-check + execute against `req`, run the generator) is increment 2b. fn parse_witness(&mut self, start_span: Span) -> PResult { self.expect_contextual("witness")?; @@ -1260,7 +1260,7 @@ impl<'a> Parser<'a> { }) } - /// Scan a forge-tier proof block `{ … }` (REQ-3) WITHOUT structurally parsing + /// Scan a forge-tier proof block `{ … }` (REQ-3) without structurally parsing /// its tactic content (the frozen tactic battery is increment 2c, REQ-5). /// Captures the verbatim inner source `text` plus the open proof holes (`?pN`, /// [`HoleContext::Proof`]) in document order, tracking brace depth so nested @@ -1469,7 +1469,7 @@ impl<'a> Parser<'a> { fn parse_clause(&mut self, keyword: &TokKind) -> PResult { self.consume(keyword, "a clause keyword")?; // An optional `@bvN` machine-semantics tag sits between the keyword and the - // clause expression (`ens@bv64 P`). It is parsed BEFORE `start` so the + // clause expression (`ens@bv64 P`). It is parsed before `start` so the // clause `text` (the addressing oracle string) stays the expression only. let bv = self.parse_bv_tag()?; let start = self.peek_span(); @@ -1498,10 +1498,10 @@ impl<'a> Parser<'a> { /// only if the shadow-flag plumbing is compiled in (the `bv` cargo /// feature) — this is the build-flag gate, REQ-1's structural lock R-BV-1: /// - /// - WITHOUT the feature, the `@`-handling code path below is `#[cfg]`-removed, + /// - without the feature, the `@`-handling code path below is `#[cfg]`-removed, /// so the tag is a structured parse error (`BvTagWithoutShadowPlumbing`) and - /// the feature genuinely cannot exist in the build (AC-1's negative half). - /// - WITH the feature, `@bvN` for N ∈ {8, 16, 32, 64} parses, plus the optional + /// the feature cannot exist in the build (AC-1's negative half). + /// - with the feature, `@bvN` for N ∈ {8, 16, 32, 64} parses, plus the optional /// `(nowrap)` modifier (`@bvN(nowrap)`, REQ-5's surface). A bad width /// (`@bv7`, `@bv`) is `BvWidthInvalid`; a malformed modifier is the generic /// unexpected-token error. @@ -1578,11 +1578,11 @@ impl<'a> Parser<'a> { /// Parse a `dec ` clause, supporting the forge-tier measure forms /// (`.design/stage1-forge-tier.md` REQ-3, Q-DECWF). Consumes the `dec` keyword, /// then: - /// - `dec wf ` — a WELL-FOUNDED relation (ASCII spelling per Q-DECWF, NOT + /// - `dec wf ` — a WELL-FOUNDED relation (ASCII spelling per Q-DECWF, not /// the Unicode `⟨⟩` — the lexer stays ASCII-only). Since `wf ` is two /// tokens (not one expression), it is normalized to the registry-free call /// `wf()` so a downstream consumer keys on the `wf` callee. A bare `wf` - /// NOT followed by an expression (a `{` body, a clause keyword, EOF) is an + /// not followed by an expression (a `{` body, a clause keyword, EOF) is an /// ordinary measure named `wf` (the v1 reading), handled by the plain path. /// - `dec lex(, …)` — a LEXICOGRAPHIC tuple. `lex` is a contextual ident, so /// `lex(...)` is ALREADY an ordinary `Expr::Call` (registry-free, like the @@ -1927,7 +1927,7 @@ impl<'a> Parser<'a> { } /// Parse + desugar a C10 `for i in lo..hi inv … { B }` bounded-range loop - /// (`.design/basis/11-ergonomics.md` REQ-2). `for`/`in` are CONTEXTUAL + /// (`.design/basis/11-ergonomics.md` REQ-2). `for`/`in` are contextual /// identifiers (not reserved keywords), so the caller dispatched on /// `Ident("for")`. Pure desugar to the shipped `while`+`inv`/`dec` core: /// `let mut i = lo;` @@ -2731,8 +2731,8 @@ impl<'a> Parser<'a> { /// The keyword (`forall`/`exists`) is at the cursor. /// /// Grammar: `QUANT '(' IDENT ':' IDENT ')' 'in' '.' `. `in` is a - /// CONTEXTUAL identifier (mirroring the C10 `for … in` loop precedent, - /// `parse_for`), NOT a reserved keyword. The `` is parsed with the postfix + /// contextual identifier (mirroring the C10 `for … in` loop precedent, + /// `parse_for`), not a reserved keyword. The `` is parsed with the postfix /// `.` suppressed (`with_no_dot`) so the `.` introducing the body is /// unambiguous; the body is a full greedy `parse_expr` (lowest precedence). The /// parser builds the node unconditionally — well-sortedness of `S`/`` and @@ -2944,7 +2944,7 @@ impl<'a> Parser<'a> { // An arm body is in value position, so a struct-literal construction // (`Point { x: 1 }`) must parse here even when the `match` sits under // an enclosing no-struct-literal head (a contract clause / `match` - // scrutinee). Re-enable struct literals exactly as `parse_call_args` + // scrutinee). Re-enable struct literals as `parse_call_args` // does inside `( … )` (REQ-2/REQ-4); the scrutinee above stays under // the no-struct-literal context, and `with_struct_literal` restores // the prior context on exit so no leak escapes the body. @@ -3151,7 +3151,7 @@ impl<'a> Parser<'a> { TokKind::LParen => { self.bump(); if self.check(&TokKind::RParen) { - // Arity 0: `()` is the unit type (UNCHANGED). + // Arity 0: `()` is the unit type (unchanged). self.bump(); return Ok(Type::Unit); } @@ -3203,7 +3203,7 @@ impl<'a> Parser<'a> { "usize" => Ok(Type::Prim(PrimType::Usize)), "bool" => Ok(Type::Prim(PrimType::Bool)), // The heap-indirection primitive `Box` - // (`.design/basis/01-adts.md` REQ-3, OQ-1 RESOLVED: a + // (`.design/basis/01-adts.md` REQ-3, OQ-1 resolved: a // dedicated `Type::Box` node). `Box` is a contextual // identifier (not a reserved keyword), matched here by name. "Box" => { @@ -3213,7 +3213,7 @@ impl<'a> Parser<'a> { Ok(Type::Box(Box::new(inner))) } // The bounded growable-collection primitive `Vec` - // (`.design/basis/04-collections.md` REQ-1, OQ-2 RESOLVED: a + // (`.design/basis/04-collections.md` REQ-1, OQ-2 resolved: a // dedicated `Type::Vec` node, mirroring `Box`). `Vec` is a // contextual identifier (not a reserved keyword), matched here // by name as `Box` is. The element type `T` parses @@ -3228,7 +3228,7 @@ impl<'a> Parser<'a> { Ok(Type::Vec(Box::new(inner))) } // The bounded owned-text primitive `String` - // (`.design/basis/07-strings.md` REQ-2, OQ-3 RESOLVED: a + // (`.design/basis/07-strings.md` REQ-2, OQ-3 resolved: a // dedicated nullary `Type::String` node with no `` argument, // unlike `Vec`, because the element type is fixed to `u8` // (the char model is bytes for v1). `String` is a contextual @@ -3240,7 +3240,7 @@ impl<'a> Parser<'a> { // new surface; `==`/`+` are the existing `Binary` ops. "String" => Ok(Type::String), // The built-in optional primitive `Option` - // (`.design/basis/09-option-result.md` REQ-1, OQ-1 RESOLVED: a + // (`.design/basis/09-option-result.md` REQ-1, OQ-1 resolved: a // dedicated `Type::Option` node, mirroring `Box`/`Vec`). // `Option` stops being a string-named `Generic` so the // lowerer/validator key on the node kind. `Option` is a @@ -3254,7 +3254,7 @@ impl<'a> Parser<'a> { Ok(Type::Option(Box::new(inner))) } // The built-in fallible primitive `Result` - // (`.design/basis/09-option-result.md` REQ-2, OQ-1 RESOLVED: a + // (`.design/basis/09-option-result.md` REQ-2, OQ-1 resolved: a // dedicated two-type-argument node, the first two-arg type in // the grammar, the parser change of C7). The // single-arg `Generic { name, arg }` dies at the comma; this arm @@ -3271,7 +3271,7 @@ impl<'a> Parser<'a> { Ok(Type::Result(Box::new(ok_ty), Box::new(err_ty))) } // The bounded verified key-value primitive `Map` - // (`.design/basis/13-map.md` REQ-1, C12: the SECOND + // (`.design/basis/13-map.md` REQ-1, C12: the second // two-type-argument node, mirroring `Result`: // the single-arg `Generic { name, arg }` cannot carry a key and // a value (it dies at the comma, the C7 finding). `Map` is diff --git a/thermite-syntax/tests/bv_tag_parse.rs b/thermite-syntax/tests/bv_tag_parse.rs index 3482475cd..cb426922f 100644 --- a/thermite-syntax/tests/bv_tag_parse.rs +++ b/thermite-syntax/tests/bv_tag_parse.rs @@ -3,10 +3,10 @@ //! annotation in `thermite-syntax`, parse-gated behind the shadow-flag plumbing. //! //! The tag (`ens@bvN` / `inv@bvN` / `@bvN(nowrap)`, N ∈ {8, 16, 32, 64}) parses -//! ONLY when the crate is built with the `bv` cargo feature — the -//! structural lock R-BV-1: a build without the plumbing genuinely cannot parse +//! only when the crate is built with the `bv` cargo feature — the +//! structural lock R-BV-1: a build without the plumbing cannot parse //! the tag (the parser code path is `#[cfg]`-removed), so the tag is a structured -//! syntax error there. This file pins BOTH halves of AC-1, each behind the +//! syntax error there. This file pins both halves of AC-1, each behind the //! matching `cfg`: //! //! - `#[cfg(not(feature = "bv"))]` — the negative half: `ens@bv64` fails to @@ -15,7 +15,7 @@ //! `nowrap` parse with the AST tag recovered and the clause `text` round-trips //! (the tag sits outside the addressing oracle string). //! -//! CI runs `cargo test -p thermite-syntax` (negative half) AND +//! CI runs `cargo test -p thermite-syntax` (negative half) and //! `cargo test -p thermite-syntax --features bv` (positive half) so both //! configurations are exercised in one run (AC-1's "build-flag test in CI"). //! R-CHAR-3: shapes hand-derived from the grammar; `tests/` is ungated. @@ -31,7 +31,7 @@ use thermite_syntax::{parse, SyntaxError}; mod plumbing_absent { use super::*; - /// `ens@bv64` in a build WITHOUT `bv` is a structured syntax error + /// `ens@bv64` in a build without `bv` is a structured syntax error /// pointing at the `@` (the structural lock R-BV-1 / AC-1 negative half). #[test] fn ens_bv_tag_is_a_parse_error_without_plumbing() { @@ -97,7 +97,7 @@ mod plumbing_present { use super::*; use thermite_syntax::{BvWidth, Clause, ForgeItem, Item}; - /// Parse `src` cleanly and return the single `fn`'s `ens` clauses. + /// Parse `src` and return the single `fn`'s `ens` clauses. fn fn_ens(src: &str) -> Vec { let result = parse(src); assert!( @@ -143,7 +143,7 @@ mod plumbing_present { #[test] fn the_tag_sits_outside_the_clause_text_round_trip() { // The clause `text` is the addressing oracle string (semantic-addressing - // AC-1): the `@bv64` tag must NOT bleed into it — only the expression. + // AC-1): the `@bv64` tag must not bleed into it — only the expression. let ens = fn_ens(&fn_with_ens("@bv64")); assert_eq!(ens[0].text, "result == 0"); assert!(ens[0].bv.is_some()); diff --git a/thermite-syntax/tests/conformance.rs b/thermite-syntax/tests/conformance.rs index 18e11bca6..8c1098eb4 100644 --- a/thermite-syntax/tests/conformance.rs +++ b/thermite-syntax/tests/conformance.rs @@ -416,7 +416,7 @@ fn check_addresses(oracle_file: &str) { oracle.source ); - // Every `inv`/`dec` address must resolve to the verbatim oracle text, AND + // Every `inv`/`dec` address must resolve to the verbatim oracle text, and // resolution must be the inverse of computation (AC-4). for fact in &oracle.addresses { let entry = resolve(&result.program, &fact.addr) diff --git a/thermite-syntax/tests/dec_forms.rs b/thermite-syntax/tests/dec_forms.rs index 49369dfad..3ba98d69e 100644 --- a/thermite-syntax/tests/dec_forms.rs +++ b/thermite-syntax/tests/dec_forms.rs @@ -9,7 +9,7 @@ use thermite_syntax::{parse, Expr, Item}; -/// Parse `src` cleanly and return the single spec fn's `dec` clause. +/// Parse `src` and return the single spec fn's `dec` clause. fn spec_fn_dec(src: &str) -> thermite_syntax::Clause { let result = parse(src); assert!( diff --git a/thermite-syntax/tests/divergence_adt_parse.rs b/thermite-syntax/tests/divergence_adt_parse.rs index c28ab4363..a284d9cf7 100644 --- a/thermite-syntax/tests/divergence_adt_parse.rs +++ b/thermite-syntax/tests/divergence_adt_parse.rs @@ -11,7 +11,7 @@ use thermite_syntax::{parse, Expr, Item}; /// clause into a `match` arm body. /// /// Authority: `.design/basis/01-adts.md` REQ-2 ("a struct-variant construction -/// is a NEW `Expr::StructLit`") and REQ-4 ("`match` in expression position"). +/// is a new `Expr::StructLit`") and REQ-4 ("`match` in expression position"). /// A `match` arm body is in value position, so a struct-literal construction /// (`Point { x: 1 }`) there must parse, as it does in any other value /// position (e.g. a `let` initializer, which `tests/adt_parse.rs` @@ -36,7 +36,7 @@ use thermite_syntax::{parse, Expr, Item}; #[test] fn divergence_clause_match_arm_body_struct_lit_parses() { // A `fn` whose `ens` clause is a bare `match` (a no-struct-literal head) and - // whose arm bodies construct a struct in VALUE position. + // whose arm bodies construct a struct in value position. let src = "\ struct Point { x: u64, } enum E { A, B, } diff --git a/thermite-syntax/tests/divergence_final.rs b/thermite-syntax/tests/divergence_final.rs index 335db58c4..c11ddd065 100644 --- a/thermite-syntax/tests/divergence_final.rs +++ b/thermite-syntax/tests/divergence_final.rs @@ -75,7 +75,7 @@ fn divergence_value_less_trailing_if_is_statement_not_tail_expr() { // Both branches are assignment statements -> neither branch produces a // value. By surface-grammar.md decision #2 ("the expression form ... must // have a value; the statement form does not") + OQ-3 ("the corpus only uses - // the statement form"), this is the STATEMENT form: it must be the LAST + // the statement form"), this is the statement form: it must be the last // `Stmt::If` in the body, and the loop body must have no tail expr. // // EXPECTED (design authority): loop body tail is None; last stmt is Stmt::If diff --git a/thermite-syntax/tests/divergence_grammar.rs b/thermite-syntax/tests/divergence_grammar.rs index 2e915f47d..403ea0ecc 100644 --- a/thermite-syntax/tests/divergence_grammar.rs +++ b/thermite-syntax/tests/divergence_grammar.rs @@ -115,7 +115,7 @@ fn divergence_if_else_in_tail_position_is_expr() { let Some(Block { stmts, tail }) = &f.body else { panic!("expected an in-language fn with a body (not a boundary fn)"); }; - // The if/else is the block's VALUE: it must be the tail Expr::If, not a + // The if/else is the block's value: it must be the tail Expr::If, not a // statement (surface-grammar.md Block grammar + ast.md REQ-6). assert!( matches!(tail.as_deref(), Some(Expr::If { .. })), diff --git a/thermite-syntax/tests/divergence_intlit.rs b/thermite-syntax/tests/divergence_intlit.rs index b6558e676..3033f3d7c 100644 --- a/thermite-syntax/tests/divergence_intlit.rs +++ b/thermite-syntax/tests/divergence_intlit.rs @@ -59,7 +59,7 @@ fn divergence_intlit_multi_underscore_value_and_raw() { /// neither value nor raw; both end at the last digit. `1_000_` lexes to value /// `1000` and raw `"1_000"` (the trailing `_` is dropped from raw; interior /// `_` kept). Expected hand-derived from REQ-3 ("A trailing/leading `_` … -/// excluded from BOTH the value and the raw (the raw ends at the last digit)"). +/// excluded from both the value and the raw (the raw ends at the last digit)"). #[test] fn divergence_intlit_trailing_underscore_excluded_from_raw() { assert_eq!(first_int("1_000_"), Some((1_000u128, "1_000".to_string()))); diff --git a/thermite-syntax/tests/divergence_reaudit.rs b/thermite-syntax/tests/divergence_reaudit.rs index cd8437681..6feeeb737 100644 --- a/thermite-syntax/tests/divergence_reaudit.rs +++ b/thermite-syntax/tests/divergence_reaudit.rs @@ -142,7 +142,7 @@ fn divergence_deep_enum_pattern_no_panic() { /// `parse_block`), which calls `parse_block` for the branches; their tails /// re-enter the `If`-token arm of `parse_block` → `parse_if_parts` again. This /// `parse_block`/`parse_if_parts` cycle never increments `expr_depth` (only the -/// `if` CONDITION goes through `parse_expr`), so a 1500-deep nest of +/// `if` condition goes through `parse_expr`), so a 1500-deep nest of /// `if x == 0 { } else { 0 }` as the function-body tail drives unbounded /// native recursion and SIGABRTs. /// diff --git a/thermite-syntax/tests/divergence_reaudit2.rs b/thermite-syntax/tests/divergence_reaudit2.rs index 561b8eb9a..d6e487259 100644 --- a/thermite-syntax/tests/divergence_reaudit2.rs +++ b/thermite-syntax/tests/divergence_reaudit2.rs @@ -67,7 +67,7 @@ fn in_fn(inner: &str) -> String { /// `parse_block` dispatches a `loop`/`while` statement to `parse_loop`, whose /// body is parsed by `parse_block` again — a `parse_block`<->`parse_loop` cycle /// that routes through neither `guard_recursion` entry point (the #31 fix -/// guarded expr/type/pattern/if-tail, not this loop-body cycle). DEPTH nested +/// guarded expr/type/pattern/if-tail, not this loop-body cycle). depth nested /// `loop`s (each `loop inv true dec 0 { ... }`) therefore recurse unbounded. /// /// Authority: `surface-grammar.md` REQ-3 / EBNF `LoopExpr ::= 'loop' @@ -94,7 +94,7 @@ fn divergence_deep_nested_loop_no_panic() { /// /// Same `parse_block`<->`parse_loop` cycle as D2-R1, via the `while` arm. The /// `while` condition routes through the (guarded) `parse_expr`, but the -/// BODY-cycle re-entry (`parse_loop` -> `parse_block` -> `parse_loop`) does not, +/// body-cycle re-entry (`parse_loop` -> `parse_block` -> `parse_loop`) does not, /// so the guard caps the condition expression, never the loop nesting. /// /// Authority: `surface-grammar.md` REQ-3 / EBNF `WhileExpr ::= 'while' Expr @@ -115,7 +115,7 @@ fn divergence_deep_nested_while_no_panic() { ); } -/// CONTROL — Deeply nested STATEMENT-form `if` is already bounded. +/// CONTROL — Deeply nested statement-form `if` is already bounded. /// /// Statement `if a { if a { ... } }` re-enters `parse_block` -> `parse_if_parts` /// (GUARDED by the #31 fix) -> `parse_block`, so this path SHOULD return a diff --git a/thermite-syntax/tests/forge_items.rs b/thermite-syntax/tests/forge_items.rs index cdf9e59a4..b610c1501 100644 --- a/thermite-syntax/tests/forge_items.rs +++ b/thermite-syntax/tests/forge_items.rs @@ -2,7 +2,7 @@ //! items (`.design/stage1-forge-tier.md` REQ-3 / AC-7, increment 2a): `prop fn`, //! `lemma`, `proof for`, `witness`, and the `?pN` proof holes inside proof blocks. //! -//! These items are PARSE-ONLY in this increment (their semantic consumers are the +//! These items are PARSE-only in this increment (their semantic consumers are the //! covenant engine 2b, the tactic battery 2c, the proof view 2e, the lemma library //! 3); the tests here are their consumers — they assert the parsed AST shape and //! the semantic addresses (`f.proof.ens#k`, `?pN`). R-CHAR-3: expected shapes are @@ -80,7 +80,7 @@ fn lemma_parses_with_req_ens_and_proof_block() { assert_eq!(l.name, "add_id"); assert_eq!(l.params.len(), 1); assert_eq!(l.ens.len(), 1); - // The proof block captures verbatim tactic text (NOT structurally parsed) and + // The proof block captures verbatim tactic text (not structurally parsed) and // has no open holes here. assert_eq!(l.proof.text, "omega"); assert!(l.proof.holes.is_empty()); @@ -235,7 +235,7 @@ fn nested_braces_in_proof_block_are_balanced() { #[test] fn forge_items_do_not_disturb_v1_items_in_a_mixed_program() { - // A program mixing a v1 `fn` with forge items parses cleanly; the v1 fn keeps + // A program mixing a v1 `fn` with forge items parses; the v1 fn keeps // its ordinary address and the forge items add theirs. let src = "fn id(x: u64) -> u64 req true ens result == x fx pure { x }\n\ lemma l(a: u64) req true ens a == a proof { omega }\n\ diff --git a/thermite-syntax/tests/operators_parse.rs b/thermite-syntax/tests/operators_parse.rs index 0fc0b07b4..797a66505 100644 --- a/thermite-syntax/tests/operators_parse.rs +++ b/thermite-syntax/tests/operators_parse.rs @@ -17,7 +17,7 @@ use thermite_syntax::ast::{BinOp, Expr, Item, UnaryOp}; use thermite_syntax::{parse, tokenize, TokKind}; // --------------------------------------------------------------------------- -// Lexer: char / hex / binary literals → the SAME Int token (lexer.md REQ-3/REQ-9). +// Lexer: char / hex / binary literals → the same Int token (lexer.md REQ-3/REQ-9). // --------------------------------------------------------------------------- /// The single `Int` token a one-literal source lexes to, asserting zero @@ -36,7 +36,7 @@ fn lex_int_literal(src: &str) -> (u128, String) { #[test] fn char_literal_lexes_to_byte_value_int_token() { - // AC-8: `'A'` is the byte value 65 (the u8 char model), carried by the SAME + // AC-8: `'A'` is the byte value 65 (the u8 char model), carried by the same // `Int` token as a numeric literal; raw is the verbatim `"'A'"`. assert_eq!(lex_int_literal("'A'"), (65, "'A'".to_string())); // `'\n'` == 10, `'\x1b'` == 27 (the shared escape table). @@ -46,7 +46,7 @@ fn char_literal_lexes_to_byte_value_int_token() { #[test] fn hex_and_binary_literals_lex_to_decimal_value() { - // AC-7: a hex/binary literal carries the SAME integer value as the decimal, + // AC-7: a hex/binary literal carries the same integer value as the decimal, // with the verbatim raw (prefix preserved, #37). assert_eq!(lex_int_literal("0x1b"), (27, "0x1b".to_string())); assert_eq!(lex_int_literal("0b101"), (5, "0b101".to_string())); @@ -229,7 +229,7 @@ fn binary_pipe_distinct_from_closure_pipe() { #[test] fn char_hex_binary_parse_to_intlit_no_new_variant() { // AC-7 / ast.md AC-1c: `'A'`/`0x1b`/`0b101` each parse to `Expr::IntLit` (the - // SAME node as a decimal), carrying the byte/radix value + verbatim raw. + // same node as a decimal), carrying the byte/radix value + verbatim raw. for (src, value, raw) in [ ("'A'", 65u128, "'A'"), ("0x1b", 27, "0x1b"), diff --git a/thermite-syntax/tests/proof_hole_lex.rs b/thermite-syntax/tests/proof_hole_lex.rs index 3536a1d46..0f7c76f79 100644 --- a/thermite-syntax/tests/proof_hole_lex.rs +++ b/thermite-syntax/tests/proof_hole_lex.rs @@ -78,7 +78,7 @@ fn digit_then_p_is_a_body_hole_then_ident() { #[test] fn proof_hole_in_fn_body_is_structured_error_not_a_body_hole() { // A `?pN` in fn-body statement position is rejected: proof holes live only in - // proof blocks (AC-7). It is NOT silently reclassified as a body hole. + // proof blocks (AC-7). It is not silently reclassified as a body hole. let src = "fn f(x: u64) -> u64 req true ens result == x fx pure { ?p0 }"; let result = parse(src); assert!( diff --git a/thermite-syntax/tests/quantifiers_parse.rs b/thermite-syntax/tests/quantifiers_parse.rs index ecfe662ca..24f4cfee4 100644 --- a/thermite-syntax/tests/quantifiers_parse.rs +++ b/thermite-syntax/tests/quantifiers_parse.rs @@ -3,16 +3,16 @@ //! REQ-0, AC-0): the surface binder grammar over a named sorted carrier the (R2) //! index grammar admits, plus the binder/scope corner-case pins. //! -//! REQ-0 is the FOUNDATION increment blocking REQ-1 (the Lean `Strat/Syntax` -//! denote path) and REQ-4 (the Rust classifier). It is deliberately DISTINCT from +//! REQ-0 is the foundation increment blocking REQ-1 (the Lean `Strat/Syntax` +//! denote path) and REQ-4 (the Rust classifier). It is distinct from //! the registry-free `forall_in`/`forall_below`/`forall_from`/`sorted` COMBINATOR //! calls, which stay ordinary `Expr::Call` nodes — the combinator registry is -//! untouched (the registry-unchanged pins below assert exactly this). R-CHAR-3: +//! untouched (the registry-unchanged pins below assert this). R-CHAR-3: //! shapes hand-derived from the grammar; `tests/` is ungated. use thermite_syntax::{parse, BinOp, Expr, ForgeItem, Item, Quant}; -/// Parse `src` cleanly and return the body tail `Expr` of the single `prop fn` +/// Parse `src` and return the body tail `Expr` of the single `prop fn` /// (its `{ }` body is a bool-valued expression — a convenient quantifier /// carrier needing no contract scaffolding). fn prop_body(src: &str) -> Expr { @@ -30,7 +30,7 @@ fn prop_body(src: &str) -> Expr { } } -/// Parse `src` cleanly and return the `req` clause of the single `fn` (both its +/// Parse `src` and return the `req` clause of the single `fn` (both its /// parsed `expr` and its verbatim `text`, for the round-trip assertion). fn fn_req(src: &str) -> (Expr, String) { let result = parse(src); @@ -118,7 +118,7 @@ fn quantifier_req_clause_preserves_verbatim_text() { #[test] fn body_is_greedy_lowest_precedence() { - // `forall …. a && b` reads the WHOLE `a && b` as the body, not just `a` + // `forall …. a && b` reads the whole `a && b` as the body, not just `a` // (the binder body extends greedily to the right). let e = prop_body(&prop_with_body( "forall (i : Idx) in xs. xs[i] != 0 && xs[i] != 1", @@ -203,7 +203,7 @@ fn quantifiers_nest_in_the_body() { #[test] fn domain_may_be_an_indexed_slice_before_the_dot_separator() { - // `in xs[..n].` — the domain is an index expression; the `.` AFTER the `]` + // `in xs[..n].` — the domain is an index expression; the `.` after the `]` // (not a field access) separates the domain from the body. The `[..n]` slice // uses `..` (DotDot), distinct from the body-separator `.` (Dot). let e = prop_body(&prop_with_body("forall (i : Idx) in xs[..3]. xs[i] != 0")); @@ -222,7 +222,7 @@ fn domain_may_be_an_indexed_slice_before_the_dot_separator() { #[test] fn a_parenthesized_domain_reenables_field_dots() { // Inside a parenthesised domain the postfix `.` is RE-ENABLED, so `(a.b)` is a - // field access and the FIRST `.` after the `)` is the body separator. + // field access and the first `.` after the `)` is the body separator. let e = prop_body(&prop_with_body("forall (i : Idx) in (xs.foo). xs[i] != 0")); match e { Expr::Quantifier { domain, body, .. } => { @@ -240,9 +240,9 @@ fn a_parenthesized_domain_reenables_field_dots() { #[test] fn forall_in_combinator_stays_a_plain_call_not_a_binder() { - // `forall_in` is a DISTINCT identifier from the `forall` keyword (the lexer + // `forall_in` is a distinct identifier from the `forall` keyword (the lexer // keys on the full word), so the registry-free combinator call is unchanged: - // it parses as an ordinary `Expr::Call`, NOT an `Expr::Quantifier`. + // it parses as an ordinary `Expr::Call`, not an `Expr::Quantifier`. let e = prop_body(&prop_with_body("forall_in(xs, |x| x != 0)")); match e { Expr::Call { callee, args } => { diff --git a/thermite-syntax/tests/refinement_sugar.rs b/thermite-syntax/tests/refinement_sugar.rs index 9866ee6d6..045e85670 100644 --- a/thermite-syntax/tests/refinement_sugar.rs +++ b/thermite-syntax/tests/refinement_sugar.rs @@ -1,16 +1,16 @@ //! Tests for the refinement-type sugar `x: T{P}` / `-> T{P}` and its post-parse //! desugar pass (`.design/stage1-forge-tier.md` REQ-3, increment 2a). //! -//! The sugar desugars in a NEW post-parse pass (`thermite_syntax::desugar`) so -//! downstream stages see ONLY the v1 `req`/`ens` clause shapes: a parameter +//! The sugar desugars in a new post-parse pass (`thermite_syntax::desugar`) so +//! downstream stages see only the v1 `req`/`ens` clause shapes: a parameter //! refinement folds into `req` (and so becomes a Verus-checked call-site //! obligation), a return refinement folds into `ens`. After parsing, the transient -//! `FnItem.refinements` store is EMPTY. R-CHAR-3: expected shapes are hand-derived +//! `FnItem.refinements` store is empty. R-CHAR-3: expected shapes are hand-derived //! from the grammar; `tests/` is ungated. use thermite_syntax::{parse, BinOp, Expr, Item}; -/// Parse `src` cleanly and return its single `fn` item. +/// Parse `src` and return its single `fn` item. fn single_fn(src: &str) -> thermite_syntax::FnItem { let result = parse(src); assert!( @@ -28,7 +28,7 @@ fn single_fn(src: &str) -> thermite_syntax::FnItem { fn param_refinement_folds_into_req_and_clears_the_transient_store() { let src = "fn f(x: u64{x > 0}) -> u64 req true ens result == x fx pure { x }"; let f = single_fn(src); - // The transient refinement store is EMPTY post-parse: downstream sees v1 shapes. + // The transient refinement store is empty post-parse: downstream sees v1 shapes. assert!( f.refinements.is_empty(), "refinements must be folded + cleared, got: {:?}", @@ -91,7 +91,7 @@ fn an_unrefined_fn_is_byte_stable() { #[test] fn refinement_predicate_is_a_parsed_expression() { - // The predicate is a real contract-position expression, not opaque text. + // The predicate is a contract-position expression, not opaque text. let src = "fn f(x: u64{x > 0 && x < 10}) -> u64 req true ens result == x fx pure { x }"; let f = single_fn(src); // The folded req's rhs conjunct is the (parsed) predicate `x > 0 && x < 10`. diff --git a/thermite-tv/examples/strat_probe.rs b/thermite-tv/examples/strat_probe.rs index 21a53a9db..c7637a186 100644 --- a/thermite-tv/examples/strat_probe.rs +++ b/thermite-tv/examples/strat_probe.rs @@ -106,7 +106,7 @@ struct Instance { /// chosen disjoint from the generator's free-variable vocabulary (`gen.rs` draws /// slices `xs`/`ys` and indices from `n`/`m`/`k`), and the production-style /// binders (`i`/`j`) likewise. A binder that coincided with a free index variable -/// (`forall k . … k < k …` when the index is `k`) would be a genuine capture (the +/// (`forall k . … k < k …` when the index is `k`) would be a capture (the /// two spellings would then not be equivalent), so the probe avoids it. fn templates(inst: &Instance) -> (String, String) { let s = &inst.slice_a; diff --git a/thermite-tv/src/exec_encode.rs b/thermite-tv/src/exec_encode.rs index 19f9bd9ac..038b5b06f 100644 --- a/thermite-tv/src/exec_encode.rs +++ b/thermite-tv/src/exec_encode.rs @@ -52,12 +52,12 @@ use std::fmt; use thermite_syntax::ast::{BinOp, Expr, IndexArg, PrimType, Type, UnaryOp}; -/// An honest failure to encode a construct outside the pure-exec subset (REQ-1). +/// An failure to encode a construct outside the pure-exec subset (REQ-1). /// The exec reference encoder never panics and never silently emits a wrong /// encoding: an unsupported construct is a real `Err` carrying the offending shape /// (R-CODE-2 / R-APG-1). A silent wrong encoding would compare a wrong reference /// and either spuriously pass or spuriously fail. Method calls / Vec-String -/// accessors are out of scope for step 2.1 (the #154/#156 territory) → an honest +/// accessors are out of scope for step 2.1 (the #154/#156 territory) → an /// [`RefEncodeError::Unsupported`]. #[derive(Debug, Clone, PartialEq, Eq)] pub enum RefEncodeError { @@ -138,7 +138,7 @@ impl ExecRefCtx { /// the bounded element value). /// /// Anything else (a method call, a Vec/String accessor, a struct literal, an `if`/ -/// `match`, a closure, …) is an honest [`RefEncodeError::Unsupported`] (never a +/// `match`, a closure, …) is an [`RefEncodeError::Unsupported`] (never a /// panic, never a silent wrong encoding — #154/#156 territory). pub fn exec_ref_value(expr: &Expr, ctx: &ExecRefCtx) -> Result { encode(expr, ctx) @@ -254,7 +254,7 @@ fn encode_unary(op: UnaryOp, inner: &Expr, ctx: &ExecRefCtx) -> Result Result { let Expr::Path(segments) = callee else { return Err(RefEncodeError::Unsupported(format!( @@ -276,7 +276,7 @@ fn encode_call(callee: &Expr, args: &[Expr], ctx: &ExecRefCtx) -> Result Result { let IndexArg::Single(i) = index else { @@ -344,7 +344,7 @@ fn encode_cast(inner: &Expr, ty: &Type, ctx: &ExecRefCtx) -> Result Result { @@ -477,7 +477,7 @@ mod tests { } /// A method call (exec / Vec-String accessor) is out of scope for step 2.1 → - /// an honest `Err`, never a silent wrong encoding (REQ-1 / R-CODE-2). + /// an `Err`, never a silent wrong encoding (REQ-1 / R-CODE-2). #[test] fn method_call_is_unsupported_not_panic() { let e = Expr::MethodCall { @@ -492,7 +492,7 @@ mod tests { } /// A bare index over a non-slice base has no scalar-value denotation in the - /// frozen subset → an honest `Err`. + /// frozen subset → an `Err`. #[test] fn non_slice_index_is_unsupported() { let e = Expr::Index { diff --git a/thermite-tv/src/exec_stmt_encode.rs b/thermite-tv/src/exec_stmt_encode.rs index 58c17fd30..1d38e5cc3 100644 --- a/thermite-tv/src/exec_stmt_encode.rs +++ b/thermite-tv/src/exec_stmt_encode.rs @@ -27,7 +27,7 @@ //! substituted under the current env, order-sensitive: `s = s + 1; s = s * 2` //! threads `s |-> x` -> `s |-> (x + 1)` -> `s |-> ((x + 1) * 2)`, but the reorder //! `s = s * 2; s = s + 1` threads to `((x * 2) + 1)`, a different closed form -//! (the state-sequencing teeth — `exec-stmt-tv.md` AC-3). +//! (the state-sequencing check in `exec-stmt-tv.md` AC-3). //! - `if c { .. } else { .. }` as the body tail composes the two branch //! state-transformers into a Verus `if`-expression over the (substituted) //! condition — `if c { } else { }` (`exec-stmt-tv.md` @@ -56,7 +56,7 @@ //! //! ## Honest boundary (out of the frozen 2.2.1 subset -> an `Err`, never silent-wrong) //! -//! A construct outside the straight-line subset is an honest +//! A construct outside the straight-line subset is an //! [`crate::exec_encode::RefEncodeError::Unsupported`] (R-CODE-2 / R-APG-1 — never a //! panic, never a silent wrong denotation): a `Stmt::Loop`/`Break`/`Continue` (step //! 2.2.2, kernel-gated), a mid-body early `return` nested in an `if` branch (the @@ -91,7 +91,7 @@ //! re-constrained to `inv ∧ ¬cond` (the analogue of how Verus itself models a //! loop's after-state). Every out-of-v1 loop (`loop`-kind, `break`/`continue`, a //! mid-body `return`, a nested loop, non-scalar state, a trivially-weak `inv`) is an -//! honest [`RefEncodeError::Unsupported`] (R-HONEST-3 — Skipped, never silently +//! [`RefEncodeError::Unsupported`] (R-HONEST-3 — Skipped, never silently //! Faithful). //! //! @@ -188,7 +188,7 @@ pub fn body_ref_state(block: &Block, ctx: &BodyRefCtx) -> Result` compares element-wise at the bounded type /// (the grounded projection equality `r.0 == b`, `ast.rs` `TupleProj`). The -/// reorder/wrong-cell teeth bite on whichever projection differs (B4's `b` cell). +/// reorder and wrong-cell tests fail on whichever projection differs (B4's `b` cell). /// /// This is the obligation-shape concern (how `result` is compared), kept distinct /// from [`body_ref_state`] (the state denotation itself, REQ-2). Reuses the same @@ -306,7 +306,7 @@ pub fn loop_ref_obligations( }; // A trivially-weak `inv` (the conjunction is the bare `true`) is out of v1 (the - // after-loop `true ∧ ¬cond` is vacuous) — honest Err, checked before encoding. + // after-loop `true ∧ ¬cond` is vacuous), checked before encoding. if invariant_is_vacuous(&loop_node.invs) { return Err(RefEncodeError::Unsupported( "trivially-weak loop invariant (`inv true` — the after-loop `true ∧ ¬cond` \ @@ -340,7 +340,7 @@ pub fn loop_ref_obligations( // substitutes the whole entry env (so a referenced `hi |-> n` is resolved, not // left free); the fn inputs are the only surviving free vars. Every cell must have // a prefix `let mut` binding (an assigned cell needs an in-scope introducer); - // honest Err otherwise. + // Return Err otherwise. let mut entry_env: Env = Env::new(); for stmt in prefix { thread_stmt(stmt, &mut entry_env)?; @@ -406,7 +406,7 @@ pub fn loop_ref_obligations( /// Recognize the v1 frozen-subset `while` loop: `block`'s last statement must be a /// `Stmt::Loop` with `kind: While(_)`, non-empty `invs`, a `dec`, and a straight-line /// scalar body containing no nested loop / `break` / `continue` / mid-body `return`. -/// Returns the pre-loop prefix statements + the loop node, or an honest +/// Returns the pre-loop prefix statements + the loop node, or an /// [`RefEncodeError::Unsupported`] naming the out-of-v1 reason (Skipped, never /// silently Faithful — R-HONEST-3). fn recognize_v1_loop(block: &Block) -> Result<(&[Stmt], &LoopNode), RefEncodeError> { @@ -441,7 +441,7 @@ fn recognize_v1_loop(block: &Block) -> Result<(&[Stmt], &LoopNode), RefEncodeErr } if loop_node.invs.is_empty() { // Structurally LoopNode carries a non-empty invs (the parser enforces §4.1); - // the honest Err keeps the rule total against a hand-built node. + // the Err keeps the rule total against a hand-built node. return Err(RefEncodeError::Unsupported( "`while` loop with no `inv` (v1's after-loop characterization needs a \ usable invariant — Skipped honestly)" @@ -455,12 +455,12 @@ fn recognize_v1_loop(block: &Block) -> Result<(&[Stmt], &LoopNode), RefEncodeErr } /// Reject an out-of-v1 loop body: a nested `Stmt::Loop`, a `break`/`continue`, or a -/// mid-body `return` (the multi-exit CPS forms) → an honest +/// mid-body `return` (the multi-exit CPS forms) → an /// [`RefEncodeError::Unsupported`]. Recurses into `if`-branch bodies (a `break` / /// `return` nested in an `if` is just as out). A straight-line scalar body (the v1 /// in-set: `let`/`assign`/`if`/`expr`) passes; the per-statement value/scalar /// rejection is left to the shipped [`thread_stmt`] (e.g. a non-scalar assignment is -/// already an honest Err there). +/// already an Err there). fn reject_out_of_subset_body(body: &Block) -> Result<(), RefEncodeError> { for stmt in &body.stmts { reject_out_of_subset_stmt(stmt)?; @@ -507,7 +507,7 @@ fn reject_out_of_subset_stmt(stmt: &Stmt) -> Result<(), RefEncodeError> { /// `Stmt::Assign` to a bare in-scope name). Recurses into `if`-branch bodies (a cell /// mutated in a branch is a mutated cell). A `let`-introduced branch-local binding is /// not a mutated outer cell (it does not leak — the body_ref_state semantics), so a -/// branch-local `let mid = ..` is excluded. Returns an honest Err only on a malformed +/// branch-local `let mid = ..` is excluded. Returns an Err only on a malformed /// non-bare-name target (left to the shipped threading otherwise). fn collect_assigned_cells(body: &Block) -> Result, RefEncodeError> { let mut cells = BTreeSet::new(); @@ -556,7 +556,7 @@ fn collect_assigned_cells_block( /// cells are substituted by their env value (entry / stepped) then the predicate is /// reused through [`exec_ref_value`] (the bounded comparison / logical reference — the /// same independent encoder the per-RHS value uses). A predicate outside the bounded -/// exec sublanguage (a quantifier, a spec-only combinator) is an honest Err from +/// exec sublanguage (a quantifier, a spec-only combinator) is an Err from /// [`exec_ref_value`]: the v1 loop subset is scalar-comparison invariants (`lo <= /// hi`, `i <= n`), never a `forall_*` (those are the `binary_search` v2 forms). fn encode_predicate(expr: &Expr, env: &Env, ctx: &BodyRefCtx) -> Result { @@ -598,7 +598,7 @@ pub fn negate_condition(cond: &str) -> String { /// Thread `block`'s statements through `env` (in order), then encode its tail value /// under the resulting env. A block with no tail (a unit-valued straight-line body) /// is outside the v1 single-exit value subset: the body-refinement obligation -/// compares a result value, so a tail is required (an honest `Err` otherwise). +/// compares a result value, so a tail is required (an `Err` otherwise). fn encode_block_tail( block: &Block, env: &mut Env, @@ -623,7 +623,7 @@ fn encode_block_tail( /// `Expr` here; `If`/`Return` are only admitted in tail position (handled by /// [`encode_value`] / the tail), so an `If`/`Return` in non-tail (statement) /// position — a mid-body branch / early return — is out of v1 (the multi-exit CPS -/// form) and an honest `Err`. A `Loop`/`Break`/`Continue` is step 2.2.2. +/// form) and an `Err`. A `Loop`/`Break`/`Continue` is step 2.2.2. fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { match stmt { Stmt::Let { @@ -631,7 +631,7 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { } => { // A re-shadow `let x = ..; let x = ..` in the same block is out of v1 // (the flat name->value env can't represent two distinct `x` cells) — - // honest `Err`, never a silent wrong substitution. + // `Err`, never a silent wrong substitution. if env.contains_key(name) { return Err(RefEncodeError::Unsupported(format!( "re-shadowed binding `{name}` in the same block (the v1 state \ @@ -660,7 +660,7 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { } }; // The cell must already be in scope (a `let mut` introduced it). An - // assignment to an unbound name is malformed input — an honest `Err`. + // assignment to an unbound name is malformed input — an `Err`. if !env.contains_key(&name) { return Err(RefEncodeError::Unsupported(format!( "assignment to the unbound cell `{name}` (no in-scope `let mut` \ @@ -668,7 +668,7 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { ))); } // Order-sensitive: substitute under the current env (the value before - // this assignment), then rebind. This is the state-sequencing teeth: a + // this assignment), then rebind. This preserves assignment order: a // reorder threads a different substitution chain -> a different closed // form (`exec-stmt-tv.md` AC-3). let substituted = substitute(value, env)?; @@ -695,7 +695,7 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { // states (the state-transformer semantics — exec-stmt-tv.md REQ-2 / AC-4). A // cell mutated in neither branch is unchanged. The recursion handles a nested // `if`-statement in a branch; an out-of-subset branch construct (a loop, a - // non-scalar mutation, a mid-branch return) propagates its honest `Err`. + // non-scalar mutation, a mid-branch return) propagates its `Err`. Stmt::If { cond, then, else_ } => { // The condition is itself an exec value — substitute it under the // pre-`if` env so the composed value is a closed form in the inputs. @@ -703,7 +703,7 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { // Thread each branch into its own copy of the pre-`if` env. A branch-tail // value (a value-discarding `if c { ..; v }` statement) is out of the v1 - // mutation subset — an honest `Err` (the state-denotation only composes a + // mutation subset — an `Err` (the state-denotation only composes a // branch that mutates cells, never a discarded branch value). let mut then_env = env.clone(); thread_branch(then, &mut then_env)?; @@ -773,11 +773,11 @@ fn thread_stmt(stmt: &Stmt, env: &mut Env) -> Result<(), RefEncodeError> { /// Thread an `if`-statement branch `Block`'s statements through `env` (in order), /// reusing the per-statement [`thread_stmt`] recursively (so a nested `if`-statement /// in the branch is composed, and an out-of-subset branch construct — a loop, a -/// non-scalar mutation, a mid-branch early return — propagates its honest `Err`). A +/// non-scalar mutation, a mid-branch early return — propagates its `Err`). A /// branch in the v1 mutation subset is value-less (`tail: None`): it mutates outer /// cells via `Stmt::Assign`, it does not produce a discarded value. A branch with a /// tail value (`if c { ..; v }` as a statement) is out of the v1 mutation subset — an -/// honest [`RefEncodeError::Unsupported`], never a silent discard. +/// [`RefEncodeError::Unsupported`], never a silent discard. fn thread_branch(branch: &Block, env: &mut Env) -> Result<(), RefEncodeError> { for stmt in &branch.stmts { thread_stmt(stmt, env)?; @@ -1056,7 +1056,7 @@ mod tests { ); } - /// B2 reference (the mutation-order teeth): `s = s + 1; s = s * 2` threads to + /// B2 mutation-order reference: `s = s + 1; s = s * 2` threads to /// `((x + 1) * 2)`, and the reorder threads to a different form, so the order /// matters in the reference, not just in production. #[test] @@ -1135,8 +1135,8 @@ mod tests { ); } - /// A loop body is out of the frozen 2.2.1 subset -> an honest `Err`, never a - /// silent (wrong) denotation (REQ-1 honest boundary). + /// A loop body is out of the frozen 2.2.1 subset -> an `Err`, never a + /// silent (wrong) denotation (REQ-1 boundary). #[test] fn loop_body_is_unsupported_not_panic() { use thermite_syntax::ast::{Clause, LoopKind, LoopNode}; @@ -1172,7 +1172,7 @@ mod tests { } /// A re-shadow `let x = ..; let x = ..` in the same block is out of v1 (the flat - /// env can't represent two `x` cells) -> an honest `Err`. + /// env can't represent two `x` cells) -> an `Err`. #[test] fn reshadow_is_unsupported() { let block = Block { diff --git a/thermite-tv/src/gen.rs b/thermite-tv/src/gen.rs index 11f1e56c3..a085f1b9e 100644 --- a/thermite-tv/src/gen.rs +++ b/thermite-tv/src/gen.rs @@ -14,7 +14,7 @@ //! (`thermite_lower::lower_contract_expr`) and encoded to the independent reference //! (`crate::ref_encode::ref_contract_pred`), and the per-clause Z3 equivalence //! obligation (`crate::obligation::equivalence_obligation`) is discharged on each. -//! The faithful lowerer makes all verify; any counterexample is a real off-corpus +//! The faithful lowerer makes all verify; any counterexample is a off-corpus //! infidelity finding (`thermite-design.md` §1 — auditable by a skeptical third //! party, here over an unbounded clause space). //! @@ -67,7 +67,7 @@ use thermite_syntax::ast::{BinOp, Expr, IndexArg, PrimType, Type, UnaryOp}; /// generator, enough to drive the structural choices below reproducibly. The same /// `seed` always produces the same stream. /// A self-contained SplitMix64 PRNG (R-CODE-5: deterministic, seeded, no `rand` -/// crate, no wall-clock). Public so an out-of-crate consumer can ride the SAME +/// crate, no wall-clock). Public so an out-of-crate consumer can ride the same /// generator the contract/exec TV streams ride: the forge covenant engine's `falsify` /// run (`.design/stage1-forge-tier.md` REQ-4, increment 2b) seeds it with the fixed /// `falsify` seed and draws scalar inputs from it, so a covenant's falsification @@ -165,12 +165,12 @@ const MAX_DEPTH: usize = 3; /// produced so the recursion always bottoms out. /// /// Note on byte-view (`s.byte_at(i)`/`s.len()`): a generated byte-view clause is -/// emitted (it is a frozen contract construct, F3 in the teeth), but the forge +/// emitted (it is a frozen contract construct, F3 in the negative tests), but the forge /// off-corpus run frames `s` as a `Seq` directly, so production's byte-view /// rewrite (which keys on a `&String` param + the TString wrapper) does not apply -/// uniformly and the run reports such a clause `Skipped` (an honest not-checked, +/// uniformly and the run reports such a clause `Skipped` (an not-checked, /// not a false faithful — `forge::contract_tv`). Byte-view lowering fidelity is -/// covered by the F3 teeth + the String corpus programs; framing it off-corpus +/// covered by the F3 negative test and the String corpus programs; framing it off-corpus /// needs the TString-wrapper bridge, which is String/body-TV scope (#139 step 2). fn gen_bool(rng: &mut Rng, depth: usize) -> Expr { // At the depth cap, a leaf bool form only (no further nesting). Byte-view is not @@ -193,7 +193,7 @@ fn gen_bool(rng: &mut Rng, depth: usize) -> Expr { // (3) A cast-`<`-class comparison (`n as u32 < k`) — the #146/#148 off-corpus // regression guard (#147). A leaf form, so it appears at every depth. 3 => gen_cast_lt(rng, depth), - // (4) A logical AND of two sub-predicates (nesting). + // (4) A logical and of two sub-predicates (nesting). 4 => Expr::Binary { op: BinOp::And, lhs: Box::new(gen_bool(rng, depth + 1)), @@ -205,7 +205,7 @@ fn gen_bool(rng: &mut Rng, depth: usize) -> Expr { lhs: Box::new(gen_bool(rng, depth + 1)), rhs: Box::new(gen_bool(rng, depth + 1)), }, - // (6) A logical NOT of a sub-predicate (nesting). + // (6) A logical not of a sub-predicate (nesting). _ => Expr::Unary { op: UnaryOp::Not, expr: Box::new(gen_bool(rng, depth + 1)), @@ -213,7 +213,7 @@ fn gen_bool(rng: &mut Rng, depth: usize) -> Expr { } } -/// One of the comparison operators (the `==`/`<=`-class — the F1 teeth surface). +/// One of the comparison operators covered by the F1 negative test (`==` versus `<=`). const CMP_OPS: &[BinOp] = &[ BinOp::Eq, BinOp::Ne, @@ -318,7 +318,7 @@ fn gen_nat_cmp(rng: &mut Rng) -> Expr { /// generic-arg list — the bug #146/#148 fixed in production (`lower_binary_operand`) /// and #147 gap #2 mirrored in `ref_encode` (`encode_binary_operand`). Generating it /// now confirms the fix holds off-corpus on both encoders: a divergence here is a -/// real off-corpus hole in the #146/#148 fix (report it and file a blocker). +/// off-corpus hole in the #146/#148 fix (report it and file a blocker). /// /// The cast target is an integer prim (`u32`) or `nat`; the RHS is an `int` scalar /// term (so the comparison is well-typed: `n as u32` is `u32`, `n as nat`/`int` is @@ -688,7 +688,7 @@ impl ExecScope { /// (`x as u32 < k`, casts under `<`/`<=`/`<<`), shifts, bitwise ops, and slice /// indexing (`xs[i]`) — plus the adequate overflow/index frame so the faithful /// lowering verifies (the part the critic flagged: an un-bounded `a + b` would make -/// the honest overflow obligation fire and every clause Unverifiable). The cast-`<` +/// the overflow obligation fire and every clause Unverifiable). The cast-`<` /// + arithmetic + cast classes are the off-corpus #122/#146 regression guard. /// /// Seeded + deterministic (R-CODE-5): the same `(seed, n)` always yields the same @@ -991,8 +991,8 @@ fn gen_exec_index_as(rng: &mut Rng, ty: ExecTy, _depth: usize, scope: &mut ExecS use thermite_spec::classifier::{self, Atom, Frm, Mach, Rel, Sort2, Tm}; /// The binder-nesting cap on a generated stratified formula. Two reasons (R-CODE-2): -/// it bounds the recursion so generation always terminates, and — load-bearing for the -/// differential battery — it bounds the sort-graph edge count, hence the SIZE of the +/// it bounds the recursion so generation always terminates, and — required for the +/// differential battery — it bounds the sort-graph edge count, hence the size of the /// Roy–Warshall `reach` the Lean `admitted` runs (`Strat/Graph.lean`'s `reach` is /// exponential in the node count; the Rust mirror is polynomial). Three binders keep the /// node set tiny so `lake env lean --run` on each formula stays fast. @@ -1002,7 +1002,7 @@ const STRAT_MAX_BINDERS: usize = 3; const STRAT_MAX_TM_DEPTH: usize = 2; /// The hard formula-tree depth cap. Beyond it `gen_frm` forces an atom, so the /// propositional recursion (`neg`/`conj`/`disj`/`imp`) always bottoms out and generation -/// terminates (R-CODE-2) — distinct from the binder cap, which only stops NEW binders. +/// terminates (R-CODE-2) — distinct from the binder cap, which only stops new binders. const STRAT_MAX_FRM_DEPTH: usize = 5; /// The relation operators a generated atom draws from (the full `Cls.Rel`). @@ -1010,14 +1010,14 @@ const STRAT_RELS: &[Rel] = &[Rel::Eq, Rel::Ne, Rel::Lt, Rel::Le, Rel::Gt, Rel::G /// Generate `n` well-sorted stratified-cage formulas deterministically from `seed` /// (REQ-4 / AC-4 — the differential battery's clause source). Each is a -/// `thermite_spec::classifier::Frm` over the sort-typed `Cls` surface, run through BOTH +/// `thermite_spec::classifier::Frm` over the sort-typed `Cls` surface, run through both /// the Rust classifier (`thermite_spec::classifier::admitted`) and `lake env lean --run` /// on the Lean `Thermite.Strat.Cls.admitted`; any verdict disagreement is a hard CI /// failure. /// /// The stream is the **Q5** mix the design fixes: a **corpus-mimicking** arm (the real /// array-property shapes — sortedness, bounds, nested reads, the cast/kv cycles, the -/// (R2) traps, a `seq` binder) deliberately spanning every admit/reject class, and a +/// (R2) traps, a `seq` binder) spanning every admit/reject class, and a /// **uniform-random** arm (bounded recursive descent over the full grammar). Both arms /// are well-sorted: a generated `var` is always an in-scope de Bruijn index carrying the /// sort recorded at its binder. Seeded + deterministic (R-CODE-5): the same `(seed, n)` @@ -1063,7 +1063,7 @@ fn pick_fin_sort(rng: &mut Rng) -> Sort2 { /// The corpus-mimicking arm: a hand-shaped array-property formula, one per call, chosen /// uniformly across the admit/reject classes so a single run covers them all. The -/// element sorts / relations are randomized for diversity, but the SHAPE pins the +/// element sorts / relations are randomized for diversity, but the shape pins the /// expected verdict class (named in each arm). fn gen_strat_corpus(rng: &mut Rng) -> Frm { let op = pick_rel(rng); @@ -1227,7 +1227,7 @@ impl StratCtx { /// once the binder cap or a depth budget is hit, so generation always terminates. fn gen_frm(&mut self, rng: &mut Rng, depth: usize) -> Frm { // Hard depth floor: beyond the cap, force an atom so every recursive path - // (propositional AND binder) terminates. + // (propositional and binder) terminates. if depth >= STRAT_MAX_FRM_DEPTH { return Frm::Atom(self.gen_atom(rng)); } @@ -1693,7 +1693,7 @@ mod tests { } } - /// REQ-4 / AC-4: the stream is diverse and exercises EVERY verdict class — admit, + /// REQ-4 / AC-4: the stream is diverse and exercises every verdict class — admit, /// and each frozen rejection reason (`seq-quantifier`, `index-grammar`, the named /// cycle). If any class were missing the differential battery would be vacuous for /// it, so this is the "report the construct coverage" honesty check. diff --git a/thermite-tv/src/lib.rs b/thermite-tv/src/lib.rs index 56e22fa48..e88523922 100644 --- a/thermite-tv/src/lib.rs +++ b/thermite-tv/src/lib.rs @@ -10,7 +10,7 @@ //! independent reference encoder for the SpecTherm contract sublanguage //! ([`ref_encode`]) plus a per-clause Z3 equivalence obligation //! ([`obligation`]) of the shape `assert(P_production <==> P_reference)`. A -//! divergence is a real lowering-fidelity bug (the #122 cast-paren and #127 +//! divergence is a lowering-fidelity bug (the #122 cast-paren and #127 //! byte-view-misdispatch classes) that the five existing layers structurally //! cannot see. //! @@ -93,7 +93,7 @@ pub mod obligation; pub mod ref_encode; // Stage-2 REQ-8 (`.design/stage2-stratified-cage.md` REQ-8 / AC-8): the stratified // reference encoder + the two-phase TV (syntactic normalizer + thin semantic fallback) -// + the trust flip. Unlike `normalize` (the SPIKE-2 leaf), these ARE load-bearing TV +// + the trust flip. Unlike `normalize` (the SPIKE-2 leaf), these ARE required TV // pipeline modules (consumed by `forge`'s stratified faithfulness sweep). pub mod strat_ref_encode; pub mod strat_two_phase; diff --git a/thermite-tv/src/obligation.rs b/thermite-tv/src/obligation.rs index 0e8e7d344..9369ac33b 100644 --- a/thermite-tv/src/obligation.rs +++ b/thermite-tv/src/obligation.rs @@ -22,7 +22,7 @@ //! (`thermite-design.md` §5.1 "counterexamples, not adjectives"). //! //! `thermite-tv` does not run verus itself: it emits the obligation text. The -//! teeth-test (`tests/teeth.rs`, REQ-4) and the future forge plug-in (REQ-5, +//! negative test (`tests/teeth.rs`, REQ-4) and the future forge plug-in (REQ-5, //! `forge/src/contract_tv.rs`) discharge it through the existing //! `forge::check::run_verus` path. //! @@ -36,7 +36,7 @@ //! | REQ-TV-CONTRACT-OBLIGATION | shipped | `thermite-tv/src/obligation.rs` | Contract-TV per-clause Z3 equivalence obligation | | //! //! -//! ## EXEC-position extension (`.design/verified/exec-tv.md` REQ-2; epic #151) +//! ## exec-position extension (`.design/verified/exec-tv.md` REQ-2; epic #151) //! //! [`exec_equivalence_obligation`] is the exec dual: it emits the exec-fn-wrapped //! `fn tv_exec_wrap(..) requires , ensures result == @@ -70,7 +70,7 @@ //! satisfied`), and exit (`proof fn` with `requires inv && !cond` asserting the claimed //! after-loop characterization, where an over-claim stronger than `inv ∧ ¬cond` fails). //! The single-iteration step reuses the shipped `body_ref_state` (no new body machinery, -//! AC-5); a loop out of v1 is an honest `Unsupported` from `loop_ref_obligations` +//! AC-5); a loop out of v1 is an `Unsupported` from `loop_ref_obligations` //! (Skipped, never silently Faithful, R-HONEST-3). //! //! @@ -195,8 +195,8 @@ impl ObligationFrame { /// `requires`. /// /// Returns the obligation program text (`thermite-tv` does not run verus — the -/// teeth-test / forge plug-in discharge it). Returns [`RefEncodeError`] if the -/// source clause is outside the frozen contract sublanguage (an honest error, +/// negative test and forge plug-in discharge it). Returns [`RefEncodeError`] if the +/// source clause is outside the frozen contract sublanguage (an error, /// never a panic / silent wrong encoding). pub fn equivalence_obligation( source: &Expr, @@ -249,7 +249,7 @@ pub fn equivalence_obligation( pub struct ExecParamDecl { /// The parameter name as it appears in the obligation signature and the body. pub name: String, - /// The Verus EXEC value-type spelling (`u64` / `usize` / `&[u32]` / `bool`). + /// The Verus exec value-type spelling (`u64` / `usize` / `&[u32]` / `bool`). pub type_str: String, } @@ -354,8 +354,8 @@ impl ExecObligationFrame { /// (`exec-tv.md` AC-4): the structural reason the obligation is an exec fn. /// /// Returns the obligation program text (`thermite-tv` does not run verus — the -/// teeth-test / forge plug-in discharge it). Returns [`ExecRefEncodeError`] if the -/// source body expr is outside the pure-exec subset (an honest error, never a +/// negative test and forge plug-in discharge it). Returns [`ExecRefEncodeError`] if the +/// source body expr is outside the pure-exec subset (an error, never a /// panic / silent wrong encoding). pub fn exec_equivalence_obligation( source: &Expr, @@ -408,7 +408,7 @@ pub fn exec_equivalence_obligation( pub struct BodyParamDecl { /// The parameter name as it appears in the obligation signature and the body. pub name: String, - /// The Verus EXEC value-type spelling (`u64` / `usize` / `&[u32]` / `bool`). + /// The Verus exec value-type spelling (`u64` / `usize` / `&[u32]` / `bool`). pub type_str: String, } @@ -509,16 +509,16 @@ impl BodyObligationFrame { /// faithful. A `postcondition not satisfied` counterexample is a /// state-transformation infidelity — a dropped statement, a reordered mutation, a /// swapped `if`-branch (each changes the final state while every sub-expression stays -/// value-faithful — the state-sequencing teeth that per-expression step-2.1 TV cannot +/// value-faithful, which is the state-sequencing failure that per-expression step-2.1 TV cannot /// see). The production body is an exec `fn` (not `proof`/`spec`), so the /// always-active runtime overflow checks are live (the same structural reason the /// step-2.1 obligation is an exec fn). /// /// Returns the obligation program text (`thermite-tv` does not run verus — the -/// teeth-test / the future forge plug-in discharge it). Returns +/// negative test and the future forge plug-in discharge it). Returns /// [`ExecRefEncodeError`] if the source body is outside the frozen straight-line /// subset (a loop / mid-branch early return / non-scalar mutation / re-shadow — an -/// honest error, never a panic / silent wrong encoding). +/// error, never a panic / silent wrong encoding). pub fn body_equivalence_obligation( body: &Block, p_production: &str, @@ -579,7 +579,7 @@ pub fn body_equivalence_obligation( pub struct LoopParamDecl { /// The parameter name (as it appears in the signature and the predicates). pub name: String, - /// The Verus EXEC value-type spelling (`usize` / `u64` / `&[u32]` / `bool`). + /// The Verus exec value-type spelling (`usize` / `u64` / `&[u32]` / `bool`). pub type_str: String, } @@ -702,7 +702,7 @@ impl LoopObligationFrame { /// Verified iff the invariant holds on entry; an `assertion failed` /// counterexample means the entry state violates the claimed invariant (a wrong /// pre-loop initialization). Returns [`ExecRefEncodeError`] if the loop is outside -/// the v1 frozen subset (an honest Skipped, never a panic / silent wrong encoding). +/// the v1 frozen subset (an Skipped, never a panic / silent wrong encoding). pub fn loop_entry_obligation( block: &Block, frame: &LoopObligationFrame, @@ -763,10 +763,10 @@ pub fn loop_entry_obligation( /// /// Verified iff one faithful iteration preserves the invariant (and production computes /// the reference step); a `postcondition not satisfied` is a per-iteration state-lowering -/// infidelity (a dropped / reordered / wrong-cell body mutation — the same teeth -/// `body_ref_sound`'s negative lemmas bite) or a broken-invariant body (the source step +/// infidelity (a dropped, reordered, or wrong-cell body mutation covered by +/// `body_ref_sound`'s negative lemmas) or a broken-invariant body (the source step /// does not re-establish `inv`). Returns [`ExecRefEncodeError`] if the loop is outside -/// the v1 frozen subset (an honest Skipped). +/// the v1 frozen subset (an Skipped). pub fn loop_preservation_obligation( block: &Block, p_production: &str, @@ -834,7 +834,7 @@ pub fn loop_preservation_obligation( /// verus! { /// /// proof fn tv_loop_exit(, ) -/// requires && (!()), // the genuine after-loop facts (assumed) +/// requires && (!()), // the after-loop facts (assumed) /// { /// assert(); // the production's after-loop claim /// } @@ -846,7 +846,7 @@ pub fn loop_preservation_obligation( /// claim follows); an `assertion failed` counterexample means a wrong after-loop /// characterization (an over-strong claim about the exit state — stronger than /// `inv ∧ ¬cond`). Returns [`ExecRefEncodeError`] if the loop is outside the v1 frozen -/// subset (an honest Skipped). +/// subset (an Skipped). pub fn loop_exit_obligation( block: &Block, claimed_after_loop: &str, diff --git a/thermite-tv/src/ref_encode.rs b/thermite-tv/src/ref_encode.rs index 1690d1adb..39d3c7368 100644 --- a/thermite-tv/src/ref_encode.rs +++ b/thermite-tv/src/ref_encode.rs @@ -18,7 +18,7 @@ //! //! - Re-implemented here (the infidelity surface): the spec-context rewrites //! where production fidelity bugs live — comparison/connective binop map -//! (the `==`/`<=` distinction, the canonical teeth case F1), the slice→`@` +//! (the `==`/`<=` distinction, negative case F1), the slice→`@` //! view, the method→`spec_*` byte-view dispatch keyed on the receiver's shape //! (`.byte_at(i)`/`.len()` — the #127 class, F3), and the integer cast→`as //! nat`/`as int` with the #122 paren discipline. @@ -43,7 +43,7 @@ use std::fmt; use thermite_syntax::ast::{BinOp, Expr, IndexArg, MatchArm, Pattern, UnaryOp}; -/// An honest failure to encode a construct outside the frozen contract +/// An failure to encode a construct outside the frozen contract /// sublanguage (REQ-1). The reference encoder never panics and never silently /// emits a wrong encoding: an unsupported construct is a real `Err` carrying the /// offending shape (R-CODE-2 / R-APG-1). A silent wrong encoding would compare a @@ -216,7 +216,7 @@ impl RefCtx { /// - [`Expr::Index`] (`a[i]` / `a[..i]`→`a.subrange(0, i as int)`); /// - [`Expr::Cast`]→`(inner) as nat`/`as int` with the #122 paren discipline. /// -/// Anything else is an honest [`RefEncodeError`] (never a panic, never a silent +/// Anything else is an [`RefEncodeError`] (never a panic, never a silent /// wrong encoding). pub fn ref_contract_pred(expr: &Expr, ctx: &RefCtx) -> Result { encode(expr, ctx) @@ -277,7 +277,7 @@ fn encode_path(segments: &[String]) -> Result { /// The faithful 1-to-1 binary-operator map (`thermite-design.md` §4.2). Re-stated /// here independently of the production `binop in lower.rs`: the `==`-vs-`<=` -/// distinction is the canonical teeth case (F1). If this imported production's +/// distinction is negative case F1. If this imported production's /// map, a production binop bug would be invisible. fn binop_str(op: BinOp) -> &'static str { match op { @@ -492,7 +492,7 @@ fn encode_call(callee: &Expr, args: &[Expr], ctx: &RefCtx) -> Result` is /// re-encoded to a Verus closure `|x: u32| `; the body is encoded by the /// same independent recursion (so a closure-predicate infidelity, F2's `x <= 10` -/// vs `x < 10`, is caught). A non-closure in a `Pred` slot is an honest `Err` +/// vs `x < 10`, is caught). A non-closure in a `Pred` slot is an `Err` /// (the registry says this slot must be a closure). fn encode_pred_arg(arg: &Expr, ctx: &RefCtx) -> Result { match arg { @@ -623,7 +623,7 @@ fn encode_method_call( match name { // The byte-view accessor (#127): `s.byte_at(i)` is the i-th byte of the - // sequence view, `recv[i]`. F3's teeth bite here: a production misdispatch + // sequence view, `recv[i]`. F3 covers a production misdispatch // to index `1` for source index `0` differs from this. This is the // `Seq`-bound byte-view (a #127/#147 `Seq`-receiver), distinct from // the `String`/TString wrapper byte-view above (#150 gap #2). @@ -688,7 +688,7 @@ fn encode_method_call( /// /// `.slice(..)` over a `String` is not in the frozen contract byte-view set (the /// `TString` wrapper has no `spec_slice`: `slice` is an exec constructor, never -/// named in a contract; no corpus clause uses it) → an honest [`RefEncodeError`], +/// named in a contract; no corpus clause uses it) → an [`RefEncodeError`], /// never a silent wrong encoding. fn encode_string_byteview( recv: &str, @@ -740,7 +740,7 @@ fn encode_string_byteview( /// - `.len()` → `m.len()` — the wrapper `spec fn len(&self) -> nat`, unchanged. /// /// `.get(_)`/`.insert(_)` are not spec-rewritten (production names `get` only via a -/// `match`-in-`ens` over the result, and `insert` is exec) → an honest +/// `match`-in-`ens` over the result, and `insert` is exec) → an /// [`RefEncodeError`], never a silent wrong encoding. fn encode_map_accessor( recv: &str, @@ -844,7 +844,7 @@ fn encode_match( /// a binding (`x`), and a wildcard (`_`). A nested/struct/slice/or pattern, or a /// user enum variant (which production would enum-qualify via its `variants` map — /// the reference has no such map, so qualifying it would risk a silent wrong -/// encoding) is an honest [`RefEncodeError`]. +/// encoding) is an [`RefEncodeError`]. fn encode_pattern(pat: &Pattern) -> Result { match pat { Pattern::Wildcard => Ok("_".to_string()), @@ -960,7 +960,7 @@ fn encode_index(base: &Expr, index: &IndexArg, ctx: &RefCtx) -> Result Result { match inner { diff --git a/thermite-tv/src/strat_ref_encode.rs b/thermite-tv/src/strat_ref_encode.rs index 88fcaeba3..f585c1dbc 100644 --- a/thermite-tv/src/strat_ref_encode.rs +++ b/thermite-tv/src/strat_ref_encode.rs @@ -31,7 +31,7 @@ use crate::normalize::{ArithOp, CmpOp, Formula, Quant, Term}; /// `d`: `encName d i = d - 1 - i` (index 0 ↦ the innermost binder, level `d-1`). Mirrors /// `lean/Thermite/Strat/RefEncode.lean` `encName` exactly. Rendered as `v{level}` — a /// stable positional name; the normalizer alpha-canonicalizes binders anyway, so the only -/// requirement is that the SAME source binder maps consistently (no capture). +/// requirement is that the same source binder maps consistently (no capture). fn enc_name(d: u32, i: u32) -> String { // `d` is always ≥ 1 wherever a bound var is read (a var at index `i < d` is only // reachable under ≥ `i+1` binders), so `d - 1 - i` does not underflow for bound vars. @@ -43,10 +43,10 @@ fn enc_name(d: u32, i: u32) -> String { } } -/// The reference spelling of a (machine/opaque) literal. `Cls.Tm.lit` carries NO value +/// The reference spelling of a (machine/opaque) literal. `Cls.Tm.lit` carries no value /// (`Strat/Nnf.lean`: "value irrelevant here"), so the reference names it symbolically by /// its sort — a free constant `lit_`. The production lowerer and this reference must -/// agree on this convention for the syntactic phase to hit; a genuine value mismatch is +/// agree on this convention for the syntactic phase to hit; a value mismatch is /// caught by the semantic phase (where the v1 `qfree` atoms carry real values). fn lit_name(s: &Sort2) -> String { format!("lit_{}", sort_tag(s)) diff --git a/thermite-tv/src/strat_two_phase.rs b/thermite-tv/src/strat_two_phase.rs index ddedc1626..1aaf5925b 100644 --- a/thermite-tv/src/strat_two_phase.rs +++ b/thermite-tv/src/strat_two_phase.rs @@ -14,17 +14,17 @@ //! //! ## The two phases (metatheory §8.2) //! -//! - **Phase 1 — SYNTACTIC** (the common path): normalize both encodings to the layer-1 +//! - **Phase 1 — syntactic** (the common path): normalize both encodings to the layer-1 //! canonical form ([`crate::normalize`], carrying `nnf_sound`/`prenex_sound` — the //! Lean `Strat/Nnf.lean` lemmas these passes mirror) and compare byte-for-byte. A hit -//! certifies equivalence WITHOUT a solver call. SPIKE-2 measured 40/40 = 100 % +//! certifies equivalence without a solver call. SPIKE-2 measured 40/40 = 100 % //! syntactic coverage over the corpus, clearing the ≥ 90 % bar (Q-TV2), so this is the //! dominant path. -//! - **Phase 2 — SEMANTIC** (the thin fallback): on a syntactic miss, emit the -//! negation-unfriendly quantified-equivalence Z3 query with FINITE-BOUND assertions +//! - **Phase 2 — semantic** (the thin fallback): on a syntactic miss, emit the +//! negation-unfriendly quantified-equivalence Z3 query with FINITE-bound assertions //! ([`semantic_obligation`]) and run it. The two non-quantifier combinators //! (`count_where`, a recursive `nat` fold; `permutation_of`, a multiset equality; -//! REQ-6) have NO raw-quantifier spelling, so they bypass phase 1 entirely +//! REQ-6) have no raw-quantifier spelling, so they bypass phase 1 entirely //! ([`ClauseRoute::DirectSemantic`]) and land here directly. //! - **Timeout** is HONEST: a solver timeout in phase 2 WITHHOLDS the certificate //! ([`TvVerdict::Withheld`]) — it is never reported as a pass. A withheld clause keeps @@ -33,12 +33,12 @@ //! ## The trust flip (the G2 gate) //! //! During the rollout window a stratified clause carries `trust: solver(z3) + -//! ref_encode(strat, UNPROVEN — stage 2 in progress)` — honest that the reference +//! ref_encode(strat, UNPROVEN — stage 2 in progress)`, recording that the reference //! encoder's soundness (T1-S/T2-S) is proven but the END-TO-END flip is gated on G2 //! (`make audit` [1′][4′][8][9] green, REQ-9). The flip to the proven form -//! `ref_encode(strat)` is the ONE-LINE change of the [`G2_FLIPPED`] gate, and is itself +//! `ref_encode(strat)` is the one-LINE change of the [`G2_FLIPPED`] gate, and is itself //! a tested code path ([`strat_trust_profile`] + the toggle test). The gate constraint -//! (REQ-5 option B / REQ-9): the flip must NOT trigger on REQ-5's structural soundness +//! (REQ-5 option B / REQ-9): the flip must not trigger on REQ-5's structural soundness //! alone — it attests "proven over source meaning", which is REQ-8's atom-grounding //! (`lean/Thermite/Strat/Faithfulness.lean` T2-S), gated on the audit. //! @@ -63,14 +63,14 @@ pub enum ClauseRoute { } /// The outcome of a phase-2 semantic Z3 query (the pluggable solver oracle's verdict). -/// The solver execution lives in the caller (`forge`/Verus), exactly as the contract-TV +/// The solver execution lives in the caller (`forge`/Verus), as the contract-TV /// obligation text is executed there — this crate produces the query and routes the /// verdict. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum SemanticOutcome { /// Z3 proved `production <==> reference` (over the finite-bound model). Equivalent, - /// Z3 found a model where they differ — a real lowering-fidelity bug. + /// Z3 found a model where they differ — a lowering-fidelity bug. Divergent, /// Z3 timed out / returned `unknown` — no verdict. Timeout, @@ -91,9 +91,9 @@ pub enum TvVerdict { /// Certified equivalent by the named phase — the clause may carry the stratified /// trust profile. Certified(TvPhase), - /// A real divergence (phase 2 found a counter-model): the lowering is NOT faithful. + /// A real divergence (phase 2 found a counter-model): the lowering is not faithful. Divergent, - /// The semantic phase timed out — the certificate is WITHHELD (honest `Timeout` + /// The semantic phase timed out — the certificate is WITHHELD (`Timeout` /// fallback). Never a pass; the clause keeps the conservative trust profile. Withheld, } @@ -111,7 +111,7 @@ impl TvVerdict { /// /// `route` selects whether phase 1 is attempted (`count_where`/`permutation_of` skip it). /// `solve` is the phase-2 oracle: it is invoked at most once, with the -/// [`semantic_obligation`] text, ONLY when phase 1 misses (or is skipped). The closure +/// [`semantic_obligation`] text, only when phase 1 misses (or is skipped). The closure /// shape keeps the solver out of this crate (independence) while making the routing, /// the withhold-on-timeout, and the direct-semantic path fully unit-testable. pub fn classify_pair( @@ -134,7 +134,7 @@ pub fn classify_pair( } /// Build the phase-2 semantic obligation: the negation-unfriendly quantified-equivalence -/// Z3 query with FINITE-BOUND assertions (metatheory §8.2). The query asserts the +/// Z3 query with FINITE-bound assertions (metatheory §8.2). The query asserts the /// production and reference encodings are equivalent over a bounded model — every carrier /// is constrained to a finite size so the `forall`s have a decidable instantiation set /// (the (R1) finite-carrier datum, mirrored at the solver). The text is a Verus/SMT @@ -196,7 +196,7 @@ pub struct PhaseSplit { pub semantic: usize, /// Withheld: the semantic phase timed out (no certificate). pub timeout_withheld: usize, - /// Divergent: a real lowering-fidelity bug (phase 2 found a counter-model). + /// Divergent: a lowering-fidelity bug (phase 2 found a counter-model). pub divergent: usize, } @@ -209,7 +209,7 @@ impl PhaseSplit { /// The run is clean iff no clause diverged and none was withheld — i.e. every clause /// was certified by one of the two phases. (A withheld clause is not a failure of the - /// lowering, but it is NOT a pass either; a clean two-phase sweep certifies all.) + /// lowering, but it is not a pass either; a clean two-phase sweep certifies all.) #[must_use] pub fn all_certified(&self) -> bool { self.divergent == 0 && self.timeout_withheld == 0 @@ -288,33 +288,33 @@ pub fn render_report(report: &TwoPhaseReport, header: &str) -> String { // The trust flip (the G2 gate) // =========================================================================== -/// THE G2 DECLARATION (REQ-8's one-line flip, now enabled by REQ-9). This is the +/// the G2 declaration (REQ-8's one-line flip, now enabled by REQ-9). This is the /// compiled-in intent — "the four stratified soundness theorems are in the spine and the /// G2 trust flip is ENABLED" — flipped to `true` at G2 (REQ-9), the increment that built /// the audit gate and saw [1′][4′][8][9] green in one `make audit` run. /// -/// CRUCIALLY, the declaration alone does NOT emit the proven label: the EFFECTIVE per-clause -/// flip is [`g2_flip_permitted`]`(G2_FLIPPED, &checks)` — the declaration AND every gating +/// CRUCIALLY, the declaration alone does not emit the proven label: the effective per-clause +/// flip is [`g2_flip_permitted`]`(G2_FLIPPED, &checks)` — the declaration and every gating /// audit check green. A red check downgrades the label back to the conservative rollout /// form regardless of this constant (the AC-9 mechanical block: a flipped certificate can /// never out-run the audit that justifies it). `forge g2-gate` is the runtime enforcer — -/// it FAILS `make audit` if `G2_FLIPPED` is set while any of the four is red. +/// it fails `make audit` if `G2_FLIPPED` is set while any of the four is red. /// -/// The proven label is honestly SCOPED (REQ-9 / REQ-5 option B): structure proven (T1-S), +/// The proven label is scoped (REQ-9 / REQ-5 option B): structure proven (T1-S), /// qfree atoms grounded to the v1 `Thermite.denote` (T2-S), and rel/array atoms discharged /// by Z3's theory (the solver base) — kernel-grounding the rel atoms is stage-3 /// reconstruction. See [`REF_ENCODE_PROVEN`]. pub const G2_FLIPPED: bool = true; -/// The conservative (pre-G2) reference-encoder trust string: honest that the reference -/// encoder is sound (T1-S/T2-S) but the end-to-end flip is gated on G2. +/// The conservative pre-G2 trust string records that the reference encoder is sound +/// (T1-S/T2-S) while the end-to-end flip remains gated on G2. pub const REF_ENCODE_UNPROVEN: &str = "ref_encode(strat, UNPROVEN — stage 2 in progress)"; -/// The proven (post-G2) reference-encoder trust string — HONESTLY SCOPED (REQ-9 / REQ-5 +/// The proven (post-G2) reference-encoder trust string — HONESTLY scoped (REQ-9 / REQ-5 /// option B / #330–#331). The flip attests exactly: the quantifier/boolean STRUCTURE is /// proven faithful (T1-S `strat_ref_sound`), `qfree` atoms are grounded to the v1 /// `Thermite.denote` (T2-S `strat_lowering_faithful`), and `rel`/array atoms are discharged -/// by Z3's theory (the solver base — model-relative, the honest L4 boundary). It does NOT +/// by Z3's theory (the solver base — model-relative, the L4 boundary). It does not /// claim kernel-grounding of the rel/array atoms; that is stage-3 reconstruction. The /// string starts with `ref_encode(strat)` (the recognizable proven prefix) and carries the /// scope inline so a reader of the certificate sees the boundary, not an over-claim. @@ -329,8 +329,8 @@ pub const SOLVER_Z3: &str = "solver(z3)"; /// The trust profile a stratified clause carries, parameterized by the gate. This is the /// flip's tested code path: `g2_proven == false` (the rollout window) reads the UNPROVEN /// form; `g2_proven == true` (post-G2) reads the proven form. The solver component is -/// unchanged. A clause whose two-phase verdict is NOT certified -/// ([`TvVerdict::is_certified`]) must NOT be given this profile by the caller (a withheld +/// unchanged. A clause whose two-phase verdict is not certified +/// ([`TvVerdict::is_certified`]) must not be given this profile by the caller (a withheld /// or divergent clause keeps the conservative cage profile). #[must_use] pub fn strat_trust_profile(g2_proven: bool) -> Vec { @@ -357,7 +357,7 @@ pub fn strat_trust_profile_current() -> Vec { /// The four `make audit` checks that gate the G2 trust flip (REQ-9 / AC-9). Each field is /// the green (`true`) / red (`false`) outcome of one audit sub-check; the flip is permitted -/// only when EVERY one is green. The field names mirror the design-doc check labels +/// only when every one is green. The field names mirror the design-doc check labels /// ([1′][4′][8][9], `.design/stage2-stratified-cage.md` REQ-9). #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct G2Checks { @@ -386,7 +386,7 @@ impl G2Checks { self.axiom_probe && self.doc_drift && self.differential && self.two_phase_tv } - /// The labels of the RED checks (for the audit report / the withhold reason). Empty iff + /// The labels of the red checks (for the audit report / the withhold reason). Empty iff /// [`all_green`](Self::all_green). Deterministically ordered [1′][4′][8][9]. #[must_use] pub fn red(&self) -> Vec<&'static str> { @@ -418,8 +418,8 @@ impl G2Checks { } } -/// THE G2 GATE (AC-9). The trust flip is PERMITTED iff G2 is DECLARED (`declared`, the -/// compiled-in [`G2_FLIPPED`]) AND every gating audit check is green +/// the G2 gate (AC-9). The trust flip is PERMITTED iff G2 is declared (`declared`, the +/// compiled-in [`G2_FLIPPED`]) and every gating audit check is green /// ([`G2Checks::all_green`]). This is the mechanical block: any red check withholds the /// flip regardless of the declaration, so a flipped certificate can never out-run the audit /// that justifies it. This is the tested code path — the toggle tests below drive each of @@ -429,9 +429,9 @@ pub fn g2_flip_permitted(declared: bool, checks: &G2Checks) -> bool { declared && checks.all_green() } -/// The effective stratified trust profile UNDER the G2 gate: the proven (honestly scoped) +/// The effective stratified trust profile under the G2 gate: the proven, scoped /// form iff [`g2_flip_permitted`], else the conservative `UNPROVEN` rollout form. The -/// production / audit-surface emitter routes through HERE so a red check automatically +/// production / audit-surface emitter routes through here so a red check automatically /// downgrades the label (never an over-claim — REQ-9 / REQ-5 option B). #[must_use] pub fn strat_trust_profile_gated(declared: bool, checks: &G2Checks) -> Vec { @@ -581,7 +581,7 @@ mod tests { #[test] fn trust_profile_reads_unproven_before_g2_and_proven_after() { - // The pre-G2 (rollout) form: honest UNPROVEN reference-encoder string. + // The pre-G2 rollout form uses the UNPROVEN reference-encoder string. let before = strat_trust_profile(false); assert_eq!( before, @@ -605,9 +605,9 @@ mod tests { #[test] fn compiled_declaration_is_flipped_at_g2() { - // REQ-9 reached G2: `G2_FLIPPED` is now the DECLARATION (`true`). The - // declaration-level profile reads the proven (honestly scoped) form — but this is - // the DECLARATION only; the per-clause emit still routes through the gate + // REQ-9 reached G2: `G2_FLIPPED` is now the declaration (`true`). The + // declaration-level profile reads the proven, scoped form — but this is + // the declaration only; the per-clause emit still routes through the gate // ([`g2_flip_permitted`]), which a red check downgrades (see the toggle tests). // REQ-9 flips the G2 declaration on (checked at compile time). const _: () = assert!(G2_FLIPPED); @@ -636,7 +636,7 @@ mod tests { #[test] fn gate_blocks_the_flip_when_any_one_check_is_red() { - // AC-9: toggle EACH of the four red in turn (the others green) and assert the flip + // AC-9: toggle each of the four red in turn (the others green) and assert the flip // is mechanically withheld — the proven label is never emitted while a check is red, // even though the declaration is on. let labels = [ diff --git a/thermite-tv/tests/body_teeth.rs b/thermite-tv/tests/body_teeth.rs index 004b25359..e1f8b3c4c 100644 --- a/thermite-tv/tests/body_teeth.rs +++ b/thermite-tv/tests/body_teeth.rs @@ -1,4 +1,4 @@ -//! The R-CHAR-3 body-teeth-test (`.design/verified/exec-stmt-tv.md` REQ-3 / +//! The R-CHAR-3 body-TV negative test (`.design/verified/exec-stmt-tv.md` REQ-3 / //! AC-1..AC-4; epic crosslink #158, blockers #159/#160/#161). The proof that //! straight-line exec-body state-refinement TV (step 2.2.1) discriminates a faithful //! body lowering from an injected state-transformation infidelity — the class the @@ -17,7 +17,7 @@ //! - infidel -> be caught — a `postcondition not satisfied` counterexample (the //! final state differs from the reference state-denotation). //! -//! The state-threading is real (the load-bearing point): B2's reorder is caught +//! The state-threading is real (the required point): B2's reorder is caught //! because the final state differs. Each RHS (`s + 1`, `s * 2`) is value-faithful in //! isolation (step 2.1 would pass each), the order is the bug. The reference //! `body_ref_state` threads the substitution chain in order, so the reordered @@ -166,8 +166,7 @@ fn assert_faithful_verifies(fixture: &str, program: &str) { /// Discharge an infidel body obligation: TV must catch it as a `postcondition not /// satisfied` counterexample (the final state differs from the reference /// state-denotation). Skips with a logged note if verus is absent. The catch shape -/// is asserted precisely so the teeth bite for the right reason (R-CHAR-3 — a -/// spurious unrelated failure is not a pass). +/// is asserted to rule out unrelated failures (R-CHAR-3). fn assert_infidel_caught(fixture: &str, program: &str) { let tmp = std::env::temp_dir().join(format!("tv_body_teeth_{fixture}_infidel.rs")); std::fs::write(&tmp, program).unwrap_or_else(|e| panic!("write {fixture} infidel: {e}")); @@ -205,9 +204,9 @@ fn assert_infidel_caught(fixture: &str, program: &str) { } // ============================================================================ -// B1 straight-line — the let-chain teeth (a dropped statement) +// B1 straight-line — a dropped statement in a let chain // source body: { let a = x + 1; let b = a * 2; b } frame: x: u64, x <= 1000 -// faithful P_prod: the real lower_exec_body (pinned exec_body_tests::b1) +// faithful P_prod: the lower_exec_body (pinned exec_body_tests::b1) // infidel P_prod: drops `let b = a * 2`, returns `a` (final state x+1 != (x+1)*2) // reference (body_ref_state): ((x + 1) * 2) // ============================================================================ @@ -252,9 +251,9 @@ fn b1_dropped_statement_infidel_caught() { } // ============================================================================ -// B2 mutation-order — the state-sequencing teeth (the load-bearing fixture) +// B2 mutation order — reordered state updates // source body: { let mut s = x; s = s + 1; s = s * 2; s } frame: x: u64, x <= 1000 -// faithful P_prod: the real lower_exec_body (pinned exec_body_tests::b2) +// faithful P_prod: the lower_exec_body (pinned exec_body_tests::b2) // infidel P_prod: reordered `s = s * 2; s = s + 1` (final state (x*2)+1 != (x+1)*2) // reference (body_ref_state): ((x + 1) * 2) // Each RHS (s + 1, s * 2) is value-faithful in isolation; the order is the bug. @@ -307,9 +306,9 @@ fn b2_reordered_mutation_infidel_caught() { } // ============================================================================ -// B3 if-branch — the branch state-transformer teeth (a swapped branch) +// B3 if branch — a swapped state-transformer branch // source body: { if c { x + 1 } else { x - 1 } } frame: c: bool, x: u64, 1<=x<=1000 -// faithful P_prod: the real lower_exec_body (pinned exec_body_tests::b3) +// faithful P_prod: the lower_exec_body (pinned exec_body_tests::b3) // infidel P_prod: swapped branches `if c { x - 1 } else { x + 1 }` // reference (body_ref_state): if c { (x + 1) } else { (x - 1) } // ============================================================================ @@ -373,10 +372,10 @@ fn b3_swapped_branch_infidel_caught() { } // ============================================================================ -// B4 multi-cell tuple — the multi-cell projection teeth (the design's #1) +// B4 multi-cell tuple — a wrong tuple projection // source body: { let mut a = x; let mut b = y; a = a + 1; b = b + a; (a, b) } // frame: x: u64, y: u64, x <= 1000, y <= 1000 -// faithful P_prod: the real lower_exec_body (pinned exec_body_tests::b4) +// faithful P_prod: the lower_exec_body (pinned exec_body_tests::b4) // infidel P_prod: `b = b + x` (uses the old x, not the updated a) -> b cell wrong // reference (body_ref_state): ((x + 1), (y + (x + 1))) // ============================================================================ @@ -423,7 +422,7 @@ fn b4_wrong_cell_infidel_caught() { // The multi-cell projection infidelity: production threads `b = b + x` (the old // x) instead of `b = b + a` (the updated a) -> the b cell's final state is // `y + x`, != the reference `(y + (x + 1))`. The a cell is unchanged; only the - // b projection of the tuple is wrong, so the teeth bite on the tuple comparison. + // b projection of the tuple is wrong, so the tuple comparison fails. let prog = body_equivalence_obligation( &b4_body(), " let mut a = x;\n let mut b = y;\n a = a + 1;\n b = b + x;\n (a, b)\n", @@ -433,7 +432,7 @@ fn b4_wrong_cell_infidel_caught() { assert_infidel_caught("b4", &prog); } -// ---- body_ref_state unit checks (the reference output MEANS the final state) - +// ---- body_ref_state unit checks (the reference output means the final state) - /// The reference state-denotation of each source body must be the closed-form final /// state in the inputs (the obligation tests prove semantic equivalence under verus; diff --git a/thermite-tv/tests/divergence_ac4_if_stmt_mutation.rs b/thermite-tv/tests/divergence_ac4_if_stmt_mutation.rs index f00865097..82c9bb519 100644 --- a/thermite-tv/tests/divergence_ac4_if_stmt_mutation.rs +++ b/thermite-tv/tests/divergence_ac4_if_stmt_mutation.rs @@ -13,7 +13,7 @@ //! (`1 verified, 0 errors`). //! //! Toolchain: `thermite_tv::exec_stmt_encode::thread_stmt` (the `Stmt::If { .. }` -//! arm) returns `RefEncodeError::Unsupported("`if` as a non-tail STATEMENT ...")`, +//! arm) returns `RefEncodeError::Unsupported("`if` as a non-tail statement ...")`, //! so `body_ref_state` / `body_equivalence_obligation` cannot encode the AC-4 body //! and the obligation builder returns `Err`. The production side //! (`thermite_lower::lower_exec_body` → `lower_block_inner`'s `Stmt::If` arm) lowers diff --git a/thermite-tv/tests/divergence_index_combinator.rs b/thermite-tv/tests/divergence_index_combinator.rs index 970eef9c4..db9261f21 100644 --- a/thermite-tv/tests/divergence_index_combinator.rs +++ b/thermite-tv/tests/divergence_index_combinator.rs @@ -19,7 +19,7 @@ //! a Verus type error (`no method named view found for int`). A faithful clause //! `forall_below(xs, n as int, |x| x <= 7)` cannot even be validated by the //! reference side, so TV is blind on these two combinators (an `Unsupported` -//! would at least be honest; this is a silent wrong encoding, worse — R-CHAR-3 / +//! would at least report an error; this is a silent wrong encoding, worse — R-CHAR-3 / //! contract-tv.md REQ-1 "never a silent wrong encoding"). //! //! Expected value source: the registry `verus_l3` body of `forall_below` diff --git a/thermite-tv/tests/exec_teeth.rs b/thermite-tv/tests/exec_teeth.rs index 0a54c6379..ead59bc60 100644 --- a/thermite-tv/tests/exec_teeth.rs +++ b/thermite-tv/tests/exec_teeth.rs @@ -1,8 +1,8 @@ -//! The R-CHAR-3 exec-teeth-test (`.design/verified/exec-tv.md` REQ-4; epic +//! The R-CHAR-3 exec-TV negative test (`.design/verified/exec-tv.md` REQ-4; epic //! crosslink #151, blocker #155). The proof that exec-position TV discriminates a //! faithful body-expression lowering from an injected infidelity — the #122/#146 //! infidelity class in its general home (the exec body), plus the wrong-op/overflow -//! and off-by-one classes the contract-position teeth cannot reach. +//! and off-by-one classes that contract-position tests cannot reach. //! //! For each orchestrator-authored fixture (E1–E4) the test builds the exec-fn //! equivalence obligation twice — once with the faithful `P_production` (the exact @@ -63,7 +63,7 @@ fn cast(inner: Expr, ty: Type) -> Expr { } } -// ---- verus resolution + discharge (mirrors teeth.rs) ----------------------- +// ---- verus resolution + discharge (mirrors tests/teeth.rs) ----------------- fn verus_bin() -> Option { if let Ok(p) = std::env::var("VERUS_BIN") { @@ -170,8 +170,8 @@ enum CatchShape { } /// Discharge an infidel obligation: TV must catch it. Skips with a logged note if -/// verus is absent. The catch shape is asserted precisely so the teeth bite for the -/// right reason (R-CHAR-3 — a spurious unrelated failure is not a pass). +/// verus is absent. The asserted catch shape rules out unrelated failures +/// (R-CHAR-3). fn assert_infidel_caught(fixture: &str, program: &str, expect: CatchShape) { let tmp = std::env::temp_dir().join(format!("tv_exec_teeth_{fixture}_infidel.rs")); std::fs::write(&tmp, program).unwrap_or_else(|e| panic!("write {fixture} infidel: {e}")); @@ -226,7 +226,7 @@ fn assert_infidel_caught(fixture: &str, program: &str, expect: CatchShape) { } // ============================================================================ -// E1 — cast-paren (#122): the inner-paren teeth +// E1 — cast-paren (#122): missing inner parentheses // source exec expr: (n - 1) as u8 req frame: n: u64, n >= 1 (+ n - 1 <= 255 // so the narrowing cast is well-defined — a bound on the expr, not the bug) // faithful P_prod: (n - 1) as u8 (pinned in thermite-lower exec_expr_tests) @@ -270,7 +270,7 @@ fn e1_cast_paren_infidel_caught() { } // ============================================================================ -// E2 — cast-`<` (#146): the outer-paren teeth +// E2 — cast-`<` (#146): missing outer parentheses // source exec expr: x as u32 < 33 req frame: x: u64 // faithful P_prod: (x as u32) < 33 (pinned in thermite-lower exec_expr_tests) // infidel P_prod: x as u32 < 33 (the `u32 <` mis-parses as a generic list) @@ -312,7 +312,7 @@ fn e2_cast_lt_infidel_caught() { } // ============================================================================ -// E3 — wrong-op / overflow: the bounded-value teeth (the EXEC-value semantics) +// E3 — wrong operator or overflow under bounded exec-value semantics // source exec expr: a + b req frame: a: u64, b: u64, a + b <= 0xFFFF (no overflow) // faithful P_prod: a + b (pinned in thermite-lower exec_expr_tests) // infidel P_prod: a.wrapping_sub(b) (wrong op → value differs) @@ -357,7 +357,7 @@ fn e3_wrong_op_infidel_caught() { } // ============================================================================ -// E4 — off-by-one index: the bounded-access teeth +// E4 — off-by-one index under bounded-access semantics // source exec expr: xs[i] req frame: xs: &[u32], i: usize, i < xs.len() // faithful P_prod: xs[i] (pinned in thermite-lower exec_expr_tests) // infidel P_prod: xs[i + 1] (off-by-one → value differs / OOB) @@ -403,7 +403,7 @@ fn e4_index_infidel_caught() { assert_infidel_caught("e4", &prog, CatchShape::Postcondition); } -// ---- exec_ref_value unit checks (the reference output MEANS the faithful col) - +// ---- exec_ref_value unit checks (the reference output means the faithful col) - /// The exec reference encoding of each source must be a string that means the /// faithful production column at the bounded type (the obligation tests prove @@ -437,7 +437,7 @@ fn exec_ref_value_matches_faithful_meaning() { ); } -/// An out-of-scope construct (a method call / Vec-String accessor) is an honest +/// An out-of-scope construct (a method call / Vec-String accessor) is an /// `Err`, never a panic / silent wrong encoding (REQ-1 / R-CODE-2). This is the /// #154/#156 territory the step-2.1 encoder refuses. #[test] diff --git a/thermite-tv/tests/loop_teeth.rs b/thermite-tv/tests/loop_teeth.rs index 44499c0e8..d0cefe88f 100644 --- a/thermite-tv/tests/loop_teeth.rs +++ b/thermite-tv/tests/loop_teeth.rs @@ -1,4 +1,4 @@ -//! The R-CHAR-3 loop-teeth-test (`.design/verified/loop-tv.md` REQ-2 / AC-1..AC-4; +//! The R-CHAR-3 loop-TV negative test (`.design/verified/loop-tv.md` REQ-2 / AC-1..AC-4; //! epic crosslink #169, blocker #163). The proof that the v1 frozen-subset `while` //! loop TV (step 2.2.2-i) discriminates a faithful loop lowering from an injected //! per-iteration / after-loop infidelity, via the three per-run obligations: @@ -13,16 +13,16 @@ //! - L2 (broken-preservation mutant) — a production loop body that mutates a cell //! the invariant constrains in a way that breaks the per-iteration step (`lo + 2` //! for source `lo + 1`) fails the preservation obligation with `postcondition not -//! satisfied` (the `body_ref_sound` per-iteration teeth, AC-2/AC-5). +//! satisfied` (the `body_ref_sound` per-iteration check, AC-2/AC-5). //! - L3 (wrong-after-loop-state mutant) — a production after-loop characterization //! that over-claims (stronger than `inv ∧ ¬cond` — claims `lo > hi` when only //! `lo == hi` follows) fails the exit obligation (a counterexample, AC-3). //! - L4 (loop-without-usable-inv / out-of-v1) — a `loop`-kind, a `break` body, a -//! mid-body `return`, and a trivially-weak `inv true` are each an honest +//! mid-body `return`, and a trivially-weak `inv true` are each an //! `RefEncodeError::Unsupported` (Skipped, never silently Faithful — AC-4 / //! R-HONEST-3). No verus needed (the obligation builder refuses to emit). //! -//! The teeth are real (the load-bearing point, R-CHAR-3): the L2 mutant's per- +//! The tests exercise both failure modes (R-CHAR-3): the L2 mutant's per- //! iteration state differs from the reference `body_ref_state` step, so the //! preservation `ensures result.i == ` is provably violated; the L3 mutant's //! over-claim is provably not implied by `inv ∧ ¬cond`. Expected verdicts are derived @@ -177,8 +177,8 @@ fn assert_obligation_verifies(fixture: &str, program: &str) { } /// Discharge an infidel loop obligation: TV must catch it. `expect_msg` is the precise -/// catch shape (so the teeth bite for the right reason, R-CHAR-3). Skips with a logged -/// note if verus absent. +/// catch shape rules out unrelated failures (R-CHAR-3). Skips with a logged note if +/// verus is absent. fn assert_obligation_caught(fixture: &str, program: &str, expect_msg: &str) { let tmp = std::env::temp_dir().join(format!("tv_loop_teeth_{fixture}.rs")); std::fs::write(&tmp, program).unwrap_or_else(|e| panic!("write {fixture}: {e}")); @@ -322,8 +322,8 @@ fn l2_broken_preservation_caught() { // The per-iteration infidelity: production steps `lo = lo + 2` (the source step is // `lo + 1`). The reference step_cells.1 = `(lo + 1)`, so production's returned // `lo + 2 != lo + 1` → the body-TV `ensures result.1 == (lo + 1)` is provably - // violated (a `postcondition not satisfied` — the same teeth `body_ref_sound`'s - // per-iteration negative lemmas bite). This is the AC-5 reuse: the single-iteration + // violated (a `postcondition not satisfied`, as covered by `body_ref_sound`'s + // per-iteration negative lemmas). This is the AC-5 reuse: the single-iteration // step is the shipped body_ref_state, and a wrong per-iteration mutation breaks the // preservation obligation, not a silent pass. let prog = loop_preservation_obligation( @@ -344,7 +344,7 @@ fn l2_broken_preservation_caught() { #[test] fn l3_wrong_exit_characterization_caught() { // The after-loop over-claim: production characterizes the exit state as `lo > n`, - // stronger than the genuine `inv ∧ ¬cond` (which gives only `lo == n`). From + // stronger than the `inv ∧ ¬cond` (which gives only `lo == n`). From // `lo <= n && !(lo < n)` the claim `lo > n` is false (we have `lo <= n`), so the // exit assertion fails with a counterexample. A wrong after-loop characterization // is caught, never silently accepted. @@ -353,11 +353,11 @@ fn l3_wrong_exit_characterization_caught() { assert_obligation_caught("l3_wrong_exit", &prog, "assertion failed"); } -// ---- L4: out-of-v1 loops → honest Skipped (Unsupported), never Faithful ---- +// ---- L4: out-of-v1 loops → Skipped (Unsupported), never Faithful ----------- // -// Each out-of-v1 form makes the obligation builder refuse to emit (an honest +// Each out-of-v1 form makes the obligation builder refuse to emit (an // `RefEncodeError::Unsupported`), not a silent wrong encoding. No verus needed: -// the refusal is the honest Skip (`loop-tv.md` AC-4 / R-HONEST-3). +// the refusal is the Skip (`loop-tv.md` AC-4 / R-HONEST-3). fn skipped_block(loop_node: LoopNode) -> Block { Block { @@ -389,7 +389,7 @@ fn l4_loop_kind_is_skipped() { loop_ref_obligations(&block, &BodyRefCtx::default()), Err(RefEncodeError::Unsupported(_)) )); - // The obligation emitters propagate the honest Skip too (never silently Faithful). + // The obligation emitters propagate the Skip too (never silently Faithful). assert!(matches!( loop_entry_obligation(&block, &l1_frame()), Err(RefEncodeError::Unsupported(_)) diff --git a/thermite-tv/tests/strat_probe.rs b/thermite-tv/tests/strat_probe.rs index 076a9e7dd..64a6efd98 100644 --- a/thermite-tv/tests/strat_probe.rs +++ b/thermite-tv/tests/strat_probe.rs @@ -216,7 +216,7 @@ fn hit_rates_match_the_readme_record() { assert_eq!(ct, 4, "corpus-only n must be 4 (AC-7 small-n number)"); - // The two rate lines, formatted exactly as the README writes them. + // The two rate lines, formatted as the README writes them. let corpus_line = format!( "(n={ct}, **small-n**, NOT threshold-bearing on its own): **{ch}/{ct} = {:.1}%**", pct(ch, ct) diff --git a/thermite-tv/tests/strat_two_phase_corpus.rs b/thermite-tv/tests/strat_two_phase_corpus.rs index 532c87c15..6c219f4f4 100644 --- a/thermite-tv/tests/strat_two_phase_corpus.rs +++ b/thermite-tv/tests/strat_two_phase_corpus.rs @@ -9,7 +9,7 @@ //! reference-encoded ([`thermite_tv::strat_ref_encode`]). The recursive combinators //! (`count_where`/`permutation_of`) have no raw-quantifier spelling, so they are routed //! [`ClauseRoute::DirectSemantic`] and land in the semantic phase (REQ-6 / §8.2). A -//! deliberately-divergent pair and a timeout pair pin the WITHHELD / DIVERGENT buckets. +//! divergent pair and a timeout pair pin the WITHHELD / DIVERGENT buckets. use std::fs; use std::path::PathBuf; @@ -122,7 +122,7 @@ fn generated_clauses_flow_through_the_sweep() { assert!(!clauses.is_empty(), "the generator produced clauses"); // Inject the two recursive combinators (no raw-quantifier spelling → direct semantic) - // and a deliberately-divergent + a slow (timeout) pair, to exercise every bucket. + // and a divergent + a slow (timeout) pair, to exercise every bucket. let p = normalize::parse("forall i . i < len(xs)").unwrap(); let r_same = p.clone(); let r_diff = normalize::parse("forall i . i < len(ys)").unwrap(); diff --git a/thermite-tv/tests/teeth.rs b/thermite-tv/tests/teeth.rs index bd7aa80a8..9eba29065 100644 --- a/thermite-tv/tests/teeth.rs +++ b/thermite-tv/tests/teeth.rs @@ -1,4 +1,4 @@ -//! The R-CHAR-3 teeth-test (`.design/verified/contract-tv.md` REQ-4; epic +//! The R-CHAR-3 contract-TV negative test (`.design/verified/contract-tv.md` REQ-4; epic //! crosslink #143). The proof that contract-faithfulness TV discriminates a //! faithful lowering from an injected infidelity — the bug class the five //! existing layers (verus-on-emitted, the cert oracle, the vacuity/mutation @@ -184,7 +184,7 @@ fn assert_infidel_caught(fixture: &str, program: &str) { } // ============================================================================ -// F1 — comparison: the ==-vs-<= teeth (the canonical infidelity case) +// F1 — comparison: `==` encoded as `<=` // source clause: result == spec_sum(xs) // faithful P_prod: result as nat == spec_sum(xs) (xs bound as Seq, so the // slice→@ view is the identity; the doc AC-1 grounded form @@ -211,7 +211,7 @@ fn f1_frame() -> ObligationFrame { nat_coerce_params: vec!["result".to_string()], // #150 mechanical ripple: the additive `string_params`/`map_params` fields // (the String/Map byte-view receiver dispatch) are empty for F1 (no - // String/Map receiver in the comparison teeth). + // String/Map receiver in this comparison case). ..Default::default() } } @@ -240,7 +240,7 @@ fn f1_comparison_infidel_caught() { } // ============================================================================ -// F2 — combinator: the wrong-predicate teeth +// F2 — combinator with the wrong predicate // source clause: forall_in(xs, |x| x < 10) // faithful P_prod: the forall_in verus_l3 body applied with |x| x < 10 // infidel P_prod: same but |x| x <= 10 @@ -289,7 +289,7 @@ fn f2_combinator_infidel_caught() { } // ============================================================================ -// F3 — byteview (#127): the wrong-index teeth (receiver-shape dispatch) +// F3 — byteview (#127) with the wrong index (receiver-shape dispatch) // source clause: s.byte_at(0) == 65 // faithful P_prod: s@[0] == 65 (s bound as Seq → s@ == s, index 0 correct) // infidel P_prod: s@[1] == 65 (wrong index — the #127 misdispatch class) @@ -336,7 +336,7 @@ fn f3_byteview_infidel_caught() { } // ============================================================================ -// F4 — structural-drop: the dropped-conjunct teeth +// F4 — structural drop of a conjunct // source clause: a == b && c == d // faithful P_prod: a == b && c == d // infidel P_prod: a == b (a conjunct silently dropped) @@ -383,7 +383,7 @@ fn f4_structural_drop_infidel_caught() { assert_infidel_caught("f4", &prog); } -// ---- ref_encode unit checks (the reference output MEANS the faithful column) - +// ---- ref_encode unit checks (the reference output means the faithful column) - /// The reference encoding of each source must be a string that means the /// faithful production column (the obligation tests prove semantic equivalence @@ -419,7 +419,7 @@ fn ref_encode_matches_faithful_meaning() { ); } -/// An unsupported construct is an honest `Err`, never a panic / silent wrong +/// An unsupported construct is an `Err`, never a panic / silent wrong /// encoding (REQ-1 / R-CODE-2). A `match` in spec position is outside the frozen /// sublanguage. #[test] diff --git a/thermite-verified/src/lib.rs b/thermite-verified/src/lib.rs index 49c09a953..cf70ea43d 100644 --- a/thermite-verified/src/lib.rs +++ b/thermite-verified/src/lib.rs @@ -12,7 +12,7 @@ //! decision is ported to a bounded **9-atom `u16` bitset** (Read=0 .. Term=8, //! the path-insensitive atom-kind projection `EffectKind::of` already computes in //! `thermite-lower`), where subsumption is the mask test `(callee & !caller) == 0` -//! and the genuine subset relation `effects(callee) ⊆ effects(caller)` is the +//! and the subset relation `effects(callee) ⊆ effects(caller)` is the //! explicit 9-way conjunction over the bit positions. The two are proved //! equivalent by Verus `bit_vector`-mode SMT. (The proved bitset widened from //! `u8` to `u16` for the 9th atom `Term` — the §4.1 terminal-control effect, @@ -119,7 +119,7 @@ pub fn subsumes_masks(caller: u16, callee: u16) -> bool { missing == 0 } -/// The genuine subset relation `effects(callee) ⊆ effects(caller)` over the +/// The subset relation `effects(callee) ⊆ effects(caller)` over the /// 9-atom `u16` bitset, as the explicit per-atom conjunction (REQ-4 — the /// non-trivial contract `subsumes_masks` is proved to compute). For each atom /// position `i`, if `callee` has atom `i` then `caller` must have it. This is the @@ -156,7 +156,7 @@ pub enum L3Tag { Proved, /// verus timed out (inconclusive) → the sole degrade trigger → attempt L2. Timeout, - /// verus disproved the item (a real bug) → hard fail, never a degrade. + /// verus disproved the item (a bug) → hard fail, never a degrade. Counterexample, } @@ -167,7 +167,7 @@ pub enum L2Tag { Verified, /// kani exhausted its bound (inconclusive) → degrade to L1. UnderBound, - /// kani disproved the contract (a real bug) → hard fail, never a drop to L1. + /// kani disproved the contract (a bug) → hard fail, never a drop to L1. Counterexample, } @@ -436,7 +436,7 @@ mod verus_core { (mask & (1u16 << i)) != 0 } - /// The genuine subset relation `effects(callee) ⊆ effects(caller)`, as the + /// The subset relation `effects(callee) ⊆ effects(caller)`, as the /// explicit 9-way conjunction over the atom positions (mirrors the plain-Rust /// `spec_subsumes_mask`). Non-vacuous (REQ-4): false when callee has an atom /// caller lacks. Bit 8 is the #106 terminal-control atom `Term`. @@ -523,12 +523,12 @@ mod verus_core { } } - /// `true` iff the L3 verdict is a counterexample (verus disproved — a real bug). + /// `true` iff the L3 verdict is a counterexample (verus disproved — a bug). pub open spec fn l3_is_counterexample(v: L3Tag) -> bool { match v { L3Tag::Counterexample => true, _ => false } } - /// `true` iff the L2 verdict is a counterexample (kani disproved — a real bug). + /// `true` iff the L2 verdict is a counterexample (kani disproved — a bug). pub open spec fn l2_is_counterexample(v: L2Tag) -> bool { match v { L2Tag::Counterexample => true, _ => false } } diff --git a/tooling/anti-pattern-gate.py b/tooling/anti-pattern-gate.py index 086357712..0c017d3d2 100755 --- a/tooling/anti-pattern-gate.py +++ b/tooling/anti-pattern-gate.py @@ -1,39 +1,16 @@ #!/usr/bin/env python3 -""" -anti-pattern-gate hook (production-code discipline for the Thermite -toolchain). - -Deterministic PreToolUse gate on Write|Edit to gated source files that -rejects the lazy escape hatches subagents reach for when they're stuck: - - - the to-do macro - stub left behind - - the unimpl macro - same - - the unreach macro - should be a typed enum - - .expect on Result - production code shouldn't unwrap - - .unwrap on Result - same - - the panic macro - propagate via Result instead - - Arc> - escape hatch from ownership design - - Rc> - same (single-threaded) - - module-root #![allow] - root-level lint silencing - -Each forbidden pattern carries the architectural alternative, a pointer -to the goal.md rule, and the priority footer about injected instructions. - -Exemptions: anything inside `#[cfg(test)]` blocks is permitted (for Write; -Edit patches are always gated since we can't see surrounding context). - -For Write: scans the full content. -For Edit: scans the new_string ONLY (the patch being added). - -NOTE on Thermite-specific cheats: proof-dodging patterns (emitting -`assume(false)`, `#[verifier::external]`, or `#[slag]` to dodge a real -proof obligation) are forbidden by goal.md R-DEFER-9 but are NOT regex- -gated here, because they are legitimate in generated Verus output and in -genuine slag blocks — the acto-critic enforces R-DEFER-9 adversarially. - -PROJECT CUSTOMIZATION: - - Edit the PATTERNS list to match your target language's footguns. - - Edit the path-predicate (TARGET_*) to match your gated tree. +"""Reject selected production-code anti-patterns before a Write or Edit. + +The hook checks configured Rust source paths for runtime stubs, panics, +``unwrap``/``expect``, ownership escape hatches, and module-level lint +suppression. Each finding explains the preferred alternative. + +Full Write content is scanned, excluding ``#[cfg(test)]`` blocks. Edit requests +scan only the replacement text because surrounding context is unavailable. +Proof-specific constructs are reviewed elsewhere because they also have valid +uses in generated or explicitly unverified code. + +Customize ``PATTERNS`` and the ``TARGET_*`` path settings below. """ import json @@ -43,18 +20,14 @@ from pathlib import Path -# ===================================================================== -# PROJECT CUSTOMIZATION — edit these for your project -# ===================================================================== +# Project settings TARGET_CRATE_PREFIXES = ("thermite-",) TARGET_CRATE_EXACT = ("forge",) EXCLUDED_CRATES = ("thermite-test-utils",) TARGET_EXTENSION = ".rs" -# ===================================================================== -# Forbidden-pattern catalogue — adapt to your target language -# ===================================================================== +# Forbidden patterns # Each entry: (regex, name, why-forbidden, architectural-alternative) PATTERNS = [ diff --git a/tooling/control-plane-check.py b/tooling/control-plane-check.py index be1e8aa6e..46eb3f73c 100755 --- a/tooling/control-plane-check.py +++ b/tooling/control-plane-check.py @@ -1,72 +1,21 @@ #!/usr/bin/env python3 -""" -control-plane gate — the gate that guards the gates. - -`tooling/spec-discipline.py` and `tooling/anti-pattern-gate.py` are agent-facing -PreToolUse/PostToolUse hooks. They only ever fire because `.claude/settings.json` -WIRES them. That wiring is a single tracked JSON file that `crosslink init` -regenerates from a generic template — so a routine `crosslink init` (or a -`--force` re-init) silently drops the project-specific entries and leaves the -crosslink-generic ones. That is exactly what commit 5581b65f did on 2026-06-21: -both gates were dormant for the whole Stage-3 arc while README.md:172, goal.md -§Spec-discipline, and all four `.claude/agents/acto-*.md` files kept asserting -"they enforce automatically — no setup" (crosslink #93). - -Nothing could catch it: `doc-drift.py` only checks files reachable from -`tooling/spec-routes.toml`, and no route covered the control plane. The design -layer governed everything except the file that decides whether the governance -runs. - -This gate closes that loop. It asserts — as a CI-enforced, deterministic check — -that every hook this project's documentation CLAIMS is live is actually wired in -the tracked `settings.json`, and that the script each entry names exists on disk. -An "asserted enforcement that isn't" becomes a red build instead of a quiet -regression (the R-HONEST-3 move: a gate that fails open is a silent pass). - -The rule, precisely (governed by .design/tooling/control-plane.md): - - 1. Load the tracked `.claude/settings.json` (REQ-1). It must exist and parse: - Claude Code loads NO hooks from a malformed settings file, so unparseable - is gate-dead, i.e. a FINDING (exit 1), never INCONCLUSIVE. - 2. For each required wiring (event, tools, script) in REQUIRED_HOOKS below, - find a hook entry under that event whose `matcher` COVERS every required - tool and one of whose commands names `script` (REQ-2). Matcher coverage is - alternative-set containment (`Write|Edit|Bash` covers a `Write|Edit` - requirement), so a superset matcher passes and a reordering does not - false-fail — but a matcher that drops a required tool is a MISSING-WIRING. - 3. Assert each required script exists at its repo-relative path (REQ-3). A - wired-but-absent hook is the same dead gate as an unwired one: every - command is `if [ -f "$HOOK" ]`-guarded, so an absent script degrades to a - silent no-op exit 0. - 4. Report (REQ-4) deterministically in REQUIRED_HOOKS order, and exit per the - REQ-5 contract: - 0 = every required hook wired and present; - 1 = at least one MISSING-WIRING / MISSING-SCRIPT / UNPARSEABLE; - 3 = the gate could not determine the answer (no git / not a repo) — - the audit's INCONCLUSIVE precedent (scripts/audit.sh, doc-drift.py - REQ-9). An environment failure is never collapsed to "all wired". - -A finding prints the exact JSON entry to restore, so the fix is a paste, not an -archaeology dig through `git show 5581b65f`. - -NOT a Claude-Code hook: invoked by CI and by `make control-plane`. Deliberately -NOT part of `make audit` — hook wiring is a development-discipline invariant, -not a link in the proof-trust chain (the doc-drift decision-5 precedent). - -Usage: python3 tooling/control-plane-check.py [--root ] - - --root the repo to check (default: the git toplevel of the cwd). The - production invocation is flagless; --root keeps the fixture tests - hermetic. - -See: - .design/tooling/control-plane.md (the governing doc — REQ-1..REQ-5) - goal.md (authority chain; R-CODE-4/5, R-HONEST-3) - tooling/doc-drift.py (the sibling gate; exit-3 precedent) - tooling/spec-routes.toml (routes the control plane under doc-drift) - -PROJECT CUSTOMIZATION: - Edit SETTINGS_RELPATH, REQUIRED_HOOKS below. +"""Verify the hook wiring declared by this repository. + +The check reads ``.claude/settings.json`` and confirms that every entry in +``REQUIRED_HOOKS`` has a covering matcher and an existing script. A missing or +malformed setting is a finding because Claude Code cannot run that hook. + +Exit codes: + +* 0: every required hook is wired and present +* 1: missing wiring, a missing script, or invalid settings +* 3: the repository could not be inspected + +Usage: ``python3 tooling/control-plane-check.py [--root ]`` + +See ``.design/tooling/control-plane.md`` for the requirements. The check runs in +CI and through ``make control-plane``; it is not itself a hook or part of +``make audit``. Customize ``SETTINGS_RELPATH`` and ``REQUIRED_HOOKS`` below. """ import json @@ -76,16 +25,14 @@ from pathlib import Path -# ===================================================================== -# PROJECT CUSTOMIZATION — edit these constants for your project -# ===================================================================== +# Project settings # Repo-relative path to the tracked Claude Code settings file (the subject). SETTINGS_RELPATH = ".claude/settings.json" # The wirings this project's docs claim are live. Each entry is: # event — the settings.json hook event key -# tools — the tool names the matcher MUST cover +# tools — the tool names the matcher must cover # script — the repo-relative hook script the entry must invoke # claim — the doc line asserting this hook enforces (named in the report, # so a finding points at the prose that would go false) @@ -110,9 +57,7 @@ }, ) -# ===================================================================== -# Implementation — generally no edits needed below this line -# ===================================================================== +# Implementation # Defect classes (REQ-4). The literal tokens the report emits and the oracle # asserts. @@ -134,11 +79,9 @@ class EnvironmentError3(Exception): - """The gate could not determine the answer — maps to exit 3 (REQ-5). + """The check could not determine the answer; maps to exit 3 (REQ-5). - Raised (never a traceback to the user) for: git absent / not a repo. A CI - gate that fails open is a silent pass, so these are INCONCLUSIVE, never - "every hook is wired". + This covers a missing git executable or an invalid repository. """ @@ -177,11 +120,10 @@ def _matcher_covers(matcher, tools): def _entry_commands(entry): - """Every command string in a settings.json hook entry, shape-tolerantly. + """Return the command strings in a settings.json hook entry. - A wrong-shaped entry yields no commands rather than raising: the wiring it - was supposed to provide then reads as MISSING (a loud finding), which is the - honest answer for a settings file Claude Code could not act on either. + A malformed entry yields no commands and is therefore reported as missing + wiring. """ if not isinstance(entry, dict): return [] diff --git a/tooling/doc-drift.py b/tooling/doc-drift.py index 90111acea..39b167f42 100644 --- a/tooling/doc-drift.py +++ b/tooling/doc-drift.py @@ -1,65 +1,22 @@ #!/usr/bin/env python3 -""" -doc-drift tripwire — pinned-SHA freshness for every routed design doc. - -The .design/ docs are the per-component contracts (goal.md authority chain), -and spec-discipline.py guarantees they EXIST and are READ before a routed -edit — but nothing checks their CONTENT is still true of the code. They drift -silently. This gate converts that staleness from a silent failure into a loud, -gated one (the same move #[slag] makes for unverified code, thermite-design.md -§8): every routed design doc pins an `audited-content-sha256:` digest over its -governed files (or, for legacy docs, an `audited-sha:` commit), and this gate -FAILS whenever those governed file contents change. Legacy commit pins use a -full-history commit-set predicate so merge-parent order cannot hide drift. - -The rule, precisely (governed by .design/tooling/doc-drift-tripwire.md): - - 1. Enumerate the routed docs: the deduplicated `design` fields of every - [[route]] in tooling/spec-routes.toml, each inverted to its governed file - set = the union of that doc's routes' crate_patterns (REQ-1, REQ-6b). - 2. Extract each doc's pin from the doc's HTML-comment header (REQ-5): prefer - `audited-content-sha256:` (64-hex aggregate content digest) when present; - otherwise use the legacy first `audited-sha:` 40-hex commit pin. - 3. Validate a legacy commit pin: it must resolve to a commit - (`git rev-parse --verify

^{commit}`) AND be an ancestor of HEAD - (`git merge-base --is-ancestor

HEAD`) — else INVALID-PIN (REQ-6d, 8). - 4. Drift predicate: for content pins, recompute the governed-file aggregate - SHA-256 and compare it to the pin. For legacy commit pins (commit-set, - never commit-date — decision 2), a governed file f has drifted iff - `git log --full-history --format=%H

..HEAD -- ` is non-empty. - Literal paths use pathspec ``; glob patterns use `:(glob)` (REQ-6e). - A file with no commits in

..HEAD — including a file never committed at - all — is CURRENT, not drift (REQ-6 unbuilt-file rule). - 5. Report (REQ-7) deterministically sorted by doc path, then file path - (R-CODE-5), and exit per the REQ-9 contract: - 0 = every routed doc pinned and current; - 1 = at least one DRIFT / MISSING-PIN / INVALID-PIN; - 3 = the gate could not determine the answer (no git / not a repo / - tomllib absent / spec-routes.toml unreadable) — the audit's - INCONCLUSIVE precedent (scripts/audit.sh, REQ-3/REQ-9). A CI gate - that fails open is a silent pass, so an environment failure is - never collapsed to "no drift" (R-HONEST-3, R-CODE-4). - -NOT a Claude-Code hook (decision 5): invoked directly by CI, via -`make doc-drift`'s synthetic merge-ref worktree, or standalone. NOT part of -`make audit` — doc freshness is a development-discipline invariant, not a link -in the proof-trust chain. scripts/audit.sh is untouched by this component -(AC-7). - -Usage: python3 tooling/doc-drift.py [--root ] - - --root the repo to check (default: the git toplevel of the cwd). The - production invocation is flagless; --root keeps the fixture tests - hermetic. - -See: - .design/tooling/doc-drift-tripwire.md (the governing doc — REQ-5..REQ-11) - goal.md (authority chain; R-CODE-4/5, R-HONEST-3) - tooling/spec-routes.toml (the route table — single source of truth) - scripts/audit.sh (the exit-3 INCONCLUSIVE precedent) - -PROJECT CUSTOMIZATION: - Edit ROUTES_RELPATH, PIN_FIELD_RE below. +"""Check whether routed design documents are current with their source files. + +Routes come from ``tooling/spec-routes.toml``. Each routed document must contain +either an ``audited-content-sha256`` pin or a legacy ``audited-sha`` commit pin. +Content pins are compared with a digest of the governed files. Legacy pins are +checked against commits that touched those files after the pinned commit. + +Results are sorted by document and file: + +* 0: every document is pinned and current +* 1: drift, a missing pin, or an invalid pin was found +* 3: the check could not run reliably + +Usage: ``python3 tooling/doc-drift.py [--root ]`` + +The detailed rules are in ``.design/tooling/doc-drift-tripwire.md``. +This is a standalone/CI check, not a Claude Code hook or part of ``make audit``. +Customize ``ROUTES_RELPATH`` and the pin expressions below when adapting it. """ import hashlib @@ -74,9 +31,7 @@ tomllib = None -# ===================================================================== -# PROJECT CUSTOMIZATION — edit these constants for your project -# ===================================================================== +# Project settings # Repo-relative path to the route table (the enumeration source, REQ-1). ROUTES_RELPATH = "tooling/spec-routes.toml" @@ -90,14 +45,11 @@ ) CONTENT_PIN_ANY_RE = re.compile(r"^audited-content-sha256:\s*(\S+)", re.MULTILINE) -# Legacy commit pin, per REQ-5: the FIRST line matching this in a doc's header. -# Full 40-hex (never the 8-hex short form) so a pin can never go ambiguous as -# the repo grows. +# Legacy commit pin, per REQ-5: the first matching line in a doc's header. +# Use all 40 hex digits to avoid ambiguity as the repository grows. PIN_FIELD_RE = re.compile(r"^audited-sha:\s*([0-9a-f]{40})\b", re.MULTILINE) -# ===================================================================== -# Implementation — generally no edits needed below this line -# ===================================================================== +# Implementation # Defect classes (REQ-7/REQ-8). The literal tokens the report emits and the # oracle asserts. @@ -113,11 +65,10 @@ class EnvironmentError3(Exception): - """The gate could not determine the answer — maps to exit 3 (REQ-9). + """The check could not determine the answer; maps to exit 3 (REQ-9). - Raised (never a traceback to the user) for: git absent / not a repo / - tomllib absent / spec-routes.toml unreadable. A CI gate that fails open is - a silent pass, so these are INCONCLUSIVE, never "no drift". + This covers missing git, an invalid repository, missing tomllib, and an + unreadable route table. """ @@ -126,10 +77,8 @@ class EnvironmentError3(Exception): def _run_git(root, args): """Run `git ` in `root`; return (returncode, stdout, stderr). - Raises EnvironmentError3 iff git itself cannot be invoked (absent from - PATH / not executable) — an environment failure, exit 3. A non-zero - returncode from git that DOES run is returned to the caller to interpret; - it is never an environment failure on its own. + Raise EnvironmentError3 when git cannot be invoked. Otherwise return git's + status to the caller for interpretation. """ try: proc = subprocess.run( @@ -215,8 +164,8 @@ def _intervening_commits(root, pin, pathspec): def load_doc_files(root): """Invert the route table to doc -> sorted(set(governed file patterns)). - REQ-1 / REQ-6b. Raises EnvironmentError3 on an unreadable / unparseable - route table or absent tomllib (a gate that fails open is a silent pass). + Raise EnvironmentError3 when tomllib is unavailable or the route table + cannot be read and validated (REQ-1, REQ-6b, REQ-9). """ if tomllib is None: raise EnvironmentError3("tomllib is unavailable (Python < 3.11)") @@ -231,13 +180,9 @@ def load_doc_files(root): f"route table unreadable ({ROUTES_RELPATH}): {exc}" ) from exc - # Shape validation (REQ-9): a table that PARSES as TOML can still be the - # wrong SHAPE (e.g. `route = 5`, or `route = ["a"]`). Iterating such a value - # would raise a bare TypeError/AttributeError -> unhandled traceback, exit 1 - # (the DRIFT class). A malformed enumeration source is an ENVIRONMENT failure - # (the "spec-routes.toml unreadable" case), so it is INCONCLUSIVE (exit 3), - # never a drift finding and never a traceback (R-HONEST-3: a gate that fails - # open is a silent pass). Validate before the loop; name the defect loudly. + # TOML syntax alone does not validate the route-table shape. Check it here + # so malformed input is reported as inconclusive instead of raising while + # the entries are traversed (REQ-9). routes = data.get("route", []) if not isinstance(routes, list): raise EnvironmentError3( @@ -254,16 +199,9 @@ def load_doc_files(root): ) design = route.get("design") pattern = route.get("crate_pattern") - # Both fields are "# required" per the spec-routes.toml schema header, - # so an entry missing either (a None from an absent / typo'd key) or - # carrying an empty string is WRONG-SHAPED. The builder-era - # `if not design or not pattern: continue` silently DROPPED such an - # entry, shrinking the deduplicated-design checked set (REQ-1) while the - # gate could still exit 0 — a one-doc fail-open (a typo'd `crate_patern =` - # quietly removes a doc from coverage). Per REQ-9 / R-HONEST-3 a - # malformed enumeration source is INCONCLUSIVE (exit 3), never a drift - # finding and never a silent pass: route it through EnvironmentError3, - # naming the defective entry and the missing/empty field (#261). + # Both fields are required by the spec-routes.toml schema. Reject absent, + # empty, or non-string values so a malformed route cannot reduce coverage + # while the check still succeeds (#261). for field, value in (("design", design), ("crate_pattern", pattern)): if value is not None and not isinstance(value, str): raise EnvironmentError3( @@ -280,12 +218,8 @@ def load_doc_files(root): ) doc_files.setdefault(design, set()).add(pattern) if not doc_files: - # REQ-9 / R-HONEST-3: the route table is the enumeration source, and an - # empty one means there is NOTHING to check — not "every routed doc is - # current". Exiting 0 here would be a vacuous green (fail-open silent - # pass), so an empty/usable-but-route-less table is INCONCLUSIVE (3), - # exactly like an unreadable one. ("The tool never exits 0 without - # having checked all 48 docs.") + # With no routes, the check cannot establish that routed docs are current. + # Treat that case as inconclusive (REQ-9). raise EnvironmentError3( f"route table {ROUTES_RELPATH} yielded zero routed docs — nothing " f"to check; an empty enumeration source is INCONCLUSIVE, not a pass" diff --git a/tooling/req-status.py b/tooling/req-status.py index 92ff274f5..23d823c17 100644 --- a/tooling/req-status.py +++ b/tooling/req-status.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 """ -REQ-status inventory + contradiction lint for source-level `//!` status rows. +REQ-status inventory and contradiction lint for source-level `//!` status rows. The pinned-SHA doc-drift gate catches routed design docs that are stale relative to governed files. It does not catch semantic contradictions inside the long @@ -11,7 +11,7 @@ //! | REQ-... | SHIPPED|NOT-STARTED | evidence | -and applies three deliberately mechanical checks: +and applies three checks: * exact requirement labels must not carry conflicting statuses; * NOT-STARTED rows must cite a blocker or explicit future/deferred scope; diff --git a/tooling/spec-discipline.py b/tooling/spec-discipline.py index fd1d15ee5..167b9cb0f 100755 --- a/tooling/spec-discipline.py +++ b/tooling/spec-discipline.py @@ -1,44 +1,15 @@ #!/usr/bin/env python3 -""" -spec-discipline hook (greenfield, spec-driven variant of the vibe-fork -translate-discipline gate). - -Thermite has no upstream codebase to translate. The authority is the -design layer, not a foreign source tree. This hook enforces: - - "read goal.md, read the component's design doc, read any golden/ - conformance reference the route declares; THEN edit the toolchain." - -as a deterministic per-edit gate. - -Two invocations in .claude/settings.json: - - - PostToolUse on Read -> records the Read in session state - - PreToolUse on Write|Edit -> gates writes to gated toolchain files - -State is persisted at .crosslink/.spec-reads.json (per-worktree). - -Required source classes for any gated edit: - 1. goal.md (always; the binding contract) - 2. .design//.md (per route; must exist on disk + Read) - 3. route.reference[*] (per route; ONLY if the route declares a - non-empty `reference` list — then >=1 - reference path must be Read. These are - conformance/ corpus entries or - tests/golden/ files: the external truth.) +"""Require the relevant design material to be read before source edits. -If a route is missing, the hook BLOCKS with instructions to add one. -If a design doc is missing, the hook BLOCKS with instructions to -dispatch acto-doc-author. +The PostToolUse hook records reads in ``.crosslink/.spec-reads.json``. The +PreToolUse hook checks Write and Edit requests against ``spec-routes.toml``. +Each routed edit requires ``goal.md``, its design document, and at least one +declared conformance or golden reference when the route specifies references. -See: - goal.md - "Spec-discipline rules" (R-XLATE-*) - goal.md - "Injected-instructions rules" (R-INJECT-*) - tooling/spec-routes.toml +Missing routes or design documents block the edit with a corrective message. +See the R-XLATE and R-INJECT rules in ``goal.md`` for the policy. -PROJECT CUSTOMIZATION: - Edit TARGET_CRATE_PREFIXES, TARGET_CRATE_EXACT, EXCLUDED_CRATES, - TARGET_EXTENSION, REFERENCE_PREFIXES below. +Customize the ``TARGET_*`` and ``REFERENCE_PREFIXES`` settings below. """ import json @@ -54,9 +25,7 @@ tomllib = None -# ===================================================================== -# PROJECT CUSTOMIZATION — edit these constants for your project -# ===================================================================== +# Project settings # Workspace crate name prefixes gated by this hook. Files outside these # crates are not gated. @@ -77,9 +46,7 @@ # the read path starts with one of the route's declared reference paths. REFERENCE_PREFIXES = ("conformance/", "tests/golden/") -# ===================================================================== -# Implementation — generally no edits needed below this line -# ===================================================================== +# Implementation # --- repo-root + state file paths -------------------------------------- From ff518b0a8f69da557c995c78b1ba143af0089bd5 Mon Sep 17 00:00:00 2001 From: Doll Date: Wed, 29 Jul 2026 01:22:08 -0500 Subject: [PATCH 2/3] Repin design docs after prose pass --- .design/basis/02-recursion-schemes.md | 2 +- .design/basis/04-collections.md | 2 +- .design/basis/06-provenance-and-sinks.md | 2 +- .design/basis/07-strings.md | 2 +- .design/basis/08-runnable-effect-link.md | 2 +- .design/basis/09-option-result.md | 2 +- .design/basis/10-recursion-tuples.md | 2 +- .design/basis/11-ergonomics.md | 2 +- .design/basis/12-mutual-recursion.md | 2 +- .design/basis/13-map.md | 2 +- .design/build/kernel-target.md | 2 +- .design/forge/audit-manifest.md | 2 +- .design/forge/build.md | 2 +- .design/forge/certificate-manifest.md | 2 +- .design/forge/check.md | 2 +- .design/forge/cli.md | 2 +- .design/forge/degrade-ladder.md | 2 +- .design/forge/e2e-vs-boundary.md | 2 +- .design/forge/goal-repl.md | 2 +- .design/forge/mutation-scoring.md | 2 +- .design/forge/proof-cache.md | 2 +- .design/forge/proof-repair.md | 2 +- .design/forge/runtime-sandbox.md | 2 +- .design/forge/solver-profiles.md | 2 +- .design/forge/solver-vacuity.md | 2 +- .design/forge/spec-review.md | 2 +- .design/forge/strengthening-probes.md | 2 +- .design/forge/vacuity-triage.md | 2 +- .design/lower/boundary-composition.md | 2 +- .design/lower/l1-runtime-checks.md | 2 +- .design/lower/l2-kani.md | 2 +- .design/lower/verus-lowering.md | 2 +- .design/skill/skill-generator.md | 2 +- .design/spec/spectherm-combinators.md | 2 +- .design/syntax/ast.md | 2 +- .design/syntax/lexer.md | 2 +- .design/syntax/parser.md | 2 +- .design/syntax/semantic-addressing.md | 2 +- .design/tooling/control-plane.md | 2 +- .design/tooling/doc-drift-tripwire.md | 2 +- .design/tooling/req-registry.md | 2 +- .design/verified/contract-tv.md | 2 +- .design/verified/exec-stmt-tv.md | 2 +- .design/verified/exec-tv.md | 2 +- .design/verified/exporter-surface-correspondence.md | 2 +- .design/verified/proof-backends.md | 2 +- .design/verified/self-verification.md | 2 +- .design/verified/strat-rust-lean-correspondence.md | 2 +- 48 files changed, 48 insertions(+), 48 deletions(-) diff --git a/.design/basis/02-recursion-schemes.md b/.design/basis/02-recursion-schemes.md index 6125fa639..ac6798ba8 100644 --- a/.design/basis/02-recursion-schemes.md +++ b/.design/basis/02-recursion-schemes.md @@ -3,7 +3,7 @@ tier: 3-component status: draft audited-sha: 92396428567edc6940a9e2845217f5ff4c2ea3c6 (re-pinned 2026-06-16, user-authorized: the only change to this doc's governed files since the prior pin is the additive stage-1 forge-tier increment 2a — the new Item::Forge surface + inert Item::Forge match arms, verified net-additive with no substantive removal of existing v1 logic (git log

..HEAD = the 8 forge commits); the v1 behavior this doc governs is unchanged, and the new forge-tier surface is specified in .design/stage1-forge-tier.md / REQ-S1-3) -audited-content-sha256: 9465c40b9cb13c9d7d1df49e36d751bee6fd76d4e2e13980c2384d33101e02cf +audited-content-sha256: 233bba31b9b79f796b931325d5542eada48aeb1dacf1981282b6716a99d00576 governs: thermite-syntax/src/ast.rs governs: thermite-syntax/src/parser.rs governs: thermite-spec/src/validator.rs diff --git a/.design/basis/04-collections.md b/.design/basis/04-collections.md index 5af1cc97b..67b58e8ef 100644 --- a/.design/basis/04-collections.md +++ b/.design/basis/04-collections.md @@ -3,7 +3,7 @@ tier: 3-component status: draft audited-sha: 92396428567edc6940a9e2845217f5ff4c2ea3c6 (re-pinned 2026-06-16, user-authorized: the only change to this doc's governed files since the prior pin is the additive stage-1 forge-tier increment 2a — the new Item::Forge surface + inert Item::Forge match arms, verified net-additive with no substantive removal of existing v1 logic (git log
..HEAD = the 8 forge commits); the v1 behavior this doc governs is unchanged, and the new forge-tier surface is specified in .design/stage1-forge-tier.md / REQ-S1-3) -audited-content-sha256: 7b94ae19debe08195561fa5cfd7a64afa7214923f7a06fab7021d4f132ccc83a +audited-content-sha256: c2d49852cf6133dee75d384c92736de61ab033e6a364dcff117dda6cc5deb4c2 governs: thermite-syntax/src/ast.rs governs: thermite-syntax/src/parser.rs governs: thermite-spec/src/validator.rs diff --git a/.design/basis/06-provenance-and-sinks.md b/.design/basis/06-provenance-and-sinks.md index 5c59c3733..94309a330 100644 --- a/.design/basis/06-provenance-and-sinks.md +++ b/.design/basis/06-provenance-and-sinks.md @@ -3,7 +3,7 @@ tier: 3-component status: draft audited-sha: 92396428567edc6940a9e2845217f5ff4c2ea3c6 (re-pinned 2026-06-16, user-authorized: the only change to this doc's governed files since the prior pin is the additive stage-1 forge-tier increment 2a — the new Item::Forge surface + inert Item::Forge match arms, verified net-additive with no substantive removal of existing v1 logic (git log
..HEAD = the 8 forge commits); the v1 behavior this doc governs is unchanged, and the new forge-tier surface is specified in .design/stage1-forge-tier.md / REQ-S1-3) -audited-content-sha256: 0982a363ece7986bdb15f21d4cbfcc4ebfefd36158f471a3b8a79e13e163c1d5 +audited-content-sha256: 3fc06e7b12bb9271ab5f13ff36a554f93addf85b8f57b704c0009f1937b184cd governs: thermite-spec/src/validator.rs governs: thermite-syntax/src/ast.rs governs: thermite-lower/src/lower.rs diff --git a/.design/basis/07-strings.md b/.design/basis/07-strings.md index 12ac39e58..b8a826995 100644 --- a/.design/basis/07-strings.md +++ b/.design/basis/07-strings.md @@ -3,7 +3,7 @@ tier: 3-component status: draft audited-sha: 92396428567edc6940a9e2845217f5ff4c2ea3c6 (re-pinned 2026-06-16, user-authorized: the only change to this doc's governed files since the prior pin is the additive stage-1 forge-tier increment 2a — the new Item::Forge surface + inert Item::Forge match arms, verified net-additive with no substantive removal of existing v1 logic (git log
..HEAD = the 8 forge commits); the v1 behavior this doc governs is unchanged, and the new forge-tier surface is specified in .design/stage1-forge-tier.md / REQ-S1-3) -audited-content-sha256: 7b94ae19debe08195561fa5cfd7a64afa7214923f7a06fab7021d4f132ccc83a +audited-content-sha256: c2d49852cf6133dee75d384c92736de61ab033e6a364dcff117dda6cc5deb4c2 governs: thermite-syntax/src/ast.rs governs: thermite-syntax/src/parser.rs governs: thermite-spec/src/validator.rs diff --git a/.design/basis/08-runnable-effect-link.md b/.design/basis/08-runnable-effect-link.md index 191d0ffc1..3890ebb31 100644 --- a/.design/basis/08-runnable-effect-link.md +++ b/.design/basis/08-runnable-effect-link.md @@ -3,7 +3,7 @@ tier: 3-component status: draft audited-sha: 92396428567edc6940a9e2845217f5ff4c2ea3c6 (re-pinned 2026-06-16, user-authorized: the only change to this doc's governed files since the prior pin is the additive stage-1 forge-tier increment 2a — the new Item::Forge surface + inert Item::Forge match arms, verified net-additive with no substantive removal of existing v1 logic (git log
..HEAD = the 8 forge commits); the v1 behavior this doc governs is unchanged, and the new forge-tier surface is specified in .design/stage1-forge-tier.md / REQ-S1-3) -audited-content-sha256: df347aac593e3f751d671d91af8a5bdd5c5d0ec1007017e86664c40b4f9c6ca2 +audited-content-sha256: a27e21b7ee949f1348a8b96077e1b75efbb8644ca440c70797d5fc3fee55bcd5 governs: forge/src/build.rs governs: forge/src/effect_wrappers.rs (the thermite-stdlib/src/effect/* paths this doc originally listed were never diff --git a/.design/basis/09-option-result.md b/.design/basis/09-option-result.md index 15d1ae787..4e4571a99 100644 --- a/.design/basis/09-option-result.md +++ b/.design/basis/09-option-result.md @@ -2,7 +2,7 @@