THE CONTROLLED TACTIC TABLE

same goal, ten tactics, axioms recorded per cell

Every observational measure in this program has the same weakness: a theorem's classical dependence can come from what it says or from how it was proved, and a census cannot tell you which. This is the intervention that can. The goal is held fixed, the tactic varies, and anything that changes between cells is the tactic's doing.

The result is a boundary rather than a gradient. norm_num introduces the axiom of choice on all 15 order goals (, <, over Nat and Int) and on none of the 12 equality and divisibility goals. On the same 27 goals decide, omega and trivial introduce it nowhere, so no goal here ever required it.

Measured with gonzalgo · Lean 4.32.1 with Mathlib v4.32.1 · 9 goal families × 3 goals × 10 tactics = 270 cells, every cell a separate theorem with its own #print axioms

goalshapetacticclosedclassicalaxioms
(3 : Nat) ∣ 12equality/divisibilityaesopyesnopropext Quot.sound
(3 : Nat) ∣ 12equality/divisibilitydecideyesnopropext
(3 : Nat) ∣ 12equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(3 : Nat) ∣ 12equality/divisibilitylinarithnonosorryAx
(3 : Nat) ∣ 12equality/divisibilitynorm_numyesnopropext
(3 : Nat) ∣ 12equality/divisibilityomegayesnopropext Quot.sound
(3 : Nat) ∣ 12equality/divisibilitypositivitynonosorryAx
(3 : Nat) ∣ 12equality/divisibilityrflnonosorryAx
(3 : Nat) ∣ 12equality/divisibilitysimpyesnopropext Quot.sound
(3 : Nat) ∣ 12equality/divisibilitytrivialyesnopropext
(5 : Nat) ∣ 40equality/divisibilityaesopyesnopropext Quot.sound
(5 : Nat) ∣ 40equality/divisibilitydecideyesnopropext
(5 : Nat) ∣ 40equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(5 : Nat) ∣ 40equality/divisibilitylinarithnonosorryAx
(5 : Nat) ∣ 40equality/divisibilitynorm_numyesnopropext
(5 : Nat) ∣ 40equality/divisibilityomegayesnopropext Quot.sound
(5 : Nat) ∣ 40equality/divisibilitypositivitynonosorryAx
(5 : Nat) ∣ 40equality/divisibilityrflnonosorryAx
(5 : Nat) ∣ 40equality/divisibilitysimpyesnopropext Quot.sound
(5 : Nat) ∣ 40equality/divisibilitytrivialyesnopropext
(1 : Nat) ∣ 7equality/divisibilityaesopyesnopropext
(1 : Nat) ∣ 7equality/divisibilitydecideyesnopropext
(1 : Nat) ∣ 7equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(1 : Nat) ∣ 7equality/divisibilitylinarithnonosorryAx
(1 : Nat) ∣ 7equality/divisibilitynorm_numyesnopropext
(1 : Nat) ∣ 7equality/divisibilityomegayesnopropext Quot.sound
(1 : Nat) ∣ 7equality/divisibilitypositivitynonosorryAx
(1 : Nat) ∣ 7equality/divisibilityrflnonosorryAx
(1 : Nat) ∣ 7equality/divisibilitysimpyesnopropext
(1 : Nat) ∣ 7equality/divisibilitytrivialyesnopropext
(2 : Int) + 3 = 5equality/divisibilityaesopyesnopropext
(2 : Int) + 3 = 5equality/divisibilitydecideyesno(none)
(2 : Int) + 3 = 5equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(2 : Int) + 3 = 5equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(2 : Int) + 3 = 5equality/divisibilitynorm_numyesnopropext
(2 : Int) + 3 = 5equality/divisibilityomegayesnopropext Quot.sound
(2 : Int) + 3 = 5equality/divisibilitypositivitynonosorryAx
(2 : Int) + 3 = 5equality/divisibilityrflyesno(none)
(2 : Int) + 3 = 5equality/divisibilitysimpyesnopropext
(2 : Int) + 3 = 5equality/divisibilitytrivialyesno(none)
(6 : Int) * 7 = 42equality/divisibilityaesopyesnopropext
(6 : Int) * 7 = 42equality/divisibilitydecideyesno(none)
(6 : Int) * 7 = 42equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(6 : Int) * 7 = 42equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(6 : Int) * 7 = 42equality/divisibilitynorm_numyesnopropext
(6 : Int) * 7 = 42equality/divisibilityomegayesnopropext Quot.sound
(6 : Int) * 7 = 42equality/divisibilitypositivitynonosorryAx
(6 : Int) * 7 = 42equality/divisibilityrflyesno(none)
(6 : Int) * 7 = 42equality/divisibilitysimpyesnopropext
(6 : Int) * 7 = 42equality/divisibilitytrivialyesno(none)
(2 : Int) - 9 = -7equality/divisibilityaesopyesnopropext
(2 : Int) - 9 = -7equality/divisibilitydecideyesno(none)
(2 : Int) - 9 = -7equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(2 : Int) - 9 = -7equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(2 : Int) - 9 = -7equality/divisibilitynorm_numyesnopropext
(2 : Int) - 9 = -7equality/divisibilityomegayesnopropext Quot.sound
(2 : Int) - 9 = -7equality/divisibilitypositivitynonosorryAx
(2 : Int) - 9 = -7equality/divisibilityrflyesno(none)
(2 : Int) - 9 = -7equality/divisibilitysimpyesnopropext
(2 : Int) - 9 = -7equality/divisibilitytrivialyesno(none)
(2 : Nat) + 3 = 5equality/divisibilityaesopyesnopropext
(2 : Nat) + 3 = 5equality/divisibilitydecideyesno(none)
(2 : Nat) + 3 = 5equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(2 : Nat) + 3 = 5equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(2 : Nat) + 3 = 5equality/divisibilitynorm_numyesnopropext
(2 : Nat) + 3 = 5equality/divisibilityomegayesnopropext Quot.sound
(2 : Nat) + 3 = 5equality/divisibilitypositivitynonosorryAx
(2 : Nat) + 3 = 5equality/divisibilityrflyesno(none)
(2 : Nat) + 3 = 5equality/divisibilitysimpyesnopropext
(2 : Nat) + 3 = 5equality/divisibilitytrivialyesno(none)
(6 : Nat) * 7 = 42equality/divisibilityaesopyesnopropext
(6 : Nat) * 7 = 42equality/divisibilitydecideyesno(none)
(6 : Nat) * 7 = 42equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(6 : Nat) * 7 = 42equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(6 : Nat) * 7 = 42equality/divisibilitynorm_numyesnopropext
(6 : Nat) * 7 = 42equality/divisibilityomegayesnopropext Quot.sound
(6 : Nat) * 7 = 42equality/divisibilitypositivitynonosorryAx
(6 : Nat) * 7 = 42equality/divisibilityrflyesno(none)
(6 : Nat) * 7 = 42equality/divisibilitysimpyesnopropext
(6 : Nat) * 7 = 42equality/divisibilitytrivialyesno(none)
(10 : Nat) - 4 = 6equality/divisibilityaesopyesnopropext
(10 : Nat) - 4 = 6equality/divisibilitydecideyesno(none)
(10 : Nat) - 4 = 6equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(10 : Nat) - 4 = 6equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(10 : Nat) - 4 = 6equality/divisibilitynorm_numyesnopropext
(10 : Nat) - 4 = 6equality/divisibilityomegayesnopropext Quot.sound
(10 : Nat) - 4 = 6equality/divisibilitypositivitynonosorryAx
(10 : Nat) - 4 = 6equality/divisibilityrflyesno(none)
(10 : Nat) - 4 = 6equality/divisibilitysimpyesnopropext
(10 : Nat) - 4 = 6equality/divisibilitytrivialyesno(none)
(5 : Nat) ≥ 3orderaesopyesnopropext
(5 : Nat) ≥ 3orderdecideyesno(none)
(5 : Nat) ≥ 3ordergrindyesyespropext Classical.choice Quot.sound
(5 : Nat) ≥ 3orderlinarithyesyespropext Classical.choice Quot.sound
(5 : Nat) ≥ 3ordernorm_numyesyespropext Classical.choice Quot.sound
(5 : Nat) ≥ 3orderomegayesnopropext Quot.sound
(5 : Nat) ≥ 3orderpositivitynonosorryAx
(5 : Nat) ≥ 3orderrflnonosorryAx
(5 : Nat) ≥ 3ordersimpyesnopropext
(5 : Nat) ≥ 3ordertrivialyesno(none)
(40 : Nat) ≥ 12orderaesopyesnopropext
(40 : Nat) ≥ 12orderdecideyesno(none)
(40 : Nat) ≥ 12ordergrindyesyespropext Classical.choice Quot.sound
(40 : Nat) ≥ 12orderlinarithyesyespropext Classical.choice Quot.sound
(40 : Nat) ≥ 12ordernorm_numyesyespropext Classical.choice Quot.sound
(40 : Nat) ≥ 12orderomegayesnopropext Quot.sound
(40 : Nat) ≥ 12orderpositivitynonosorryAx
(40 : Nat) ≥ 12orderrflnonosorryAx
(40 : Nat) ≥ 12ordersimpyesnopropext
(40 : Nat) ≥ 12ordertrivialyesno(none)
(7 : Nat) ≥ 0orderaesopyesnopropext
(7 : Nat) ≥ 0orderdecideyesno(none)
(7 : Nat) ≥ 0ordergrindyesyespropext Classical.choice Quot.sound
(7 : Nat) ≥ 0orderlinarithyesyespropext Classical.choice Quot.sound
(7 : Nat) ≥ 0ordernorm_numyesyespropext Classical.choice Quot.sound
(7 : Nat) ≥ 0orderomegayesnopropext Quot.sound
(7 : Nat) ≥ 0orderpositivityyesyespropext Classical.choice Quot.sound
(7 : Nat) ≥ 0orderrflnonosorryAx
(7 : Nat) ≥ 0ordersimpyesnopropext
(7 : Nat) ≥ 0ordertrivialyesno(none)
(3 : Int) ≤ 5orderaesopyesnopropext
(3 : Int) ≤ 5orderdecideyesno(none)
(3 : Int) ≤ 5ordergrindyesyespropext Classical.choice Quot.sound
(3 : Int) ≤ 5orderlinarithyesyespropext Classical.choice Quot.sound
(3 : Int) ≤ 5ordernorm_numyesyespropext Classical.choice Quot.sound
(3 : Int) ≤ 5orderomegayesnopropext Quot.sound
(3 : Int) ≤ 5orderpositivitynonosorryAx
(3 : Int) ≤ 5orderrflnonosorryAx
(3 : Int) ≤ 5ordersimpyesnopropext
(3 : Int) ≤ 5ordertrivialyesno(none)
(-4 : Int) ≤ 2orderaesopyesnopropext
(-4 : Int) ≤ 2orderdecideyesno(none)
(-4 : Int) ≤ 2ordergrindyesyespropext Classical.choice Quot.sound
(-4 : Int) ≤ 2orderlinarithyesyespropext Classical.choice Quot.sound
(-4 : Int) ≤ 2ordernorm_numyesyespropext Classical.choice Quot.sound
(-4 : Int) ≤ 2orderomegayesnopropext Quot.sound
(-4 : Int) ≤ 2orderpositivitynonosorryAx
(-4 : Int) ≤ 2orderrflnonosorryAx
(-4 : Int) ≤ 2ordersimpyesnopropext
(-4 : Int) ≤ 2ordertrivialyesno(none)
(0 : Int) ≤ 7orderaesopyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7orderdecideyesno(none)
(0 : Int) ≤ 7ordergrindyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7orderlinarithyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7ordernorm_numyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7orderomegayesnopropext Quot.sound
(0 : Int) ≤ 7orderpositivityyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7orderrflnonosorryAx
(0 : Int) ≤ 7ordersimpyesyespropext Classical.choice Quot.sound
(0 : Int) ≤ 7ordertrivialyesno(none)
(3 : Nat) ≤ 5orderaesopyesnopropext
(3 : Nat) ≤ 5orderdecideyesno(none)
(3 : Nat) ≤ 5ordergrindyesyespropext Classical.choice Quot.sound
(3 : Nat) ≤ 5orderlinarithyesyespropext Classical.choice Quot.sound
(3 : Nat) ≤ 5ordernorm_numyesyespropext Classical.choice Quot.sound
(3 : Nat) ≤ 5orderomegayesnopropext Quot.sound
(3 : Nat) ≤ 5orderpositivitynonosorryAx
(3 : Nat) ≤ 5orderrflnonosorryAx
(3 : Nat) ≤ 5ordersimpyesnopropext
(3 : Nat) ≤ 5ordertrivialyesno(none)
(12 : Nat) ≤ 40orderaesopyesnopropext
(12 : Nat) ≤ 40orderdecideyesno(none)
(12 : Nat) ≤ 40ordergrindyesyespropext Classical.choice Quot.sound
(12 : Nat) ≤ 40orderlinarithyesyespropext Classical.choice Quot.sound
(12 : Nat) ≤ 40ordernorm_numyesyespropext Classical.choice Quot.sound
(12 : Nat) ≤ 40orderomegayesnopropext Quot.sound
(12 : Nat) ≤ 40orderpositivitynonosorryAx
(12 : Nat) ≤ 40orderrflnonosorryAx
(12 : Nat) ≤ 40ordersimpyesnopropext
(12 : Nat) ≤ 40ordertrivialyesno(none)
(0 : Nat) ≤ 7orderaesopyesnopropext
(0 : Nat) ≤ 7orderdecideyesno(none)
(0 : Nat) ≤ 7ordergrindyesyespropext Classical.choice Quot.sound
(0 : Nat) ≤ 7orderlinarithyesyespropext Classical.choice Quot.sound
(0 : Nat) ≤ 7ordernorm_numyesyespropext Classical.choice Quot.sound
(0 : Nat) ≤ 7orderomegayesnopropext Quot.sound
(0 : Nat) ≤ 7orderpositivityyesyespropext Classical.choice Quot.sound
(0 : Nat) ≤ 7orderrflnonosorryAx
(0 : Nat) ≤ 7ordersimpyesnopropext
(0 : Nat) ≤ 7ordertrivialyesno(none)
(3 : Int) < 5orderaesopyesnopropext
(3 : Int) < 5orderdecideyesno(none)
(3 : Int) < 5ordergrindyesyespropext Classical.choice Quot.sound
(3 : Int) < 5orderlinarithyesyespropext Classical.choice Quot.sound
(3 : Int) < 5ordernorm_numyesyespropext Classical.choice Quot.sound
(3 : Int) < 5orderomegayesnopropext Quot.sound
(3 : Int) < 5orderpositivitynonosorryAx
(3 : Int) < 5orderrflnonosorryAx
(3 : Int) < 5ordersimpyesnopropext
(3 : Int) < 5ordertrivialyesno(none)
(-4 : Int) < 2orderaesopyesyespropext Classical.choice Quot.sound
(-4 : Int) < 2orderdecideyesno(none)
(-4 : Int) < 2ordergrindyesyespropext Classical.choice Quot.sound
(-4 : Int) < 2orderlinarithyesyespropext Classical.choice Quot.sound
(-4 : Int) < 2ordernorm_numyesyespropext Classical.choice Quot.sound
(-4 : Int) < 2orderomegayesnopropext Quot.sound
(-4 : Int) < 2orderpositivitynonosorryAx
(-4 : Int) < 2orderrflnonosorryAx
(-4 : Int) < 2ordersimpyesyespropext Classical.choice Quot.sound
(-4 : Int) < 2ordertrivialyesno(none)
(0 : Int) < 7orderaesopyesyespropext Classical.choice Quot.sound
(0 : Int) < 7orderdecideyesno(none)
(0 : Int) < 7ordergrindyesyespropext Classical.choice Quot.sound
(0 : Int) < 7orderlinarithyesyespropext Classical.choice Quot.sound
(0 : Int) < 7ordernorm_numyesyespropext Classical.choice Quot.sound
(0 : Int) < 7orderomegayesnopropext Quot.sound
(0 : Int) < 7orderpositivityyesyespropext Classical.choice Quot.sound
(0 : Int) < 7orderrflnonosorryAx
(0 : Int) < 7ordersimpyesyespropext Classical.choice Quot.sound
(0 : Int) < 7ordertrivialyesno(none)
(3 : Nat) < 5orderaesopyesnopropext
(3 : Nat) < 5orderdecideyesno(none)
(3 : Nat) < 5ordergrindyesyespropext Classical.choice Quot.sound
(3 : Nat) < 5orderlinarithyesyespropext Classical.choice Quot.sound
(3 : Nat) < 5ordernorm_numyesyespropext Classical.choice Quot.sound
(3 : Nat) < 5orderomegayesnopropext Quot.sound
(3 : Nat) < 5orderpositivitynonosorryAx
(3 : Nat) < 5orderrflnonosorryAx
(3 : Nat) < 5ordersimpyesnopropext
(3 : Nat) < 5ordertrivialyesno(none)
(12 : Nat) < 40orderaesopyesnopropext
(12 : Nat) < 40orderdecideyesno(none)
(12 : Nat) < 40ordergrindyesyespropext Classical.choice Quot.sound
(12 : Nat) < 40orderlinarithyesyespropext Classical.choice Quot.sound
(12 : Nat) < 40ordernorm_numyesyespropext Classical.choice Quot.sound
(12 : Nat) < 40orderomegayesnopropext Quot.sound
(12 : Nat) < 40orderpositivitynonosorryAx
(12 : Nat) < 40orderrflnonosorryAx
(12 : Nat) < 40ordersimpyesnopropext
(12 : Nat) < 40ordertrivialyesno(none)
(0 : Nat) < 7orderaesopyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7orderdecideyesno(none)
(0 : Nat) < 7ordergrindyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7orderlinarithyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7ordernorm_numyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7orderomegayesnopropext Quot.sound
(0 : Nat) < 7orderpositivityyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7orderrflnonosorryAx
(0 : Nat) < 7ordersimpyesyespropext Classical.choice Quot.sound
(0 : Nat) < 7ordertrivialyesno(none)
(3 : Nat) ≠ 5equality/divisibilityaesopyesnopropext
(3 : Nat) ≠ 5equality/divisibilitydecideyesno(none)
(3 : Nat) ≠ 5equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(3 : Nat) ≠ 5equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(3 : Nat) ≠ 5equality/divisibilitynorm_numyesnopropext
(3 : Nat) ≠ 5equality/divisibilityomegayesnopropext Quot.sound
(3 : Nat) ≠ 5equality/divisibilitypositivitynonosorryAx
(3 : Nat) ≠ 5equality/divisibilityrflnonosorryAx
(3 : Nat) ≠ 5equality/divisibilitysimpyesnopropext
(3 : Nat) ≠ 5equality/divisibilitytrivialyesno(none)
(12 : Nat) ≠ 40equality/divisibilityaesopyesnopropext
(12 : Nat) ≠ 40equality/divisibilitydecideyesno(none)
(12 : Nat) ≠ 40equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(12 : Nat) ≠ 40equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(12 : Nat) ≠ 40equality/divisibilitynorm_numyesnopropext
(12 : Nat) ≠ 40equality/divisibilityomegayesnopropext Quot.sound
(12 : Nat) ≠ 40equality/divisibilitypositivitynonosorryAx
(12 : Nat) ≠ 40equality/divisibilityrflnonosorryAx
(12 : Nat) ≠ 40equality/divisibilitysimpyesnopropext
(12 : Nat) ≠ 40equality/divisibilitytrivialyesno(none)
(1 : Nat) ≠ 0equality/divisibilityaesopyesnopropext
(1 : Nat) ≠ 0equality/divisibilitydecideyesno(none)
(1 : Nat) ≠ 0equality/divisibilitygrindyesyespropext Classical.choice Quot.sound
(1 : Nat) ≠ 0equality/divisibilitylinarithyesyespropext Classical.choice Quot.sound
(1 : Nat) ≠ 0equality/divisibilitynorm_numyesnopropext
(1 : Nat) ≠ 0equality/divisibilityomegayesnopropext Quot.sound
(1 : Nat) ≠ 0equality/divisibilitypositivityyesyespropext Classical.choice Quot.sound
(1 : Nat) ≠ 0equality/divisibilityrflnonosorryAx
(1 : Nat) ≠ 0equality/divisibilitysimpyesnopropext
(1 : Nat) ≠ 0equality/divisibilitytrivialyesno(none)

