Regula rule reference

3. Logic and Proof Patterns🔗

Overview🔗

This section establishes proof patterns for mathematical statements and contracts about executable Lean definitions. These patterns implement the theorem-backed-claims principle from Section 1.

3.1 What Must Be Proven🔗

Requirement: Every material property claimed as established about a mathematical model or executable Lean definition MUST have machine-checked evidence from its type, proof-bearing construction, or a supporting theorem. State assumptions and open targets separately (§3.10).

Claim-Specific Proof Obligations: Apply the following obligations where required; they do not demand a separate theorem for every category.

  1. Admission and Invariant Preservation: Establish the unary invariant at admission and every write/update by proof-bearing results or justified raw representation contracts of §1.1. Conditional preservation alone does not establish admission.

  2. Totality: State the function’s domain and encode any promised restriction in its API. Distinguish a total logical definition from termination of its execution (§3.2.1).

  3. Termination: Recursion presented as terminating requires structural or well-founded justification or another checked termination argument. Classify other recursion mechanisms explicitly (§3.6).

  4. Composition Properties: Prove the claimed relations over transitions, reachable states, histories, or resource use. Include initialization, preservation, and composition where needed to derive the claim. A state invariant alone does not establish how an output relates to an input or history.

  5. Impossibility Results: Use the type or a theorem to establish that the states a claim excludes cannot occur.

Example - An Intrinsic Invariant and Its Consequences:

/-- A system state that must maintain invariants. -/ structure SystemState where /-- The number of participants. -/ participants : Nat /-- The number of active proposals. -/ activeProposals : Nat /-- Invariant: there are no more active proposals than participants. -/ inv : activeProposals ≤ participants /-- Non-vacuity: states satisfying the invariant exist. -/ example : Nonempty SystemState := ⟨⟨3, 1, ⊢ 1 ≤ 3 All goals completed! 🐙⟩⟩ /-- Adding a participant maintains the invariant. -/ def addParticipant (s : SystemState) : SystemState where participants := s.participants + 1 activeProposals := s.activeProposals inv := s:SystemState⊢ s.activeProposals ≤ s.participants + 1 -- Proof that invariant is maintained (`omega`: core tactic, pure-Nat goal) s:SystemStateh:s.activeProposals ≤ s.participants⊢ s.activeProposals ≤ s.participants + 1 All goals completed! 🐙 /-- The input's active proposals stay within the participants after adding one. -/ theorem activeProposals_le_addParticipant_participants (s : SystemState) : s.activeProposals ≤ (addParticipant s).participants := -- Proof is immediate from construction (addParticipant s).inv /-- Composition preserves the invariant stated above. Because the invariant is a *field* of `SystemState`, no side conditions on `f` and `g` are needed for this bound — their results already carry it. -/ theorem comp_activeProposals_le_participants (f g : SystemState → SystemState) (s : SystemState) : ((f ∘ g) s).activeProposals ≤ ((f ∘ g) s).participants := (f (g s)).inv /-- Prove the excluded states are excluded. -/ theorem not_exists_participants_eq_zero_and_activeProposals_pos : ¬∃ s : SystemState, s.participants = 0 ∧ 0 < s.activeProposals := ⊢ ¬∃ s, s.participants = 0 ∧ 0 < s.activeProposals s:SystemStatehp:s.participants = 0ha:0 < s.activeProposals⊢ False s:SystemStatehp:s.participants = 0ha:0 < s.activeProposalsthis:s.activeProposals ≤ s.participants⊢ False s:SystemStatehp:s.participants = 0ha:0 < s.activeProposalsthis:s.activeProposals ≤ 0⊢ False All goals completed! 🐙

The theorem comp_activeProposals_le_participants follows from the result type: every returned SystemState carries its bound. The functions are total in Lean’s logic. This theorem establishes no relation between inputs and outputs beyond the invariant. It does not provide an execution time bound or connect to an external process (module 1 §1.6).

3.2 Proof Patterns🔗

Pattern: Structure proofs to be both correct and comprehensible, following established patterns for common proof obligations.

Core Patterns:

  1. Totality and Termination by Construction: Use structural or well-founded recursion for terminating definitions; distinguish other computation mechanisms explicitly (module 7 §7.6).

  2. Property-Based Testing as Refutation Aid: Use optional counterexample search to diagnose false candidates; a passing sampled run is not a proof (§3.2.2).

  3. Typeclasses for Lawful Abstractions: Generic interfaces with proven laws, held in the operational class or in a Prop-valued lawful mixin (§3.2.3).

  4. Decidability: Distinguish logical decidability from executable decision procedures (§3.2.4).

  5. Proof Economy: Separate elaboration, proof-artifact, kernel-replay, and native-execution cost; inspect the resulting dependencies rather than inferring trust from speed (§3.2.5).

3.2.1 Totality, Termination, and Totalized Operations🔗

Rationale: Structural and well-founded recursion justify a recursive definition through Lean’s recursors. Say precisely what that buys: the definition is total in Lean's logic. It has not established code generation, resource consumption, execution time, or a deployed system's behavior; termination is not a complexity bound and not, by itself, a security property.

Example - Termination Patterns (the examples use domain-specific types and operations rather than redeclaring matching Core or Mathlib functions such as List.sum or Nat.ack):

/-- A small review workflow used only to expose its recursive structure. -/ inductive ReviewPlan where /-- No step remains. -/ | done /-- A review step, followed by the rest of the plan. -/ | review (rest : ReviewPlan) /-- A revision step, followed by the rest of the plan. -/ | revise (rest : ReviewPlan) /-- Structural recursion: Lean accepts the call on the direct subplan. -/ def ReviewPlan.stepCount : ReviewPlan → Nat | .done => 0 | .review rest => rest.stepCount + 1 | .revise rest => rest.stepCount + 1 /-- State for a bounded retry model. -/ structure RetryState where /-- Retries still to run. -/ remaining : Nat /-- Retries already completed. -/ completed : Nat /-- The completed count once every remaining retry has run. Well-founded recursion: each call has one fewer remaining retry. -/ def RetryState.totalCompleted (s : RetryState) : Nat := if _h : s.remaining = 0 then s.completed else totalCompleted { remaining := s.remaining - 1, completed := s.completed + 1 } termination_by s.remaining decreasing_by All goals completed! 🐙

This recursive call increases its argument indefinitely, and Lean rejects the ordinary def:

✗ Rejected by Lean, as intended
/-- Unbounded increasing recursion: Lean refuses this ordinary definition. -/ def fail to show termination for loop with errors failed to infer structural recursion: Cannot use parameter n: failed to eliminate recursive application loop (n + 1) failed to prove termination, possible solutions: - Use `have`-expressions to prove the remaining goals - Use `termination_by` to specify a different well-founded relation - Use `decreasing_by` to specify your own tactic for discharging this kind of goal n:Nat⊢ n + 1 < nloop (n : Nat) : Nat := loop (n + 1)

Lean's error at line 2, the checked evidence:

fail to show termination for
  loop
with errors
failed to infer structural recursion:
Cannot use parameter n:
  failed to eliminate recursive application
    loop (n + 1)


failed to prove termination, possible solutions:
  - Use `have`-expressions to prove the remaining goals
  - Use `termination_by` to specify a different well-founded relation
  - Use `decreasing_by` to specify your own tactic for discharging this kind of goal
n : Nat
⊢ n + 1 < n

Totalized operations: Natural-number division in Lean is total; division by zero returns 0.

