Spec proof discipline
Reference doc for proof work against
Bang/Spec.lean. Distilled from the originalbang-lang-wasmfx/CLAUDE.mdduring the wasmfx merge (2026-06-21). Theproof-engineersubagent (.claude/agents/proof-engineer.md) cites this doc as its primary discipline reference.
Mission
Discharge every sorry in Bang/Spec.lean with a real, machine-checked proof.
Zero cheating. The theorem statements are the spec — they are frozen.
Hard invariants (CI-enforced, non-negotiable)
- No
sorry, noadmit, nosorryAxin any committed.leanfile outsideBang/Audit.leanitself. - No new
axiom. Every headline theorem may transitively depend ONLY on the trusted three:propext,Classical.choice,Quot.sound. Verify with#print axioms <thm>(seeBang/Audit.lean). Anything else = FAIL. - Do not weaken a theorem STATEMENT. You may not add a hypothesis, remove a conclusion, or specialise a quantifier. If a statement looks false or unprovable, STOP and record it (open question / CONTEXT.md). Never "fix" it by mutating it — a vacuous proof is worse than no proof.
≈/⊑are fixed (Bang/Spec.lean §5). Do not redefinectxApprox,ctxEquiv,Vrel, orCrelto trivialise a goal. Their content gets filled in Phase A; their role is settled.- No
opaquesurvives in the proven core. A proof about anopaquesymbol is meaningless. Phase A replaces every stub with a real definition. - No laundering. No
native_decide/decideon the metatheorems, no unproven@[simp]lemmas, noClassical-coercing a false goal toTrue.
Scope
The engineering spine is the two-grade system: effect row Eff +
multiplicity Mult, both OrderedSemiring. That is the whole contract.
Build exactly this.
OUT of scope for the engineering phase (research-layer extensions — do NOT
start them, do NOT let them leak into Bang/Spec.lean):
- cost / potential as a third grade (AARA / calf line). Real direction, not the spine. Adding it now destabilises the contract.
- distribution / CALM (
Bang/Distribution.lean) — separate result; stays a flagged conjecture. - modal-row alternative to the lacks-discipline (Tang–Lindley) — a design fork already decided against in ADR-0018; do not re-litigate in code.
- multi-shot cost, polynomial AARA — open problems, not engineering tasks.
If a task seems to require any of the above, it is mis-scoped: STOP and escalate rather than expanding the spine.
Phases — do A fully before B
Phase A — Definitions (NO theorem proofs yet)
Turn every opaque into a real definition:
- AST:
VTy,CTy,Val,Comp,Var, constructors incl.U ρ,F e. - Judgments:
HasVTy,HasCTyasinductive … : Prop. - Semantics:
Source.step/eval/evalTraceas the fuel interpreter (reuse the existingBang/Eval.leanonce it's ported to graded CBPV at ◊2). - The logical relation:
Vrel,Crelby well-founded recursion on the step index then on types. TheF e Acase is the crux — get it reviewed.
Exit criterion: file builds, sorry appears ONLY in theorem bodies.
Phase B — Proofs
Discharge sorrys in PROOF_ORDER. After each: lake build + run
Bang/Audit.lean.
PROOF_ORDER (risk-first, not file order)
lr_sound,lr_fundamental— the LR is the spine; nothing is legitimate until soundness holds.lr_fundamentaldecomposes into the per-rule compatibility lemmas inBang/Meta/BinaryLR.lean; provecompat_handleLAST (it is the[KEY]one that consumesSrel). The rest of Compat is mechanical.group_recovers— ✓ RESOLVED (the research gate fired, 2026-06-23, ADR-0032): RETIRED — false-as-stated (a divergingcmakes(c;ret()) ≉ ret()) + vacuous (noAddGroupinstance for the realEff). No≈side-condition needed (the gate's whole payoff — surfaced before the LR spine committed). Rollback is the txn handler (all_or_nothing_abort). The cheap closesseq_unit+zero_usage_erasabletake this slot now.compile_forward_sim— the contribution. Fail fast if it won't go.subst_value— validates the CBPV "no σ-split" assumption. ✓ PROVEN (de Bruijn,Bang/Core/Soundness.lean, axiom-clean).- the
[STD]block (preservation · progress · type_safety) — ✓ PROVEN (2026-06-22, axiom-clean). ⚠ It was NOT "mechanical": provingpreservationexposed 4 ways the Phase-A typing rules diverged from the Torczon port and made the frozen statements FALSE (lam dropped its body effect; handle over non-Fbodies broke progress; the letC grade reshape needs commutativeMult; progress is false at generalB). All corrected in ADR-0021. Lesson: "mechanical once X holds" is a hypothesis the proof tests, not a license to skip the proof — the STD block is where the typing rules' fidelity to the reference finally bit.
Definition of done
lake buildclean;tools/audit.shexits 0.Bang/Audit.lean: every headline theorem's axiom set ⊆ {propext,Classical.choice,Quot.sound}. NosorryAxanywhere.- Phase-A design choices recorded in ADRs (per project ADR discipline; see
docs/decisions/). - Anything you could not prove WITHOUT mutating it: escalated to the
orchestrator and logged in
CONTEXT.md.
When stuck
Surface the gap; move to the next independent goal. Never fabricate a lemma,
weaken a statement, or sorry to "make progress". A red build with honest
gaps beats a green build that lies.
What a clean proof looks like (canonical example)
The standard shape for a proof body in Bang/Spec.lean (or downstream modules implementing Phase B). Note: statement unchanged from the frozen PRD; only the body fills in.
subst_value is now PROVEN on the de Bruijn base (Bang/Core/Soundness.lean,
axiom-clean) — the real proof is a bottom-up lemma tree, not a single induction.
The block below is kept only to ILLUSTRATE the pattern (intro → induction →
case-by-case, technique cited, sorry-with-comment for blocked cases), using the
current (de Bruijn, ADR-0020) statement:
-- pattern: structural case-analysis on the typing derivation, technique cited.
-- shape: torczon-oopsla24-effects-coeffects §graded-subst (port: resource/CBPV/typing.v)
-- the REAL proof (and its weakening/grade-arithmetic lemmas) is in Bang/Core/Soundness.lean.
theorem subst_value
(ρ : Mult) {γ γ_v : GradeVec Mult} {Γ : TyCtx Eff Mult}
{v : Val} {A : VTy Eff Mult} {c : Comp} {e : Eff} {B : CTy Eff Mult} :
HasVTy γ_v Γ v A →
HasCTy (ρ :: γ) (A :: Γ) c e B →
HasCTy (γ + ρ • γ_v) Γ (Comp.subst v c) e B := by
intro hv hc
induction hc with
| ret hv' => exact .ret hv' -- structural: ret rule
| letC hM hN ihM ihN => grind -- handled by SMT-style closer
| force hu => -- TODO(blocking-on): example only — see Metatheory for the real case
sorry
-- ... cases for force, lam, app, handle ...What this exhibits:
- Statement frozen — copied verbatim from
Bang/Spec.lean; only the body changes. - Technique citation —
-- shape: biernacki-popl18-handle-with-care §5.4. Cite the source paper / chapter as a comment so future readers can verify the adaptation. - Pattern:
intro → induction → case-by-case. For 4-6-constructor judgments, this is the workhorse. - Tactic choice:
exactfor cases that match a constructor;grind(≥4.28) for cases SMT-style closure can handle;sorryonly when blocked AND commented WHY. - Sorry-with-comment — never bare. The comment names what's blocking (e.g., a missing lemma, a definitional adjustment needed in the kernel) so a future session knows where to pick up.
What an anti-pattern proof looks like
theorem subst_value ... := by
intros
sorry -- ← bad: no reason, no plan-- ALSO BAD: weakening the statement to make the proof close.
-- (Original statement removed a precondition; this version weakens.)
theorem subst_value_weakened (h : False) : True := by trivial-- BAD: a generic `by tactic_chain` that masks failure modes.
theorem subst_value ... := by aesop -- if aesop times out or
-- fails partially, the actual
-- structure is lostDiscipline: if a proof needs to weaken the statement, that's a signal
to escalate to kernel-engineer (statement might be wrong) — not a license
to mutate.