closed is false where the tactic did not prove the goal. Lean records those as depending on sorryAx, which is the tactic failing rather than a proof resting on something unfinished, so they are excluded from every rate below — a tactic that never ran cannot have introduced an axiom. classical means Classical.choice appears in the cell's axiom set.

Get the data

controlled-tactics.json · controlled-tactics.csv · CC-BY-4.0 · version 2026-08-12

One of the gonzalgo indexes — standing measurements of what formal libraries rest on, remeasured as the libraries move.

Reproduce it

lake env lean -D maxErrors=4000 lean/Controlled.lean

set_option maxErrors inside the file is ignored; only the command-line flag works, and without it Lean halts at 100 errors long before the last theorem. The generator and the raw output are in 10.5281/zenodo.21883963.

Rates, over goals each tactic actually closed

tacticgoals closedclassicalrate
aesop27415%
decide2700%
grind2727100%
linarith2424100%
norm_num271556%
omega2700%
positivity66100%
rfl600%
simp27415%
trivial2700%

What the boundary means

norm_num's dependence is not a property of the arithmetic. The same numbers, stated as an equality, come out choice-free from the same tactic. It is the order goals specifically, and on Nat and Int the constructive decidability instance exists and is axiom-free, so the classical route was taken where a free one was available.

grind is classical on every goal it closes and that is not a defect: it is architecturally classical. Reporting it beside norm_num without that distinction is exactly the error the attribution paper is about — a rate cannot separate a tactic that is designed classical from one that reached for choice unnecessarily.

aesop and simp agree on all 27 goals, cell for cell. aesop's classical cells are simp's classical cells. A search procedure built over a simp set inherits that set's axiom behaviour rather than adding to it.

Why this is worth more than the census

Per-tactic rates measured across a library vary as much within a library as between them — 5.8–28.2% in Mathlib, 45–100% in Lean core — because eligibility is a property of the theorem while the defect is a property of the proof term. Fixing the goal removes that confound entirely. Every difference in this table is attributable, because nothing else was allowed to vary.