/-- `Nat.div` is total: the zero-divisor case is defined, yielding 0. -/ example : (1 : Nat) / 0 = 0 := rfl /-- A restricted domain is expressed by the API when the model wants one: the denominator subtype carries the proof obligation. -/ def safeDiv (a : Nat) (d : {d : Nat // 0 < d}) : Nat := a / d.val /-- `safeDiv` agrees with the underlying total division. -/ theorem safeDiv_eq (a : Nat) (d : {d : Nat // 0 < d}) : safeDiv a d = a / d.val := rfl example : safeDiv 6 ⟨2, ⊢ 0 < 2 All goals completed! 🐙⟩ = 3 := rfl

The subtype documents the intended domain at the type level. The underlying division is total in both cases; 1 / 0 remains 0, but a zero denominator cannot be constructed for safeDiv. Mathlib names this positive-natural subtype ℕ+ (PNat). This example spells out the subtype pattern for teaching rather than introducing a new mathematical type. The existing name is available for ordinary reuse (module 1 §1.4). The forbidden zero argument is checked separately:

✗ Rejected by Lean, as intended
/-- Division by a divisor that carries its proof of positivity. -/ def safeDiv (a : Nat) (d : {d : Nat // 0 < d}) : Nat := a / d.val /-- The domain restriction is enforced at elaboration: the zero divisor cannot be supplied a proof, so this call does not elaborate. -/ def oops := safeDiv 1 ⟨0, ⊢ 0 < 0 Tactic `decide` proved that the proposition 0 < 0 is false⊢ 0 < 0⟩

Lean's error at line 6, the checked evidence:

Tactic `decide` proved that the proposition
  0 < 0
is false

3.2.2 Property-Based Testing as Refutation Aid🔗

Rationale: Counterexample search is optional diagnostic assistance. A counterexample checked against exact definitions and hypotheses refutes a candidate universal statement. A passing sampled run does not prove it. Prefer types, construction, existing theorems, and structural or compositional proofs. Use testing to discover counterexamples when a suitable deductive or symbolic approach is unavailable. No authoring order is mandated (module 1 §1.2).

Kernel-checked exhaustive case analysis over a closed finite domain is a proof, as is a checked witness of an existential claim. These differ from sampled agreement or unchecked enumeration. Mutation tests diagnose a checker’s response to selected inputs but do not establish its universal correctness.

Example - Refutation, Then a Library Proof:

/-- The naive claim `∀ (l₁ l₂ : List Nat), (l₁ ++ l₂).reverse = l₁.reverse ++ l₂.reverse` is refuted by the counterexample `l₁ = [1]`, `l₂ = [2]` (LHS `[2, 1]` ≠ RHS `[1, 2]`). The corrected statement follows from the library theorem, regardless of how the counterexample was found. -/ theorem reverse_append_eq (l₁ l₂ : List Nat) : (l₁ ++ l₂).reverse = l₂.reverse ++ l₁.reverse := @List.reverse_append Nat l₁ l₂ /-- info: 'reverse_append_eq' depends on axioms: [propext] -/ #guard_msgs in #print axioms reverse_append_eq

A discovered counterexample can become a checked refutation, and a discovered witness can become a checked existence proof. A corrected universal statement requires its own proof; the preceding test run does not establish it.

3.2.3 Typeclasses for Lawful Abstractions🔗

Rationale: Typeclasses can bundle operations, laws, or both. An operational class alone supplies its declared operations. An instance of a law-bearing class must supply evidence of its laws.

Requirement: Every claimed typeclass law MUST follow from proof-requiring fields of the operational class or a lawful mixin required by the interface. A law may be supplied directly or derived by a checked theorem from sufficient primitive laws under the advertised hypotheses. Derived consequences need no duplicate fields. Comments explain laws but do not supply their proofs.

Implementation Note: Construct each law-bearing instance with evidence for every required field, reusing existing instances or theorems where appropriate. Missing evidence prevents a complete instance; sorry or admit is prohibited. An operations-only instance is valid when it makes no lawfulness claim.

Example - Operations Without the Claimed Laws:

The following class and instance elaborate, but the two advertised laws are false. The checked refutations identify the defect; merely compiling the operations would not.

/-- ❌ CRITICAL VIOLATION: laws only in documentation. The operations below are the class's only fields. -/ class BadFlourishing (α : Type) where /-- A binary combining operation. -/ enhance : α → α → α /-- An integer-valued measure. -/ measure : α → Int -- Law: enhance is associative (comment only) -- Law: measure is monotone (comment only) -- These "laws" are just comments! Any instance can violate them! -- (Plain `--` lines, NOT `/-- ... -/` docstrings: a docstring demands a -- declaration after it, and dangling ones fail to parse.) -- This violating instance elaborates — that is exactly the defect: instance : BadFlourishing Int where enhance := (· - ·) -- Not associative! measure := (fun x ↦ -x) -- Not monotone! -- No error because the laws are not fields /-- Subtraction is not associative. -/ example : ¬ (∀ a b c : Int, BadFlourishing.enhance (BadFlourishing.enhance a b) c = BadFlourishing.enhance a (BadFlourishing.enhance b c)) := ⊢ ¬∀ (a b c : Int), BadFlourishing.enhance (BadFlourishing.enhance a b) c = BadFlourishing.enhance a (BadFlourishing.enhance b c) h:∀ (a b c : Int), BadFlourishing.enhance (BadFlourishing.enhance a b) c = BadFlourishing.enhance a (BadFlourishing.enhance b c)⊢ False h:∀ (a b c : Int), BadFlourishing.enhance (BadFlourishing.enhance a b) c = BadFlourishing.enhance a (BadFlourishing.enhance b c)bad:BadFlourishing.enhance (BadFlourishing.enhance 0 0) 1 = BadFlourishing.enhance 0 (BadFlourishing.enhance 0 1)⊢ False All goals completed! 🐙 /-- Negation does not preserve the ordinary integer order. -/ example : ¬ (∀ a b : Int, a ≤ b → BadFlourishing.measure a ≤ BadFlourishing.measure b) := ⊢ ¬∀ (a b : Int), a ≤ b → BadFlourishing.measure a ≤ BadFlourishing.measure b h:∀ (a b : Int), a ≤ b → BadFlourishing.measure a ≤ BadFlourishing.measure b⊢ False h:∀ (a b : Int), a ≤ b → BadFlourishing.measure a ≤ BadFlourishing.measure bbad:BadFlourishing.measure 0 ≤ BadFlourishing.measure 1⊢ False All goals completed! 🐙

Example - Lawful Abstraction:

Failure to prove a law leaves an unresolved obligation. A counterexample may show that the law or operations need correction; difficulty finding a proof does not prove either is wrong. This separate interface illustrates three order laws rather than repairing the associativity contract above.

/-- The order is a separate lawful parameter (the relation `LE α` with its `Std.IsPreorder α` laws), not a relation the instance could choose for itself. -/ class Flourishable (α : Type) [LE α] [Std.IsPreorder α] where -- Operations /-- An operation that must not decrease its first argument. -/ enhance : α → α → α /-- An operation that must not increase its first argument. -/ diminish : α → α → α /-- A natural-number measure that must preserve the order. -/ measure : α → Nat -- Laws that MUST be proven for every instance /-- `enhance` does not decrease its first argument. -/ enhance_increases : ∀ a b, a ≤ enhance a b /-- `diminish` does not increase its first argument. -/ diminish_decreases : ∀ a b, diminish a b ≤ a /-- `measure` preserves the order. -/ measure_monotone : ∀ a b, a ≤ b → measure a ≤ measure b /-- Concrete instance with ALL laws proven. Core's order lemmas prove the max/min laws; the identity measure preserves the supplied inequality. -/ instance : Flourishable Nat where enhance := max diminish := min measure := id enhance_increases := Nat.le_max_left diminish_decreases := Nat.min_le_left measure_monotone := fun _ _ h ↦ h

These laws state that enhance does not decrease its first argument, diminish does not increase it, and measure preserves the supplied order. Whether these properties suffice depends on the interface’s intended claim.

Laws in a lawful mixin: The laws need not live in the class that holds the operations. Core keeps Std.LawfulOrderMin and Std.LawfulOrderMax apart from Min and Max, and LawfulFunctor, LawfulMonad, and LawfulBEq apart from Functor, Monad, and BEq; the pinned Mathlib keeps ordered-algebra laws in Prop-valued mixins (IsOrderedAddMonoid, IsStrictOrderedRing) over the operational classes. Every claimed law must follow from the available law-bearing evidence, directly or by checked derivation. Every instance discharges each required primitive field, and a claimed lawful interface obtains its required mixin. Four consequences follow:

  1. A declaration using a mixin’s law MUST have its evidence available. A generic declaration may require the mixin directly ([Flourishable α] [LawfulFlourishable α]) or obtain it from stronger assumptions that supply the instance. A concrete use obtains it from an established instance (the Nat examples below). The operations alone do not supply the mixin’s laws; the elaborator refuses the law without the mixin (second negative fence below).

  2. An operational instance alone supplies no evidence of the mixin’s laws. Prose that presents it as lawful is a prose-only law (THEOREM-02, SCOPE-02), and a claimed-lawful use site that omits the mixin fails THEOREM-02.

  3. The mixin instance is admitted only with every field proved; the elaborator rejects an omitted law exactly as it does for a law in the operational class (first negative fence below).

  4. The laws are relative to the order supplied to the mixin ([LE α] [Std.IsPreorder α]). When the claim concerns an existing order, the interface MUST use that order or provide checked correspondence to it. This example takes the order as a parameter so the operations do not select a different relation. Bundled order hierarchies are also valid. Avoid competing instance paths that silently select a different order.

import Audit.Economy /-- The claim names the mixin; the `Nat` instance discharged every law from core's order lemmas. -/ example : Economy.LawfulFlourishable Nat := inferInstance /-- Using a law requires the mixin in the binders. -/ example {α : Type} [LE α] [Std.IsPreorder α] [Economy.Flourishable α] [Economy.LawfulFlourishable α] (a b : α) : Economy.Flourishable.measure a ≤ Economy.Flourishable.measure (Economy.Flourishable.enhance a b) := Economy.measure_enhance_ge a b

For the order on Bad induced by its integer field, subtraction violates enhance_increases and the negated measure violates measure_monotone. The following fence checks that an instance omitting the required laws is rejected. Missing-field rejection alone does not prove that proposed laws are false.

✗ Rejected by Lean, as intended
import Audit.Economy /-- A wrapper around an integer. -/ structure Bad where /-- The wrapped integer. -/ x : Int instance : LE Bad := ⟨fun a b ↦ a.x ≤ b.x⟩ instance : Std.IsPreorder Bad where le_refl a := Int.le_refl a.x le_trans _ _ _ := Int.le_trans instance : Economy.Flourishable Bad where enhance := fun a b ↦ ⟨a.x - b.x⟩ diminish := fun a b ↦ ⟨a.x + b.x⟩ measure := fun a ↦ (-a.x).toNat -- The mixin instance is the lawfulness claim; it is refused without its proofs. instance : Economy.LawfulFlourishable Bad Fields missing: `enhance_increases`, `diminish_decreases`, `measure_monotone`where

Lean's error at line 20, the checked evidence:

Fields missing: `enhance_increases`, `diminish_decreases`, `measure_monotone`

And a law cannot be used from the operations alone:

✗ Rejected by Lean, as intended
import Audit.Economy /-- The mixin is not required, so the law is not available. -/ theorem le_enhance {α : Type} [LE α] [Std.IsPreorder α] [Economy.Flourishable α] (a b : α) : a ≤ Economy.Flourishable.enhance a b := failed to synthesize instance of type class Economy.LawfulFlourishable α Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.Economy.LawfulFlourishable.enhance_increases a b

Lean's error at line 6, the checked evidence:

failed to synthesize instance of type class
  Economy.LawfulFlourishable α

Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.

The same operational class and mixin over Mathlib's Preorder, with a real-valued measure and an ℝ instance discharged from Mathlib's lattice lemmas, are checked in the Mathlib integration package.

3.2.4 Decidability: Logical vs Executable🔗

Rationale: A proposition p : Prop states a property; it is not a proof or a decision procedure. Decidable p is data with two constructors: one carries a proof of p, the other a proof of ¬p. A computable producer of that data gives an executable decision procedure. Classical.propDecidable supplies such data logically for any proposition, but is noncomputable and depends on Classical.choice.

A branch that affects runtime data needs a computable decision procedure. Classical reasoning may be used in erased proofs under an allowed foundation profile or in noncomputable logical definitions. Proof erasure and noncomputability do not prevent all logical reduction; they distinguish what must execute in compiled code. For example, this logical admission decides an existential over every natural number classically:

open Classical in /-- Proof-producing logical admission: no program decides whether an arbitrary sequence has a zero, so the branch is classical and the definition noncomputable. -/ noncomputable def someZero? (f : Nat → Nat) : Option {n : Nat // f n = 0} := if h : ∃ n, f n = 0 then some ⟨Classical.choose h, Classical.choose_spec h⟩ else none

Mathlib's order on ℝ is noncomputable in the same way: an admission that branches on 0 ≤ c for a real c is a logical definition, not an executable validator. In contrast, the natural-number validator in module 1 §1.1 is executable and produces a proof-bearing value.

Example - A Predicate with an Executable Decision Procedure:

/-- A proposition over natural-number arithmetic. -/ def HasQuorum (n : Nat) (total : Nat) : Prop := 2 * total < 3 * n /-- A *computable* decision procedure for the predicate. -/ instance (n total : Nat) : Decidable (HasQuorum n total) := inferInstanceAs (Decidable (2 * total < 3 * n)) /-- The decision message; it branches on the predicate in computable code. -/ def quorumDecision (votes total : Nat) : String := if HasQuorum votes total then "Proceed with proposal" else "Insufficient votes" -- The predicate is both logically precise and computationally usable example : quorumDecision 7 10 = "Proceed with proposal" := rfl /-- The contrast: `Classical.propDecidable` decides ANY proposition — but it is noncomputable and choice-dependent. It licenses `if` in *proofs*, never an executable decision. -/ noncomputable def classicalDecidable (p : Prop) : Decidable p := Classical.propDecidable p -- 'classicalDecidable' depends on axioms: [propext, Classical.choice, Quot.sound] 'classicalDecidable' depends on axioms: [propext, Classical.choice, Quot.sound]#print axioms classicalDecidable

Rule: A decision used in erased proof code must respect the claimed foundation profile. A decision used to select runtime data must have a computable producer. A Decidable type alone establishes neither executability nor foundation profile. A function whose result type is Decidable p decides p in both directions by construction, so a function registered as a decision with that result type needs no decision contract (§3.8).

3.2.5 Proof Economy: Four Cost Domains and One Trust Question🔗

Rationale: Proof cost has four distinct domains. A proof can be cheap in one and expensive in another. Soundness and foundation strength depend on the resulting term and its dependencies, not on its speed. Prefer the simplest proof of the same claim under the intended assumptions and foundation profile.

Domain

Cost

When it is paid

Elaboration and search

Tactic execution, unification, typeclass synthesis, and proof construction.

When the source is elaborated.

Proof artifact

Produced terms and auxiliary declarations, serialized size, and loading.

During storage and loading of imported artifacts.

Kernel checking

Type checking and the definitional reduction needed to accept the term.

When a declaration is kernel-checked, including fresh checks.

Native execution

Compiled code, including compiler replacements and external primitives.

When that code runs.

  • Separate tactic reduction from kernel reduction. In the examples below, structural sumTo unfolds for ordinary decide. The well-founded sumWf is irreducible at the elaborator’s default transparency, so ordinary decide fails. decide +kernel can ask the kernel to check by reduction. A tactic’s failure to unfold a definition is not evidence that the kernel cannot reduce it.

  • A small proof term can require substantial checking. Reduction-based proofs such as decide and rfl may require the kernel to evaluate definitions in the goal. By default, decide also reduces its decision instance during elaboration. +kernel avoids that initial evaluation. Proof-producing tactics such as simp, omega, and norm_num can avoid replaying the target computation, but their costs depend on the goal and generated proof. No tactic name guarantees cheap checking.

  • Prefer reusable arguments when they eliminate repeated computation. two_mul_sumTo proves the identity for every natural number by induction. Applying it avoids unfolding the recursive sum separately for each numeral. Reducing sumTo n traverses recursive cases. That observation alone is not a wall-clock complexity bound because arithmetic and checker overhead also cost time.

  • Inspect the elaborated recursion and its dependencies. On this pin, sumWf uses WellFounded.Nat.fix. A general well-founded relation can use WellFounded.fix and an accessibility proof. The former uses recursion over a natural-number fuel bound. #print shows the actual construction. A definition’s transitive axiom set includes its body, termination proof, and selected instances. The omega proof in this particular sumWf contributes propext and Quot.sound. Do not assign a foundation profile from the recursion scheme or tactic name alone.

  • Compiler simplification preserves the logical definition. The @[csimp] equality replaces compiled calls to sumTo with closedSum. It does not change the kernel reduction of sumTo. So it does not remove recursive computation from a decide proof about that definition (§3.6).

  • Native proof evaluation changes the trust account. Ordinary #eval produces an observed result, not a theorem. native_decide and decide +native use a generated axiom to accept a compiled result. On this pin, the SAT path of bv_decide likewise uses a generated axiom to accept native certificate checking. A goal closed by normalization alone may have no such axiom. Inspect the exact axiom set. These mechanisms are classified as compiler-trusting and excluded from conforming positive proof surfaces (§3.4).

Example - Same Equations, Different Kernel Cost, Analytic Discharge:

import Audit.Economy open Economy /-- Kernel reduction evaluates the structural recursion at 100. -/ example : sumTo 100 = 5050 := ⊢ sumTo 100 = 5050 All goals completed! 🐙 /-- Analytic discharge: one rewrite by the universal theorem, then kernel-accelerated literal arithmetic. -/ example : sumTo 100 = 5050 := ⊢ sumTo 100 = 5050 ⊢ closedSum 100 = 5050; All goals completed! 🐙 /-- The universal statement costs one induction for every `n`. -/ example (n : Nat) : 2 * sumTo n = n * (n + 1) := two_mul_sumTo n /-- The well-founded twin agrees pointwise; the proof goes through its equation theorem, the stable interface, rather than through transparency. -/ example : sumWf 100 = 5050 := ⊢ sumWf 100 = 5050 ⊢ sumTo 100 = 5050; All goals completed! 🐙 /-- Kernel replay of the well-founded definition is available; only the elaborator's default-transparency reduction is not. -/ example : sumWf 10 = 55 := ⊢ sumWf 10 = 55 All goals completed! 🐙 'Economy.sumTo' does not depend on any axioms#print axioms sumTo -- 'Economy.sumTo' does not depend on any axioms 'Economy.sumWf' depends on axioms: [propext, Quot.sound]#print axioms sumWf -- 'Economy.sumWf' depends on axioms: [propext, Quot.sound]

The elaborator does not unfold the @[irreducible] well-founded definition at default transparency, so a plain decide fails before any kernel replay starts. This is an elaboration-domain failure, not a kernel fact. The preceding decide +kernel proves the same statement by kernel replay.

✗ Rejected by Lean, as intended
import Audit.Economy example : Economy.sumWf 10 = 55 := ⊢ Economy.sumWf 10 = 55 Tactic `decide` failed for proposition Economy.sumWf 10 = 55 because its `Decidable` instance instDecidableEqNat (Economy.sumWf 10) 55 did not reduce to `isTrue` or `isFalse`. After unfolding the instances `instDecidableEqNat` and `Nat.decEq`, reduction got stuck at the `Decidable` instance match h : (Economy.sumWf 10).beq 55 with | true => isTrue ⋯ | false => isFalse ⋯⊢ Economy.sumWf 10 = 55

Lean's error at line 3, the checked evidence:

Tactic `decide` failed for proposition
  Economy.sumWf 10 = 55
because its `Decidable` instance
  instDecidableEqNat (Economy.sumWf 10) 55
did not reduce to `isTrue` or `isFalse`.

After unfolding the instances `instDecidableEqNat` and `Nat.decEq`, reduction got stuck at the `Decidable` instance
  match h : (Economy.sumWf 10).beq 55 with
  | true => isTrue ⋯
  | false => isFalse ⋯

Example - Same Statement, Different Trust:

import Std.Tactic.BVDecide /-- Kernel-checked: `decide` produces a proof term the kernel re-checks. -/ theorem gcd_1071_462_kernel : Nat.gcd 1071 462 = 21 := ⊢ Nat.gcd 1071 462 = 21 All goals completed! 🐙 /-- Compiler-trusting: `native_decide` trusts the compiled evaluation. -/ theorem gcd_1071_462_native : Nat.gcd 1071 462 = 21 := ⊢ Nat.gcd 1071 462 = 21 All goals completed! 🐙 /-- Compiler-trusting: `decide +native` takes the same compiled path. -/ theorem gcd_1071_462_decide_native : Nat.gcd 1071 462 = 21 := ⊢ Nat.gcd 1071 462 = 21 All goals completed! 🐙 /-- Compiler-trusting: `bv_decide` checks its SAT certificate with compiled code. -/ theorem mul_comm_bv (x y : BitVec 8) : x * y = y * x := x:BitVec 8y:BitVec 8⊢ x * y = y * x All goals completed! 🐙 'gcd_1071_462_kernel' does not depend on any axioms#print axioms gcd_1071_462_kernel -- 'gcd_1071_462_kernel' does not depend on any axioms 'gcd_1071_462_native' depends on axioms: [gcd_1071_462_native._native.native_decide.ax_1_1]#print axioms gcd_1071_462_native -- The output includes a per-invocation `._native.native_decide` axiom; -- inspect the printed name rather than relying on its generated suffix. 'mul_comm_bv' depends on axioms: [propext, Classical.choice, Quot.sound, mul_comm_bv._native.bv_decide.ax_1_5]#print axioms mul_comm_bv -- Likewise a per-invocation `._native.bv_decide` axiom.

The last three theorems rely on compiler-generated axioms, one per invocation and named after the tactic; they are classified teaching examples, not conforming proofs. The first theorem uses kernel reduction. Neither tactic choice alone states the cost or foundation of an arbitrary proof.

Proof maintenance: Economy over time comes from four habits, none of which is a global tactic, style, or import mandate. State theorems against stable, elaborated interfaces (the API the definition exports, not its unfolded internals) so a representation change doesn't reopen every proof. Keep hypotheses local as binders rather than as global instances or options. Use automation intentionally: where the produced term matters for replay or readability, name the lemmas the automation may use; where it does not, closing the goal is enough. Match existing library abstractions (module 1 §1.4) so their theorems, instances, and simp sets do the work instead of bespoke ones.

When cost cannot be derived: The examples above explain which computations a proof requires; they do not establish timings. If a remaining performance decision needs measurement, first state why derivation does not settle it, the smallest informative comparison, the pinned inputs, the decision criterion, and a resource budget. Report run-to-run uncertainty when it affects the decision. A profiler observation concerns the toolchain and machine; it does not establish correctness or foundation strength (module 8).

3.3 Elaboration-Time Verification🔗

Requirement: Express the properties an interface enforces in its types and proof obligations, and ensure the resulting terms are kernel-checked. Elaboration constructs those terms and reports unsolved obligations. This checks the program’s reasoning before execution; validating runtime inputs may still require runtime computation.

Strategies:

  1. Dependent Return Types: Functions return proofs along with values.

  2. Propositions as Types: Encode requirements in type signatures.

  3. Indexed Bounds: Use types such as Fin n to require evidence of a bound; n and the index may be runtime inputs.

  4. Witness Types: Return evidence of properties, not just boolean checks.

Example - Proof-Requiring Interfaces (these definitions use computable operations):

/-- Return a value with its proof of property. -/ def findPositive (l : List Nat) : Option {x : Nat // 0 < x ∧ x ∈ l} := match hx : l.find? (0 < ·) with | none => none | some x => have hp := List.find?_some hx some ⟨x, of_decide_eq_true hp, List.mem_of_find?_eq_some hx⟩ /-- Access requires an index whose bound has been established. -/ def safeGet {α : Type} {n : Nat} (v : Vector α n) (i : Fin n) : α := -- The index carries the proof `i.val < n`; this is an API claim, not a runtime-cost claim. v.get i /-- Witness-bearing comparison. -/ inductive CompareResult (a b : Nat) : Type /-- `a` is less than `b`. -/ | lt (h : a < b) : CompareResult a b /-- `a` equals `b`. -/ | eq (h : a = b) : CompareResult a b /-- `a` is greater than `b`. -/ | gt (h : b < a) : CompareResult a b /-- The comparison of `a` and `b`, with the proof of the relation that holds. -/ def compare (a b : Nat) : CompareResult a b := if h : a < b then .lt h else if h : a = b then .eq h else .gt (a:Natb:Nath✝:¬a < bh:¬a = b⊢ b < a All goals completed! 🐙) /-- Type-level state machine whose invalid transitions are type errors. -/ inductive State : Type /-- Waiting to start. -/ | ready /-- Started and not yet finished. -/ | running /-- Finished. -/ | done /-- The allowed transitions between states; a missing edge has no constructor. -/ inductive ValidTransition : State → State → Type /-- Start a ready machine. -/ | start : ValidTransition .ready .running /-- Finish a running machine. -/ | finish : ValidTransition .running .done /-- Reset a finished machine. -/ | reset : ValidTransition .done .ready /-- The target state of an allowed transition. -/ def transition {s₁ s₂ : State} (_ : ValidTransition s₁ s₂) : State := s₂ /-- There is no direct transition from ready to done in this relation. -/ example (t : ValidTransition .ready .done) : False := nomatch t

In findPositive and compare, the search or comparison still executes when the inputs arrive at runtime; the proof fields are erased. findPositive’s return type establishes soundness of a successful result but does not promise success whenever the list contains a positive value. Likewise, ValidTransition describes allowed edges; it does not require callers to use the latest state.

3.4 Foundation Strength: Axioms Are Reported, Never Assumed🔗

Requirement: A conforming proof surface contains no project logical axiom declarations and no sorry, admit, or transitive dependence on sorryAx. Domain assumptions are explicit parameters, hypotheses, or proof-bearing fields. Lean 4 has no constant command. Implemented opaque declarations are classified separately (§3.6).

What a theorem does transitively use of Lean's standard logical axioms is not hidden: it is reported, exactly, per declaration. The report is a dependency statement, not a quality ranking (module 4 §4.5).

/-- A small arithmetic fact, proved by `omega`. -/ theorem two_mul_succ (n : Nat) : 2 * (n + 1) = 2 * n + 2 := n:Nat⊢ 2 * (n + 1) = 2 * n + 2 All goals completed! 🐙 -- The output is exactly this theorem's slice of the trusted base: /-- info: 'two_mul_succ' depends on axioms: [propext, Quot.sound] -/ #guard_msgs in #print axioms two_mul_succ
  • Foundation profiles — kernel-only, choice-free, standard-logical — are defined precisely in module 4 §4.5. The shipped gate (module 7 §7.5) computes each declaration's exact transitive axiom set and its profile mechanically, including through Mathlib dependencies.

  • Compiler-trusting proof mechanisms generate axioms outside the permitted standard logical set. They are classified separately and rejected from conforming positive surfaces. They are never folded into a foundation label (module 7 §7.5).

3.5 Theorems Worth Proving🔗

Principle: Prove the theorems the development’s claims need. Explain each theorem’s mathematical purpose or intended use in its docstring. An unresolved obligation must remain explicitly open or conditional; it must not appear as a completed theorem with sorry or admit. Whether proof terms are erased during compilation is a separate question.

Documentation pattern: State why the result matters, its assumptions, and the exact conclusion. For example, runChecked_compose_success in §3.7 explains how a successful prefix supplies the state its suffix requires. Its theorem names the actual runner and the hypotheses under which the composition succeeds.

It is acceptable to defer a theorem when no current claim needs it. Its statement may remain as an explicitly open Prop definition or a hypothesis in a conditional result (§3.10); it must not be presented as proved.

3.6 Contracts for Executable and Effectful Mechanisms🔗

Requirement: Every material behavior claimed for an owned executable component has explicit contracts checked against its actual Lean definitions. Each contract states exactly the guarantee its mechanism provides on the pinned toolchain. Executable partiality, logical opacity, and unsoundness are different properties and MUST NOT be conflated. This includes optimizations: an optimized implementation MUST meet the same contract, so an optimization preserves the intended result, input domain, error behavior, and relevant ordering of effects; a faster implementation of a different specification is not an optimization of the original contract.

What each mechanism guarantees on the pinned toolchain:

  • Total definitions, including recursion accepted by structural or well-founded termination checking: kernel-checked definitions, total in Lean's logic, open to equational reasoning. Termination-as-checked is not a complexity bound and not a runtime guarantee (§3.2.1).

  • Noncomputable definitions: code generation is omitted for these declarations; the modifier adds no logical axiom by itself. A noncomputable reference alone supplies no executable witness. An executable implementation or compiled caller may be related to that reference by checked correspondence, while replacement and external execution retain their stated trust boundaries (§3.2.4).

  • Pure monadic code (StateM, ReaderT, Except, ...): ordinary total functions; proofs reason by ordinary reduction (example below).

  • IO and runtime execution: an IO α value is a program description run by Lean's runtime. Lean theorems cover the pure core and relations between programs; a theorem about a program or effect model does not by itself establish what happens in the outside world. A conforming executable claim names the split (example below).

  • partial: executable recursion without a kernel termination proof. The logical-facing declaration is opaque (#print shows opaque f; the kernel does not unfold calls) and the mechanism introduces no new logical axiom; dependencies of its type and implementation still require inspection. Opacity is not unsoundness, and an opaque constant proves nothing about its own executions. partial is excluded from conforming proof surfaces (module 7 §7.6).

  • partial_fixpoint (present on the pinned toolchain): a termination clause that, for supported result domains and with checked monotonicity in recursive calls, accepts a potentially non-terminating recursive equation as a safe def, generating one-step unfolding theorems (f.eq_def) from a choice-dependent fixpoint construction. The definition is logically sound within the standard axioms, but kernel reduction does not follow the recursive equation; proofs use the generated equations. Its execution may not terminate. This mechanism is outside the current conforming recursion surface: its generated partial helper does not meet the structural or well-founded authentication required by module 7 §7.4. This restriction is a standard/checker policy, not a claim of logical unsoundness.

  • unsafe: excluded from safe kernel declarations — the kernel rejects a safe declaration that references it — and intended for compiled execution; excluded from conforming proof surfaces (module 7 §7.6).

  • @[implemented_by]: kernel reasoning uses the reference definition while compiled execution runs the replacement. The correspondence is checked only when a Lean theorem states it (f = g, or pointwise, e.g. ∀ xs, f xs = g xs); otherwise the replacement is a trusted boundary.

  • @[csimp]: prefer this proof-backed optimization when the pinned compiler's supported constant-equality form fits. An unconditional equality @reference = @replacement lets compilation replace occurrences without changing kernel reduction. The equality relates the Lean constants; it does not verify an extern, unsafe implementation, or further replacement reached from the target. Imported and chained transformations belong in the separate execution account (§7.6), including choices subsequently overwritten or scoped away. The attribute acts only in the compiler: kernel reduction, decide cost, and the logical definition the theorems are about are unchanged (§3.2.5).

  • @[extern] / FFI: executed behavior comes from external code the Lean declaration does not establish — always a trusted boundary. Primitives resolved from the pinned toolchain's own Init, Std and Lean modules (for example Float arithmetic) are the toolchain's trusted base, as are the replacements and unsafe or partial implementations those modules declare; they are reported as what they are, neither confused with project boundaries nor presented as kernel-checked correspondence.

Continuing services and streams: A continuing service is not required to terminate, and its safety contract does not ask it to. Conforming forms include invariant preservation over arbitrary finite execution prefixes and partial correctness (every produced result satisfies the postcondition). Both are statable over total definitions, as the example below shows. Termination, productivity, progress, and liveness are distinct claims. When claimed, each carries its own stated hypotheses (scheduling or fairness assumptions as explicit parameters) and is never read out of a safety theorem.

Metaprograms: A metaprogram has two distinct objects: the terms it produces, which the kernel checks independently of how they were generated, and the producer's own behavior (which declarations it emits, which goals it closes), which kernel checking of the products does not establish. A sound resulting proof does not prove the producer correct. Claims about the producer are executable Lean definition claims with their own contracts.

Models of effects: proofs about models of effects — a modeled file system, a modeled network — are theorems about the model, distinct from guarantees about the external world. Assumptions are explicit parameters, hypotheses, or proof-bearing interfaces (§3.4), never project logical axioms.

Checker alignment: Foundation labels report transitive logical axiom dependencies (module 4 §4.5). The declaration gate separately computes an execution-coverage account for each owned executable root using the conservative closure defined in module 7. That account distinguishes retained execution edges, candidate transformations, and checked, trusted, or unresolved boundaries. A logical profile pass is not an execution guarantee: an unresolved boundary blocks the affected execution claim, and a trusted boundary must not be presented as unconditionally checked.

Example — Proof-Backed Compiler Optimization:

import Init /-- A reference convenient for stating a property. -/ def referenceStep (n : Nat) : Nat := 1 + n /-- A different executable definition with the same result. -/ def replacementStep (n : Nat) : Nat := n + 1 /-- Exact function equality; no additional premises or native evaluation. -/ @[csimp] theorem referenceStep_eq_replacementStep : referenceStep = replacementStep := funext fun n => Nat.add_comm 1 n /-- Subsequent compilation can use the replacement. -/ def nextStep (n : Nat) : Nat := referenceStep n

This theorem proves function equality for every Nat, using funext and Nat.add_comm. It does not establish a speed improvement. Both definitions use trusted native natural-number arithmetic when compiled.

Example - Pure Monadic Code Reasons by Ordinary Reduction:

/-- Increment-and-report over pure state. -/ def tick : StateM Nat Nat := do let s ← get set (s + 1) pure s /-- The computation unfolds by ordinary reduction. -/ example : tick 0 = (0, 1) := rfl

Example - Safety Over Arbitrary Finite Prefixes (a continuing service):

The authoritative definitions and universal proofs are in audit/Audit/Server.lean. Glossary.Server carries served ≤ cap; init constructs an idle value for every capacity, and validate admits exactly valid raw counts. Its public constructor requires the same proof, so it enforces validity without claiming construction is exclusive to validate.

import Audit.Server open Glossary /-- Initialization establishes the bound by construction, for any capacity. -/ example (cap : Nat) : (Server.init cap).served = 0 ∧ (Server.init cap).cap = cap := ⟨rfl, rfl⟩ /-- The actual update supplies the new proof at the write boundary. -/ example (s : Server) : s.step.served = if s.served < s.cap then s.served + 1 else s.served := Server.step_served s /-- A frame property is separate from the intrinsic bound. -/ example (s : Server) : s.step.cap = s.cap := Server.step_cap s /-- Every finite prefix stays within the starting capacity. -/ example (n : Nat) (s : Server) : (Server.run n s).served ≤ s.cap := Server.run_bounded n s /-- The shell describes an output action; terminal effects remain trusted. -/ def Server.announce (s : Server) : IO Unit := IO.println s!"served {s.served} of {s.cap}"

run_bounded combines the output proof field with run_cap, proved by induction over run. It covers every n : Nat and admitted s : Server, including values constructed directly with a proof. step_served establishes the exact increment/refusal behavior; the bound alone would also allow an idle implementation. These theorems do not establish liveness, productivity, external service behavior, or a rule requiring callers to use the latest state. Reusing a saved immutable value is well-typed. Any stronger usage guarantee must be proved over the actual transition or trace semantics. Each finite run is total; it is not a whole-process termination claim.

Direct invalid construction and an invalid update both fail at their proof obligation:

✗ Rejected by Lean, as intended
import Audit.Server /-- Attempt to build a server that has served more than its capacity. -/ def invalid : Glossary.Server := ⟨1, 0, ⊢ 1 ≤ 0 Tactic `decide` proved that the proposition 1 ≤ 0 is false⊢ 1 ≤ 0⟩

Lean's error at line 4, the checked evidence:

Tactic `decide` proved that the proposition
  1 ≤ 0
is false
✗ Rejected by Lean, as intended
import Audit.Server /-- Attempt to update a server past its capacity. -/ def invalidUpdate : Glossary.Server := { Glossary.Server.init 0 with served := 1, bounded := ⊢ 1 ≤ (Glossary.Server.init 0).cap Tactic `decide` proved that the proposition 1 ≤ (Glossary.Server.init 0).cap is false⊢ 1 ≤ (Glossary.Server.init 0).cap }

Lean's error at line 5, the checked evidence:

Tactic `decide` proved that the proposition
  1 ≤ (Glossary.Server.init 0).cap
is false

Raw counts also cannot be passed directly to the admitted-state API:

✗ Rejected by Lean, as intended
import Audit.Server /-- Attempt to step a raw pair as if it were a server. -/ def rawCrossUse (raw : Nat × Nat) : Glossary.Server := Glossary.Server.step Application type mismatch: The argument raw has type Nat × Nat but is expected to have type Glossary.Server in the application Glossary.Server.step rawraw

Lean's error at line 4, the checked evidence:

Application type mismatch: The argument
  raw
has type
  Nat × Nat
but is expected to have type
  Glossary.Server
in the application
  Glossary.Server.step raw

3.7 A Compositional Method for Complete Program Contracts🔗

A type invariant describes valid results; a functional contract also relates them to the input. For each owned component, state the intended relation before selecting proof tools. The obligations below apply to material claims about that component, not to unrelated mathematical definitions or every implementation helper.

Boundary

Required contract

Pure total function

State the exact input domain, any precondition, and the input–output relation. A restricted domain MUST appear in the API when promised (§3.2.1).

Parser, decoder, admission

Prove accepted-result soundness and input meaning. State and prove rejection behavior and any normalization/default policy. If the API promises success on a class of inputs, prove that completeness; a conditional soundness theorem alone does not deliver it. A registered decision states its direction with a decision kind (§3.8): Regula.Decides for both directions, Regula.DecidesSoundly or Regula.DecidesCompletely for one. A one-way kind is a statement that the other direction is not claimed. A function whose result type depends on its input states its kind about an erasure of that result (§3.8). A function the project registers with @[regula_decision] MUST have such a registration or a Decidable result type (§3.8).

Update

Establish the invariant at the write boundary, the intended change, and frame conditions for components promised unchanged.

Composition

Show that intermediate postconditions supply subsequent preconditions. State and prove the chosen state/error semantics, including what state an error exposes and whether the continuation runs.

Fold or loop

Prove the promised relation to the processed input. An inductive invariant is a common method: establish it initially, preserve it at each step, and derive the final relation. Reuse a library theorem or another checked argument when it already proves the claim. Separate partial correctness from termination/total correctness.

Effect interpretation

Keep a pure immutable core with an explicit interpreter/IO shell where pragmatic. Identify the actual proved definitions and modeled relation, and the external runtime assumptions at each crossed boundary (§3.6).

Every required relation MUST be proved about the actual definitions used by the application (or transferred through exact checked correspondence). Reuse proof-bearing construction and library lemmas; do not create a parallel implementation and assume equivalence. A finite fold is total in Lean's logic; arbitrary finite-prefix safety for a continuing service does not assert process termination, progress, fairness, or liveness.

Why the invariant is insufficient. This always-rejecting implementation satisfies its refined return type but violates positive-input completeness:

import Regula.Contract /-- Every possible successful result would be positive, but there are none. -/ def rejectAll : Nat → Option {n : Nat // 0 < n} := fun _ => none example : ¬ (∀ n, 0 < n → (rejectAll n).isSome = true) := ⊢ ¬∀ (n : Nat), 0 < n → (rejectAll n).isSome = true h:∀ (n : Nat), 0 < n → (rejectAll n).isSome = true⊢ False h:∀ (n : Nat), 0 < n → (rejectAll n).isSome = truebad:(rejectAll 1).isSome = true⊢ False All goals completed! 🐙 /-- `rejectAll` accepts no input. -/ theorem rejectAll_refuses (n : Nat) : ¬ (rejectAll n).isSome = true := n:Nat⊢ ¬(rejectAll n).isSome = true All goals completed! 🐙 /-- So it is not a complete decision of positivity, which `1` satisfies. -/ example : ¬ Regula.DecidesCompletely (·.isSome = true) (fun n => 0 < n) rejectAll := Regula.DecidesCompletely.not_of_refuses_all rejectAll_refuses ⟨1, ⊢ 0 < 1 All goals completed! 🐙⟩ /-- Nor a sound decision of any specification: it has no accepted input. -/ example (spec : Nat → Prop) : ¬ Regula.DecidesSoundly (·.isSome = true) spec rejectAll := Regula.DecidesSoundly.not_of_refuses_all rejectAll_refuses

The last two statements use the decision kinds of §3.8. A soundness theorem alone, ∀ n, (rejectAll n).isSome = true → 0 < n, holds of rejectAll vacuously; the sound kind also requires an input the function accepts, and rejectAll has none.

The intended admission in lean/AuditApp/Limiter.lean instead has admit_exact. For every natural capacity, a positive input returns the idle state at exactly that capacity, and zero returns none. It performs no normalization. admit_sound alone would not exclude always rejecting or substituting a different positive capacity. At the CLI boundary, requestedCapacity_exact gives the separate policy: apply Lean's String.toNat? to the first argument, default to 2 on missing/unparsable input, and ignore trailing arguments; admission then rejects parsed zero. The parser's meaning is that pinned library definition, not a theorem about terminal input or an unspecified external encoding.

Worked state/error program. The limiter carries inUse ≤ capacity. Its grant_some and grant_none prove exact increment/frame and refusal behavior; step_capacity supplies the capacity frame for release and reset as well. run_capacity and run_replicate_grant retain the total fold's frame and exact successful-burst result. run keeps going after a refused grant. The strict runChecked instead sequences checkedStep with ordinary monadic bind and stops at the first refusal.

Its stack is ExceptT Unit (StateM Limiter) Unit, which runs as Limiter → (Except Unit Unit × Limiter). State is returned on either outcome. A refused operation preserves its input state; earlier successful updates remain, and the suffix is not run (checkedStep_exact, runChecked_cons, runChecked_error). This is retention, not rollback to the script's initial state. Conversely, StateT Limiter (Except Unit) has result shape Except Unit (α × Limiter) and exposes no state on error; that type alone would not specify an application's recovery policy or undo external effects.

Fits ops l states that every grant has space at the state reached by its preceding operations, using the total step. runChecked_success is an equivalence: success occurs exactly for a fitting script, and its returned state equals run ops l. The successful prefix invariant is Fits prefix initial ∧ current = run prefix initial. runChecked_append threads that current state to the suffix on success, or retains the prefix error/state; runChecked_error identifies the successful prefix immediately before the first full grant. runChecked_capacity frames capacity in both cases, so the returned state's intrinsic bound is also relative to the original capacity.

import AuditApp.Limiter open AuditApp /-- Positive admission allows a real grant, universally over positive capacities. -/ example (cap : Nat) (h : 0 < cap) : Fits [.grant] ⟨cap, 0, Nat.zero_le cap⟩ := ⟨fun _ => h, trivial⟩ /-- The intermediate post-state is the input to the next script's precondition. -/ example (xs ys : List Op) (l : Limiter) (hx : Fits xs l) (hy : Fits ys (run xs l)) : runChecked (xs ++ ys) l = (.ok (), run ys (run xs l)) := runChecked_compose_success xs ys l hx hy /-- Error exposes exactly the successful prefix before a full grant. -/ example (ops : List Op) (l final : Limiter) : runChecked ops l = (.error (), final) ↔ ∃ before after, ops = before ++ .grant :: after ∧ Fits before l ∧ final = run before l ∧ final.inUse = final.capacity := runChecked_error ops l final

executeChecked_exact composes admission with this runner. Main passes required_contracts to executeChecked, so required admission, update, input policy, success/refusal, and composition propositions are proof obligations about the same core it calls (§7.5). The shell describes success/refusal output using the returned result and state. The proofs do not establish terminal effects, native arithmetic correctness, complexity, empirical benefit, or external liveness. The default demonstration reaches a refusal; AuditApp.demo_checked_error derives its stopped state from the universal contract.

Choose the smallest proof interface. The Lean 4.34.0 library provides Std.Do.Triple (precondition entails weakest precondition), Std.Do.Triple.bind for composition, WPMonad instances for StateT/ExceptT, and Std.Tactic.Do's mvcgen. Use these when they simplify verification conditions; no tactic is mandatory. This example reuses the standard transformers and proves exact outcome equations by reduction and list induction, avoiding an additional predicate-transformer encoding of the same equations. For a larger monadic program, Triple.bind connects the first result's postcondition to the continuation's precondition while retaining explicit exceptional postconditions.

Local let mut, a for loop, or an efficient array representation does not itself imply external effects or unsafe execution. Inspect the elaborated total function and prove its relation; efficient representations remain permitted with their boundary/correspondence proofs. For example, local rebinding below denotes a pure function for every Nat:

/-- Local syntax, immutable mathematical input/output semantics. -/ def incrementLocal (n : Nat) : Nat := Id.run do let mut current := n current := current + 1 return current example (n : Nat) : incrementLocal n = n + 1 := rfl

3.8 Delivering Executable Witnesses with Required Evidence🔗

When an API promises to produce a witness or make a decision at runtime, it MUST supply an executable definition and the required relation about that same definition. A theorem ∃ y, R x y establishes existence in Lean's logic; it does not by itself deliver executable data. Existential mathematical theorems remain legitimate. Likewise, a Decidable argument used for execution must have a computable producer (§3.2.4).

The public Regula.Contract import provides ExecutableContract f R : Prop, whose evidence field requires exactly R f. Its run requires that evidence and reduces to f (ExecutableContract.run_eq); compilation erases the proof. An application can use this interface or a richer one such as AuditApp.RequiredContracts.

Contract adequacy remains semantic. Replacing a required relation with fun _ => True loses that requirement. By contrast, a dependent function type may already enforce the entire intended relation: every f : (n : Nat) → {m : Nat // m = n + 1} supplies the stated successor property. Such evidence by construction is sufficient for that claim. Require additional input, success/refusal, frame, or composition relations only where the intended behavior needs them (§3.7).

An executable may have a classical correctness proof under Standard-Logical. Proof erasure does not turn its runtime data into a noncomputable construction:

import Regula.Contract /-- The successor of a natural number. -/ def successor (n : Nat) : Nat := n + 1 /-- `successor` returns `n + 1` for every `n`, proved classically for illustration. -/ theorem successor_contract : Regula.ExecutableContract successor (fun f => ∀ n, f n = n + 1) := ⟨Classical.byContradiction (fun h => h (fun _ => rfl))⟩ /-- `successor`, run through its registered contract. -/ def next (n : Nat) : Nat := successor_contract.run n /-- info: 'successor_contract' depends on axioms: [propext, Classical.choice, Quot.sound] -/ #guard_msgs in #print axioms successor_contract example (n : Nat) : next n = n + 1 := rfl

The classical proof here deliberately illustrates the distinction; the direct proof ⟨fun _ => rfl⟩ is simpler. The shipped standalone adopter uses that direct proof and selects Kernel-only. Its ordinary lake build also inspects registered executable roots, the selected transitive foundation profile, and execution boundaries (§7.11).

Omitting evidence while keeping the requirement fails at the proof-bearing field:

✗ Rejected by Lean, as intended
import Regula.Contract /-- The successor of a natural number. -/ def successor (n : Nat) := n + 1 theorem missing : Regula.ExecutableContract successor (fun f => ∀ n, f n = n + 1) Fields missing: `evidence`where

Lean's error at line 6, the checked evidence:

Fields missing: `evidence`

Existence evidence cannot fill a promised data result:

✗ Rejected by Lean, as intended
theorem exists_eq_add_one (n : Nat) : ∃ m : Nat, m = n + 1 := ⟨n + 1, rfl⟩ /-- Attempt to compute a successor from the existence proof alone. -/ def promised (n : Nat) : {m : Nat // m = n + 1} := Type mismatch exists_eq_add_one n has type ∃ m, m = n + 1 of sort `Prop` but is expected to have type { m // m = n + 1 } of sort `Type`exists_eq_add_one n

Lean's error at line 3, the checked evidence:

Type mismatch
  exists_eq_add_one n
has type
  ∃ m, m = n + 1
of sort `Prop` but is expected to have type
  { m // m = n + 1 }
of sort `Type`

Using choice to extract the data makes it a noncomputable specification instead:

✗ Rejected by Lean, as intended
/-- Attempt to compute a successor by choosing a witness of its existence. -/ def failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Classical.choose', which is 'noncomputable'promised (n : Nat) : Nat := Classical.choose (show ∃ m : Nat, m = n + 1 from ⟨n + 1, rfl⟩)

Lean's error at line 2, the checked evidence:

failed to compile definition, consider marking it as 'noncomputable' because it depends on 'Classical.choose', which is 'noncomputable'

Marking that definition noncomputable permits the logical definition; it does not deliver the executable promise. The build linter rejects a registered noncomputable root even under Standard-Logical. Conversely, classical evidence alone does not reject a computable root. Neither a successful build nor a Choice-Free label proves kernel normalization, witness extraction from arbitrary existence theorems, compiler correctness, or native/FFI behavior.

Decision kinds🔗

A function that acts as a checker (a parser, decoder, validator or admission function) is a decision: some of its results accept the input and the others refuse it. A soundness theorem alone admits the function that refuses every input, and a completeness theorem alone admits the function that accepts every input (§3.7). A one-way guarantee is often the right choice: a refusal that fails closed needs only soundness. What a reader needs is to tell that choice from an omission.

Regula.Contract provides three requirements for the condition of an ExecutableContract, each over an acceptance predicate accepts : ρ → Prop, a specification spec : α → Prop and the function f : α → ρ:

Kind

Fields

Not claimed

DecidesSoundly accepts spec f

sound : ∀ x, accepts (f x) → spec x and accepted : ∃ x, accepts (f x)

Completeness: f may refuse inputs that satisfy spec.

DecidesCompletely accepts spec f

complete : ∀ x, spec x → accepts (f x) and refused : ∃ x, ¬ accepts (f x)

Soundness: f may accept inputs that do not satisfy spec.

Decides accepts spec f

All four: it extends both.

Neither direction is left open.

The fields are the statements, so Lean's kernel checks the direction, and a one-way guarantee can be registered only under the structure that names it. The witnesses are about f: accepted excludes the function that refuses every input, which is sound for every specification, and refused excludes the function that accepts every input, which is complete for every specification. A satisfiable spec alone would not exclude the first. accepts is a parameter because acceptance has no canonical polarity: an Option result accepts on some for a parser and on none for a failure report.

A function of several arguments is decided on the product of its arguments, through Function.uncurry. That function is a bijection between α → β → ρ and α × β → ρ (Function.curry_uncurry, Function.uncurry_curry), so the kind's quantifiers over the pairs are the quantifiers over both arguments, and the registered implementation remains the constant its callers run:

import Regula.Contract /-- Whether `n` is positive. -/ def positive (n : Nat) : Bool := decide (0 < n) /-- `positive` accepts exactly the positive numbers: it accepts `1` and refuses `0`. -/ theorem positive_decides : Regula.ExecutableContract positive (Regula.Decides (· = true) fun n => 0 < n) := ⟨{ sound := fun _ accepted => of_decide_eq_true accepted accepted := ⟨1, ⊢ positive 1 = true All goals completed! 🐙⟩ complete := fun _ holds => decide_eq_true holds refused := ⟨0, ⊢ ¬positive 0 = true All goals completed! 🐙⟩ }⟩ /-- Whether `n` is an even number below four. -/ def smallEven (n : Nat) : Bool := decide (n % 2 = 0) && decide (n < 4) /-- `smallEven` accepts only even numbers. The kind does not claim that it accepts every even number, and it refuses `4`. -/ theorem smallEven_decidesSoundly : Regula.ExecutableContract smallEven (Regula.DecidesSoundly (· = true) fun n => n % 2 = 0) := ⟨{ sound := fun n accepted => n:Nataccepted:smallEven n = true⊢ n % 2 = 0 n:Nataccepted:n % 2 = 0 ∧ n < 4⊢ n % 2 = 0 All goals completed! 🐙 accepted := ⟨0, ⊢ smallEven 0 = true All goals completed! 🐙⟩ }⟩ /-- Whether `n` is below `limit`. -/ def within (limit n : Nat) : Bool := decide (n < limit) /-- `within` accepts exactly the pairs whose second component is below the first. -/ theorem within_decides : Regula.ExecutableContract within (fun check => Regula.Decides (· = true) (fun input : Nat × Nat => input.2 < input.1) (Function.uncurry check)) := ⟨{ sound := fun _ accepted => of_decide_eq_true accepted accepted := ⟨(1, 0), ⊢ Function.uncurry within (1, 0) = true All goals completed! 🐙⟩ complete := fun _ holds => decide_eq_true holds refused := ⟨(0, 0), ⊢ ¬Function.uncurry within (0, 0) = true All goals completed! 🐙⟩ }⟩

A function with an argument whose type depends on an earlier argument, or with a type, instance or proof argument, is decided on a structure whose fields are its arguments: the kind is stated about the function that applies f to every field of the structure, each field once and in the order of the fields, as fun input : Input => f input.a input.b input.c. The type of a field can depend on an earlier field. For two arguments the structure can be a pair (Prod, Sigma, PSigma or Subtype), and Function.uncurry f is this form at Prod (Regula.uncurry_eq_fields). A kind about that function is the kind about f when every tuple of arguments is the fields of some value of the structure (Regula.Decides.of_packing, with its one-way forms). A type with one constructor and no index has that property: the constructor takes every tuple of fields to a value of the type, and Lean's kernel reduces each projection of that value to the field. A function that fixes an argument or gives a field twice decides f on part of its domain; a structure with a field that f does not take, such as a proof about the other fields, can restrict the domain; and a type with an index holds only the tuples whose fields compute that index. None of these has this form, and the build linter rejects such a registration (§7.11). A kind about a function with universe parameters MUST be stated at those parameters, so that it holds of every universe instance of the function; its witnesses are then inputs at every universe level.

A kind MUST be stated about the function applied to every one of its arguments: the result type of the function that is decided is not a function type. This restriction is structural, and it is conservative. With an argument left, the result is a function, and how much of it the kind constrains depends on the acceptance predicate. An acceptance predicate that reads the result at one fixed value b of that argument gives exactly the kind of the slice of the function at b (Regula.Decides.iff_slice), which says nothing of the function at another value. An acceptance predicate that quantifies over that argument can constrain the function at every value. The build linter does not read the acceptance predicate: it rejects every kind whose result type is a function type after every definition is unfolded (§7.11), a kind of the second form included. The remedy is the same for both forms: supply the argument, with one more Function.uncurry (Regula.packing_uncurry_covers) or as one more field of the structure.

A function whose result type depends on its arguments is decided through an erasure of its result: one of two functions of Regula.Contract, each of which forgets the part of the result whose type depends on the input. Regula.Dependent.isSome f is fun x => (f x).isSome for f : ∀ x, Option (payload x), and Regula.Dependent.isOk f is fun x => (f x).isOk for f : ∀ x, Except (ε x) (payload x). The kind is one of the three of the table, stated about the erased function, as Regula.Decides (· = true) spec (Regula.Dependent.isOk f). The type of its acceptance predicate has no payload in it, so the predicate reads the erased result and nothing else, and the witnesses stay statements about f. The erased result corresponds to the result by Regula.Dependent.isSome_eq_true_iff and Regula.Dependent.isOk_eq_true_iff. An acceptance predicate stated for every payload type does not have that property, and neither does an erasure that a registration supplies: each can read the payload type, and "the payload type has a value" is the specification of a proof-carrying payload, whatever the function returns. So there is no kind with such a predicate, and the build linter reads these two erasures and no other (§7.11). A result of a subtype type that depends on the input, {r : ρ // P x r}, has no kind until issue 243 is decided: an erasure that returns the value of the subtype would keep a free acceptance predicate on a data value. Such a function returns Decidable p, or is registered with an ordinary requirement.

import Regula.Contract /-- The number itself with a proof that it is positive, or nothing. -/ def positive? (n : Nat) : Option {m : Nat // m = n ∧ 0 < m} := if h : 0 < n then some ⟨n, rfl, h⟩ else none /-- `positive?` returns a number exactly for a positive input. -/ theorem positive?_isSome (n : Nat) : Regula.Dependent.isSome positive? n = true ↔ 0 < n := n:Nat⊢ Regula.Dependent.isSome positive? n = true ↔ 0 < n n:Nat⊢ (if h : 0 < n then some ⟨n, ⋯⟩ else none).isSome = true ↔ 0 < n n:Nath✝:0 < n⊢ (some ⟨n, ⋯⟩).isSome = true ↔ 0 < nn:Nath✝:¬0 < n⊢ none.isSome = true ↔ 0 < n n:Nath✝:0 < n⊢ (some ⟨n, ⋯⟩).isSome = true ↔ 0 < nn:Nath✝:¬0 < n⊢ none.isSome = true ↔ 0 < n All goals completed! 🐙 /-- `positive?` accepts exactly the positive numbers: it accepts `1` and refuses `0`. Its result type depends on its argument, so the kind is stated about whether it returns a value. -/ theorem positive?_decides : Regula.ExecutableContract positive? (fun parse => Regula.Decides (· = true) (fun n => 0 < n) (Regula.Dependent.isSome parse)) := ⟨.of_iff positive?_isSome ⟨1, (positive?_isSome 1).mpr (⊢ 0 < 1 All goals completed! 🐙)⟩ ⟨0, fun accepted => absurd ((positive?_isSome 0).mp accepted) (accepted:Regula.Dependent.isSome positive? 0 = true⊢ ¬0 < 0 All goals completed! 🐙)⟩⟩ /-- Whether an index below `limit + 1` is below three. -/ def low (limit : Nat) (index : Fin (limit + 1)) : Bool := decide (index.val < 3) /-- `low` accepts exactly the dependent pairs of a limit and an index below three. -/ theorem low_decides : Regula.ExecutableContract low (fun check => Regula.Decides (· = true) (fun input => input.2.val < 3) (fun input : (limit : Nat) ×' Fin (limit + 1) => check input.1 input.2)) := ⟨{ sound := fun _ accepted => of_decide_eq_true accepted accepted := ⟨⟨0, 0⟩, ⊢ low ⟨0, 0⟩.fst ⟨0, 0⟩.snd = true All goals completed! 🐙⟩ complete := fun _ holds => decide_eq_true holds refused := ⟨⟨3, 3⟩, ⊢ ¬low ⟨3, 3⟩.fst ⟨3, 3⟩.snd = true All goals completed! 🐙⟩ }⟩ universe u /-- Whether a list of any element type has a member. -/ def inhabited {α : Type u} (items : List α) : Bool := !items.isEmpty /-- `inhabited` accepts exactly the pairs of a type and a nonempty list of it, at every universe level: the witnesses are lists of `PUnit`. -/ theorem inhabited_decides : Regula.ExecutableContract @inhabited.{u} (fun check => Regula.Decides (· = true) (fun input => input.2 ≠ []) (fun input : (α : Type u) × List α => @check input.1 input.2)) := ⟨.of_iff (fun input => input:(α : Type u) × List α⊢ inhabited input.snd = true ↔ input.snd ≠ [] input:(α : Type u) × List α⊢ inhabited [] = true ↔ [] ≠ []input:(α : Type u) × List αhead✝:input.fsttail✝:List input.fst⊢ inhabited (head✝ :: tail✝) = true ↔ head✝ :: tail✝ ≠ [] input:(α : Type u) × List α⊢ inhabited [] = true ↔ [] ≠ []input:(α : Type u) × List αhead✝:input.fsttail✝:List input.fst⊢ inhabited (head✝ :: tail✝) = true ↔ head✝ :: tail✝ ≠ [] All goals completed! 🐙) ⟨⟨PUnit, [PUnit.unit]⟩, rfl⟩ ⟨⟨PUnit, []⟩, ⊢ ¬inhabited ⟨PUnit, []⟩.snd = true All goals completed! 🐙⟩⟩ /-- An index below `limit + 1` that is below three, or below four when `wide`, with a proof that it is the supplied index. -/ def lowIndex (limit : Nat) (index : Fin (limit + 1)) (wide : Bool) : Except String {found : Fin (limit + 1) // found = index} := if index.val < (if wide then 4 else 3) then .ok ⟨index, rfl⟩ else .error "not low" /-- The arguments of `lowIndex`, as the fields of one structure. -/ structure LowIndexInput where /-- The bound of the index. -/ limit : Nat /-- The index, below `limit + 1`. -/ index : Fin (limit + 1) /-- Whether the wider bound applies. -/ wide : Bool /-- `lowIndex` accepts exactly an index below its bound: the three arguments are the fields of `LowIndexInput`, and the result type depends on the first two, so the kind is stated about whether it succeeds. -/ theorem lowIndex_decides : Regula.ExecutableContract lowIndex (fun admit => Regula.Decides (· = true) (fun input : LowIndexInput => input.index.val < (if input.wide then 4 else 3)) (Regula.Dependent.isOk fun input => admit input.limit input.index input.wide)) := ⟨.of_iff (fun input => input:LowIndexInput⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) input = true ↔ ↑input.index < if input.wide = true then 4 else 3 input:LowIndexInputlow:↑input.index < if input.wide = true then 4 else 3⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) input = true ↔ ↑input.index < if input.wide = true then 4 else 3input:LowIndexInputlow:¬↑input.index < if input.wide = true then 4 else 3⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) input = true ↔ ↑input.index < if input.wide = true then 4 else 3 input:LowIndexInputlow:↑input.index < if input.wide = true then 4 else 3⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) input = true ↔ ↑input.index < if input.wide = true then 4 else 3input:LowIndexInputlow:¬↑input.index < if input.wide = true then 4 else 3⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) input = true ↔ ↑input.index < if input.wide = true then 4 else 3 All goals completed! 🐙) ⟨⟨0, 0, false⟩, ⊢ Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) { limit := 0, index := 0, wide := false } = true All goals completed! 🐙⟩ ⟨⟨3, 3, false⟩, ⊢ ¬Regula.Dependent.isOk (fun input => lowIndex input.limit input.index input.wide) { limit := 3, index := 3, wide := false } = true All goals completed! 🐙⟩⟩

Nested dependent pairs are not a form for three or more arguments. Each projection of a pair carries the pair's type, so the elaborated statement grows by a large factor with each argument, whichever way the pairs nest. A structure declared for the arguments has projections of constant size.

A kind is a theorem about the present definitions. It compares accepts (f x) with spec x. When spec and f both call a function, a change of that function changes the two sides together. Each direction then holds or fails as its proof does, and a proof that goes through the shared function on the two sides can stay valid although the meaning changed. So the kind does not establish that the shared function is the intended one. Where the shared function has a result of Bool or BEq, the specification MUST state the proposition that the function decides: the function decides that proposition, or a theorem connects the two (RG1009). The build linter refuses a decision registration whose specification reaches such a function, outside Lean's own library and other than a projection function of a structure, that the implementation or the acceptance predicate also reaches, at any depth (§7.11). A test that only the declaration of the input type names, such as a test that an invariant of a field of the input states, is not part of the specification at any input: a change of the test changes which inputs there are, on the two sides together, and the comparison at an input is the one that it was. So the linter does not enter the declaration of the input type from the input, and its report names such a test as shared only through the type. A specification that names the input type in any other way, or that states the invariant, reaches the test, and it is counted. It compares names, so it does not find a copy of a test under a second name. A shared function with another result type is not refused: the linter names it in its report where the specification reaches it first, and what it computes remains review.

import Regula.Contract /-- Whether `n` is below four. -/ def low (n : Nat) : Bool := decide (n < 4) /-- `low` accepts exactly the numbers below four. -/ theorem low_iff (n : Nat) : low n = true ↔ n < 4 := n:Nat⊢ low n = true ↔ n < 4 All goals completed! 🐙 /-- Whether `n` is an even number below four: it calls `low`. -/ def lowEven (n : Nat) : Bool := low n && decide (n % 2 = 0) /-- `lowEven` accepts exactly the even numbers below four. The specification states `n < 4`, the proposition that `low` decides, and does not call `low`. -/ theorem lowEven_decides : Regula.ExecutableContract lowEven (Regula.Decides (· = true) fun n => n < 4 ∧ n % 2 = 0) := ⟨.of_iff (fun n => n:Nat⊢ lowEven n = true ↔ n < 4 ∧ n % 2 = 0 All goals completed! 🐙) ⟨0, ⊢ lowEven 0 = true All goals completed! 🐙⟩ ⟨1, ⊢ ¬lowEven 1 = true All goals completed! 🐙⟩⟩ /-- A `low` that accepts every number. -/ def lax (_ : Nat) : Bool := true /-- With the specification stated through the helper, the same statement and the same witnesses hold of a helper that accepts every number: the kind does not establish that the helper tests anything. -/ example : Regula.Decides (· = true) (fun n => lax n = true ∧ n % 2 = 0) (fun n => lax n && decide (n % 2 = 0)) := .of_iff (fun n => n:Nat⊢ (lax n && decide (n % 2 = 0)) = true ↔ lax n = true ∧ n % 2 = 0 All goals completed! 🐙) ⟨0, ⊢ (lax 0 && decide (0 % 2 = 0)) = true All goals completed! 🐙⟩ ⟨1, ⊢ ¬(lax 1 && decide (1 % 2 = 0)) = true All goals completed! 🐙⟩

A soundness proof without its witness does not inhabit the sound kind:

✗ Rejected by Lean, as intended
import Regula.Contract /-- Whether `n` is positive. -/ def positive (n : Nat) : Bool := decide (0 < n) theorem positive_sound : Regula.ExecutableContract positive (Regula.DecidesSoundly (· = true) fun n => 0 < n) := ⟨Fields missing: `accepted` Hint: Add missing fields: ̲ ̲ ̲ ̲ ̲ ̲a̲c̲c̲e̲p̲t̲e̲d̲ ̲:̲=̲ ̲_̲{ sound := fun _ accepted => of_decide_eq_true accepted }⟩

Lean's error at line 7, the checked evidence:

Fields missing: `accepted`

Hint: Add missing fields:
  
  ̲ ̲ ̲ ̲ ̲ ̲a̲c̲c̲e̲p̲t̲e̲d̲ ̲:̲=̲ ̲_̲

A project declares that a function is a decision by registering it with @[regula_decision], which Regula.Decision provides. The registration is a requirement: a registered function MUST be the implementation of a decision registration in its inventory, or in a surface that the project's manifest relates to its own, or have a result type of the form Decidable p, and the build linter rejects a registered function with neither (§7.11). Deleting the theorem while the function stays registered therefore fails; for an unregistered function it would delete the requirement together with the evidence.

import Regula.Contract import Regula.Decision /-- Whether `n` is positive. -/ @[regula_decision] def positive (n : Nat) : Bool := decide (0 < n) /-- `positive` accepts exactly the positive numbers: it accepts `1` and refuses `0`. -/ theorem positive_decides : Regula.ExecutableContract positive (Regula.Decides (· = true) fun n => 0 < n) := ⟨{ sound := fun _ accepted => of_decide_eq_true accepted accepted := ⟨1, ⊢ positive 1 = true All goals completed! 🐙⟩ complete := fun _ holds => decide_eq_true holds refused := ⟨0, ⊢ ¬positive 0 = true All goals completed! 🐙⟩ }⟩ /-- A registered decision whose result carries its proof: it needs no contract. -/ @[regula_decision] def positive? (n : Nat) : Decidable (0 < n) := inferInstance

A kind states which directions hold against the written specification. These remain semantic review: whether spec is the intended specification, and whether it is independent of f in substance (a copy of the implementation under another name satisfies both directions, and so does a copy of a helper); what each shared function that the report names computes; whether every function that acts as a checker is registered as a decision; whether a one-way kind should have been two-way; and which value an accepting result carries, which needs a dependent result type or a further requirement. Whether accepts is the intended reading of a result is semantic review too. A constant function has no two-way kind (Regula.Decides.not_of_constant): a sound kind about one proves the specification of every input (Regula.DecidesSoundly.spec_of_constant), and a complete kind about one refutes it of every input (Regula.DecidesCompletely.not_spec_of_constant). But when the result of f determines its input, as the result of the identity function does, an acceptance predicate can restate the specification: Regula.Decides spec spec id holds of every specification that some input satisfies and some input does not. A result of type Decidable p already carries both directions by construction (§3.2.4) and needs no kind for that claim. A result type whose dependency on the input neither erasure removes, such as an inductive family indexed by the input or a subtype type that depends on the input, has no kind: return Decidable p, or register an ordinary requirement. A kind also requires an input with a proof, so a function for which no accepted input can be given has no sound kind, and one for which no refused input can be given has no complete kind. The build linter reads the kind of each registration and reports it with the direction a one-way kind leaves open (§7.11).

3.9 Stateful Refinement and Finite-Prefix Safety🔗

A stateful safety transfer MUST identify the concrete and abstract semantics, the initial conditions, and the checked correspondence that transfers the claimed property. The forward-simulation method below uses state domains C and A, step relations, an abstract invariant, and a relation R : C → A → Prop. Other checked transfer arguments may use different obligations; they must still establish the stated claim.

For this method, prove initialization, abstract invariant preservation, and that the invariant together with R implies the concrete property. Its simulation hypothesis covers every related pair and every concrete successor: R c a → StepC c c' → ∃ a', StepA* a a' ∧ R c' a'. The abstract successor is existential, even when the abstract system is nondeterministic; this is neither reverse simulation nor equivalence of behaviors.

Define StepA* explicitly. Here StepA* is core's transitive closure Relation.TransGen StepA: one edge (single), or a finite path extended by one edge (tail). Every match therefore has at least one abstract edge, and stuttering is a property of the abstract relation, not of the closure: an abstract relation with an idle edge matches a concrete edge that changes nothing. Mathlib's Relation.ReflTransGen StepA puts the zero-step case in the closure instead, and the Mathlib integration package states this refinement with it over an abstract relation that has no idle edge. AuditApp.Refinement.finite_transfer proves the reusable transfer by induction over Relation.TransGen StepC: given a related initial pair and the initial abstract invariant, each concrete path of one or more edges ends at a state with a reachable abstract witness, the relation, the abstract invariant, and the concrete safety property. Before any concrete edge the claim is the soundness hypothesis applied to the initial pair, which needs no closure. Its types and relations are parameters; it assumes neither deterministic transitions nor an executable procedure that selects matching abstract paths. The caller supplies initialization; it is not silently assumed to exist.

The concrete step MUST be the actual executable definition or have exact checked correspondence to it. If you claim observable agreement, state the observations and prove that agreement at the promised granularity. Matching endpoints after several abstract steps does not establish equality of intermediate observations or labeled traces.

Worked limiter refinement. The authoritative instance is audit/Audit/Refinement.lean, reusing lean/AuditApp/Limiter.lean without another interpreter:

Object

Exact meaning

Concrete state c : Limiter

Natural capacity, natural inUse, and intrinsic inUse ≤ capacity.

Abstract state a : Nat

Free slots; Inv cap a is a ≤ cap. Raw numbers are a convenient relational domain; the bound holds for reachable states, not all natural numbers.

R cap c a

c.capacity = cap ∧ c.inUse + a = cap. It fixes capacity and relates occupied to free slots.

StepA cap a a'

One of a' = a (idle), 0 < a ∧ a' = a - 1, or a < cap ∧ a' = a + 1. Several choices may be enabled.

StepC c c'

∃ op, c' = AuditApp.step c op, using the existing executable dispatcher.

Initialization

initial_related proves R cap c cap ∧ Inv cap cap whenever actual admit cap = some c. admit_exact establishes that this occurs for every positive capacity.

Preservation and simulation

preserve covers all three abstract edges; simulation covers every related pair and concrete edge. Grants consume one free slot; releases return one; refused grants and idle releases stutter, matching the idle edge; reset uses free_to_capacity, an idle edge followed by a finite sequence of releases.

Transfer and observations

sound gives c.capacity = cap ∧ c.inUse ≤ cap; observations gives (c.capacity, c.inUse) = (cap, cap - a) at related endpoints.

The intrinsic bound already guarantees valid concrete values. The refinement adds the fixed-capacity relation and a matching abstract path. Indeed, this particular R already implies the concrete bound; sound retains the invariant premise to instantiate the general method, without pretending that premise is necessary here. Abstract initialization and preservation remain separately proved. Natural subtraction truncates at zero: the positive allocation guard and the relation justify the decrement, and observations derives its subtraction equation from the sum relation. All counts are unbounded Lean naturals; no fixed-width wrapping or real-number approximation is assumed.

import Audit.Refinement open AuditApp AuditApp.Refinement /-- Non-vacuity: every positive capacity admits the stated initial pair. -/ example (cap : Nat) (h : 0 < cap) : admit cap = some ⟨cap, 0, Nat.zero_le cap⟩ := cap:Nath:0 < cap⊢ admit cap = some { capacity := cap, inUse := 0, bounded := ⋯ } All goals completed! 🐙 /-- Universal simulation, with an existential finite abstract match. -/ example (cap a : Nat) (c c' : Limiter) (hr : R cap c a) (hs : StepC c c') : ∃ a', Relation.TransGen (StepA cap) a a' ∧ R cap c' a' := simulation hr hs /-- Every finite prefix, including refusal and the empty prefix, has an abstract witness and is safe. -/ example (cap : Nat) (c : Limiter) (h : admit cap = some c) (ops : List Op) (n : Nat) : ∃ a, Relation.TransGen (StepA cap) cap a ∧ R cap (runChecked (ops.take n) c).2 a ∧ Inv cap a ∧ ((runChecked (ops.take n) c).2.capacity = cap ∧ (runChecked (ops.take n) c).2.inUse ≤ cap) := prefix_safe h ops n

reachable_safe covers every concrete path of one or more edges, not a selected set of scripts or a bounded search; admitted_safe covers the admitted state before any edge. run_path links the total fold of every nonempty script to those paths; runChecked_path uses the strict runner's exact state/error equation, including retained state on refusal, which refused_edge shows is a concrete edge that changes nothing. prefix_safe combines both cases for every ops : List Op and n : Nat, the empty prefix included; taking beyond the list length selects the whole list. The existing executeChecked_exact connects positive admission to that same strict runner used by Main. This refinement observes only the returned capacity and occupancy; the runner's success/error contract remains §3.7, and terminal effects remain trusted.

A safety transfer MUST NOT be read as progress, fairness, productivity, termination of a continuing process, or liveness. Unlimited stuttering or starvation can preserve safety; any such additional claim needs its own explicit hypotheses and proof. Totality of each finite Lean runner does not establish a matching infinite abstract execution with progress. Theorems about these Lean definitions also do not verify their compiled natural arithmetic, code generation, runtime, external adapters, or scheduling (§3.6).

3.10 Research Statements: Adequacy, Conditional Completeness, and Open Targets🔗

A kernel-checked proof establishes its elaborated proposition under its explicit hypotheses and transitive axiom dependencies. It establishes nothing about whether that proposition faithfully encodes the intended mathematical problem. A research formalization, a conjecture, a reduction, or a proof-challenge submission therefore carries four separate questions, and a conforming development keeps them separate:

Question

What settles it

Statement adequacy

An independent mathematical read-back of the elaborated declaration against the source mathematics (below). Not a kernel result.

Exact formal truth

The kernel-checked proof of the elaborated statement, with its exact axiom report (§3.4).

Conditional completeness

A theorem H → C is complete when C is proved from H; it claims the implication and nothing about H's inhabitance.

Open target

A Prop-valued definition, or an unproved binder, that no declaration on the conforming surface claims to prove. Never labeled proved.

Read-back. Before a formal statement is presented as the intended problem, the elaborated declaration MUST be read back into mathematics independently of the prose that motivated it. Inspect, in the elaborated term rather than the source text: quantifier order and dependence; implicit parameters and universe levels; every consequential definition the statement unfolds to (a Mathlib predicate may carry a side condition the informal statement omits, or omit one it assumes); notation, coercions, and the instances actually selected; totalized operations and domain restrictions (§3.2.1); and whether the statement asserts existence, construction, uniqueness, or a complexity bound, since these are distinct propositions. The Lean declaration remains the object proved; when the read-back and source disagree, the statement is inadequate even if the proof is valid. This is a semantic review of one declaration, not a provenance record.

Non-vacuity is relative to the claim (module 0). A claimed admitted state, unconditional existence, or reachability requires its stated inhabitance, joint-satisfiability, or reachability proof. A deliberately conditional theorem, a contradiction argument, or a minimal-counterexample lemma claims only its implication and MUST NOT be rejected for lacking an inhabitant of its antecedent: a restriction on a hypothetical odd perfect number does not require first proving that one exists. The review question is which claim the prose makes, not whether every hypothesis has a witness. If the development establishes ¬H, then H → C is vacuously true for any C. State that fact when it affects the interpretation; do not present the implication as evidence that H is inhabited or that C holds unconditionally. Such implications remain valid and may serve as contradiction steps, boundary cases, or reusable logical lemmas.

Open targets and reductions. An open question MAY be stated as a Prop-valued definition. A reduction H₁ → H₂ → Goal MAY be a complete theorem without proving Goal unconditionally. Assumptions stay in binders or proof-bearing fields; they MUST NOT become project axioms, sorry, or admit (§3.4), and an unresolved target MUST NOT be described as proved, closed, or established. The conforming surface may contain the conjecture’s statement and conditional theorems without containing a proof of the conjecture.

External platforms. A proof platform's open cards, sketches, and accepted results are distinct from conformance under this standard. An intentionally incomplete sketch or adapter MUST stay outside every positive surface and outside the dependency closure of any declaration presented as closed (module 7 §7.2). Before reusing a platform-accepted result, state the exact elaborated type, toolchain, and foundation under which it was accepted; a result about another elaboration environment is a result about that environment (§7.1).

Worked example. audit/Audit/Research.lean states the existence of a counterexample to the Collatz conjecture as Research.CounterexampleExists : Prop and proves nothing about it unconditionally. Read-back of that definition: one existential over Nat; the number is positive and no step count takes it to 1, where ReachesOne n is ∃ k, Nat.repeat collatzStep k n = 1 and collatzStep halves an even number and sends an odd n to 3 * n + 1. The positivity conjunct is part of the target: 0 is a fixed point of collatzStep and never reaches 1 (not_reachesOne_zero), so the statement without it, exists_not_reachesOne, is true and is a different proposition that settles nothing. The conjecture, ∀ n, 0 < n → ReachesOne n, implies ¬ CounterexampleExists constructively and is classically equivalent to it, since the converse eliminates a double negation of ReachesOne n; no declaration proves either. counterexample_nine_le is a conditional restriction, every counterexample is at least 9, proved from the closed finite case analysis search_below_nine, whose eight cases the kernel evaluates; it needs no witness. Counterexample.ne_two_pow is another, proved for every exponent by induction through repeat_collatzStep_two_pow_mul. not_counterexampleExists_of_bound is a complete reduction: an explicit upper bound plus a finite search below it refutes the target. search_below_nine discharges the search half at 9; the bound half remains an open binder, so the goal remains open. exists_odd_reachesOne shows the contrasting claim kind: an unconditional existence theorem, delivered with its witness 7.

import Audit.Research open Research /-- Conditional completeness: the implication is proved for every `n`, with no inhabitant of the antecedent required or supplied. -/ example (n : Nat) (hpos : 0 < n) (h : ¬ ReachesOne n) : 9 ≤ n ∧ ∀ k, n ≠ 2 ^ k := ⟨counterexample_nine_le hpos h, Counterexample.ne_two_pow h⟩ /-- The reduction instantiated at `9`: the only remaining obligation is the explicit bound hypothesis, which this example does not pretend to have. -/ example (hbound : ∀ n, 0 < n → ¬ ReachesOne n → n < 9) : ¬ CounterexampleExists := not_counterexampleExists_of_bound 9 hbound search_below_nine /-- Unconditional existence still requires its witness. -/ example : ∃ n : Nat, n % 2 = 1 ∧ 1 < n ∧ ReachesOne n := exists_odd_reachesOne /-- Dropping the positivity conjunct gives a different, provable statement. -/ example : ∃ n : Nat, ¬ ReachesOne n := exists_not_reachesOne /-- Quantifier order is part of the statement: the swapped form is false. -/ example : ¬ ∃ y : Nat, ∀ x : Nat, x < y := no_greatest_nat_swapped

A Prop target is not its own proof. Presenting the definition where a proof is required fails at the type level:

✗ Rejected by Lean, as intended
import Audit.Research theorem claimed : Research.CounterexampleExists := Type mismatch Research.CounterexampleExists has type Prop of sort `Type` but is expected to have type Research.CounterexampleExists of sort `Prop`Research.CounterexampleExists

Lean's error at line 3, the checked evidence:

Type mismatch
  Research.CounterexampleExists
has type
  Prop
of sort `Type` but is expected to have type
  Research.CounterexampleExists
of sort `Prop`

Using sorry as the proof, or replacing the theorem with a project axiom declaration, elaborates but is rejected by the declaration gate and fence audit on a positive surface (§3.4, module 7 §7.5). The declaration gate reports each Audit.Research declaration's exact axiom set within the surface's Standard-Logical claim; none includes sorryAx or a project axiom.

The same four claim kinds stated about Mathlib's Nat.Perfect, where the read-back finds a positivity conjunct that an odd number already supplies, are checked in the Mathlib integration package.