Tasks/Math & Scientific Computing/Formal Maths

Design a verifier-guided Lean 4 proof workflow

Prove Lean theorems under a per-problem inference budget

lean_formal_proof_workflow_design Math & Scientific Computing Formal Maths
instruction.mdthis is what the agent is given

You inherit a small compiler-guided proof workflow over a pinned language model. Improve the workflow in /app/methods/main/ so it completes canonical Lean 4 theorem statements reliably under a tight inference and compiler-feedback budget. The verifier copies your final method into a clean process, re-runs it on held-out theorems, and counts only proofs accepted by the pinned Lean kernel.

Hard Constraints

  1. Edit only /app/methods/main/; you may add sibling .py helpers and import them from solver.py.
  2. Keep answer_batch(examples, llm, lean, budget) -> list[str]. Return one proof body per input, in order. Every proof body must start with the Lean token by.
  3. Per problem you may use at most 3 fixed-model calls, 12,000 total model tokens, 4 Lean checks, and 180 seconds wall time. A budget overrun scores that problem wrong.
  4. Use only the injected llm and lean callables. Do not open network connections, spawn processes, inspect host files, or import another model or prover.
  5. The formal statement is fixed. Do not emit a replacement theorem, declaration, axiom, sorry, admit, unsafe code, metaprogram execution, or commands such as #eval.
  6. Do not branch on problem IDs or encode per-problem proof tables. The hidden process replaces every ID with the same opaque value.
  7. Compiler feedback is untrusted diagnostic text, not a proof. A problem is correct only when the trusted parent independently compiles the final proof with the canonical statement.
  8. The submission guard rejects filesystem/network/process modules (os, pathlib, socket, subprocess, HTTP clients, FFI, import machinery, and similar capabilities), dynamic code/file calls, sealed-data names in any string literal, and any literal ["id"]/.get("id") lookup. Generated __pycache__/bytecode is ignored; every other submitted file must be regular Python source. Pure-computation modules such as re, json, math, and hashlib are allowed.

What You Have

  • /app/data/visible.jsonl: a frozen 54-theorem development split. Each row contains an opaque ID, the natural-language source problem, a difficulty/subtopic label, and the canonical Lean formal_statement ending in :=.
  • /app/methods/main/: the editable seed harness. It performs one model proposal, one Lean check, and at most one compiler-diagnostic repair.
  • /app/selfcheck.py: evaluates the complete fixed visible split through the same model proxy, proof-body validator, pinned Lean compiler, budgets, and aggregation used by the verifier. Always evaluate all 54 rows when comparing checkpoints.
  • The release protocol uses the immutable gpt-5.4-2026-03-05 snapshot, exposed through deployment alias gpt-5.4, at temperature 0 and seed 0.

The useful workflow primitives are proposal diversity, compiler-guided repair, error summarization, proof extraction, tactic fallback, and budget-aware stopping. The natural-language problem can help with proof planning, but the Lean statement is the authoritative target.

What You Submit

Leave your best implementation under /app/methods/main/. solver.py must expose:

def answer_batch(examples, llm, lean, budget):
    """
    examples: list[dict] with problem, formal_statement, difficulty, and subtopic
    llm:      llm(messages, max_tokens=..., stop=...) -> str
    lean:     lean(proof_body) -> {"ok": bool, "diagnostics": str}
    budget:   fixed call/token/check/wall limits
    returns:  list[str], one Lean proof body beginning with `by` per example
    """

The verifier collects /app/methods, imports no submitted code in its trusted process, and runs one fresh low-privilege child per hidden theorem. Non-generated files other than Python source, symlinks, special files, oversized source trees, invalid Unicode, and malformed outputs are rejected.

How It Is Judged

For each hidden row, the trusted parent combines the pinned imports and canonical formal_statement with your returned proof body, rejects forbidden proof constructs, and invokes the pinned Lean 4.9/mathlib compiler in a bounded low-privilege process. There is no LLM judge and no agent-authored success flag. The raw metric is:

lean_compile_accuracy_pct = 100 * compiled hidden theorems / hidden theorems

The normalized score is a monotonic function of sealed compile accuracy and is not shown to you; optimize raw compile accuracy and generalization. Submission-caused model rejections (HTTP 400/413/422), compiler timeouts, malformed output, budget overruns, or any rejected construct count as incorrect. Missing or drifted runtime configuration, model endpoint outage/authentication/429/5xx failures, missing trustworthy generated-token usage, unavailable Lean, or unavailable kernel isolation invalidate the verifier run rather than masquerading as a participant score.

Metric

compile accuracy over the 40 sealed theorems · higher is better

100 * sealed theorems whose returned proof body compiles under pinned Lean 4.9 / Mathlib, over 40

anchorheld-outreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
normalisation
submission invalid0
otherwisem / 100

m = this run's held-out metric

to_reward(m) = m/100, rounded to 6 dp. No anchor table, no shift: reward is the raw sealed compile rate. A grader failure writes 0.

Rollouts

340 minwall clock
$41.80spend
66.1Mtokens
12versions, 12 kept
15.0 22.5 30.0 37.5 45.0 52.5 $0 $10 $20 $30 cumulative spend on the run visible-set compile accuracy, 54 rows, % v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11
keptrevertedno scoreturning point
  1. v0Inherited seed: one proposal, one Lean check, one diagnostic repair16.72 min · $0.62
  2. v1Rewrite: fixed extractor, 3 candidates per call, repair rounds, hedged answerSeed extractor cut every proof at the first ':= by', truncating nested 'have'. Fixing it alone moved a probe 9/54 to 11/54.38.937 min · $4.42
  3. v2Adaptive wall-clock model from observed durations, plus parse-error blame35.268 min · $9.56
  4. v3Sink split into cheap front and heavy tail; path-stripped, proof-relative diagnostics33.378 min · $11.51
  5. v4One Lean check per model round; prefix-trimmed hedge; MAX_HEDGE 8; round-robin29.6154 min · $19.20
  6. v5done guard on every sink alternative; sink cut to 12 cheap + 2 heavy; exact? dropped'first' commits to any alternative that merely makes progress; without a trailing 'done' the sink blocked the model's own proofs.38154 min · $19.36
  7. v6A prose-planning call before any Lean is written, kept in context for later roundsProve it in prose first, no Lean, then propose against that derivation. A two-call probe went top-3 14/54 to 17/54.45.4189 min · $23.49
  8. v7Time guards made proportional to observed call and check durations45.4209 min · $25.54
  9. v8Five candidate blocks per proposal call; repair replaces the failing step first46.3240 min · $29.32
  10. v9MAX_HEDGE back to 6, hedge shrunk when time is short, truncated fences recovered45.4264 min · $31.94
  11. v10Wall reserve floor 0.32x; static cost model prices the hedge when no check is left43.5288 min · $34.99
  12. v11A verified hedge is never traded away; planning call skipped under two model calls53.7316 min · $38.36

12 snapshots, 5h40m, $41.80. Nothing reverted: v3, v4 and v10 scored below their parent and were kept on mechanism, noise being about 1 problem.

On the hidden set

held-out metricreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
this run47.5% (19/40)0.475
513 minwall clock
$3.08spend
8.0Mtokens
2versions, 2 kept
25.0 27.5 30.0 32.5 35.0 $0 $0.8 $1.5 $2.2 $3 cumulative spend on the run visible-set compile accuracy, 54 rows, % v1 v2
keptrevertedno scoreturning point
  1. v1Extractor takes the first standalone 'by' in the code block, never a nested oneThe seed regex cut every proof at the first ':= by', dropping nested 'have' and 'calc' bodies; the fix alone went 9/54 to 13/54.24.0742 min · $0.39
  2. v2Fallback tactics tried before the model, two repair rounds, last-resort cleanupSpend the free compile on decide/norm_num-style fallbacks first, repair twice, then sanitize whatever the model last returned.35.19513 min · $2.94

2 snapshots, 160 steps, 8h33m, $3.08. Seven hours between them went to unsnapshotted prompt variants scoring 18.5-33.3%, none kept.

On the hidden set

held-out metricreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
this run45.0% (18/40)0.45
178 minwall clock
-spend
-tokens
7versions, 4 kept
33.2 33.6 34.0 34.4 34.8 35.2 0 40 80 120 160 agent step (this harness reports no tokens or timestamps) visible-set compile accuracy, 54 rows, % v1 v2 v3 v4 v5 v6 v7
keptrevertedno scoreturning point
  1. v1Tactic hammer before any model call; longest-'by' extraction; Lean 4.9 promptRun decide/native_decide/norm_num/nlinarith/ring/aesop for free first, and stop truncating proofs at a nested ':= by'.9-row subset: 5/9
  2. v2Lemma-name normalizer, first-error repair, fresh last-chance call, FE hintMost failures were Lean 3 or version-skew identifiers; rewrite the names and aim the repair call at the first compiler error.16-row subset: 6/16
  3. v3cheap_variants reordered: targeted rewrites first, aliases only on unknown name35.19
  4. v4Shape hints for Complex, positive naturals and least-n; sq_sqrt and series aliasesspot checks only
  5. v5finalize() compiles the best cheap variant of the last proof before returning it21-row subset only
  6. v6Rebuilt from v3 with only the power-inequality and numeric-exists hints keptHints that each won a row were not additive: re-adding the two with new wins still lost three proofs v3 had held.33.33
  7. v7v3 restored plus identifier aliases that cannot break an already-valid proofnot re-run after v3

7 snapshots, 182 steps, 2h58m; this harness reports no token or cost data. Only v3 and v6 got a full 54-row run; the rest are subsets.

On the hidden set

held-out metricreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
this run42.5% (17/40)0.425
351 minwall clock
$93.26spend
170.4Mtokens
7versions, 3 kept
31.5 33.0 34.5 36.0 37.5 $0 $20 $40 $60 $80 cumulative spend on the run visible-set compile accuracy, 54 rows, % v1 v2 v3 v4 v5 v6 v7
keptrevertedno scoreturning point
  1. v1Inherited seed harness, unchanged, taken as the baseline snapshotblocked after 1/54 rows3 min · $0.78
  2. v2Three-stage 4500/4000/3500 proposal-repair-synthesis, top-level extractionThree staged calls on a fixed token split, stopping at the first verified candidate. The only version to reach 20/54.37.0412 min · $2.19
  3. v3Deterministic pre-check tried first: native_decide, proving 9 of 54 offline35.1957 min · $9.30
  4. v4Pre-check widened: native_decide, then a compact aesop/arithmetic chain, then ringOne Lean check buys a deterministic chain proving 10/54 offline, freeing all three model calls. Adopted on that; it tied v3.35.19102 min · $18.50
  5. v5Forensic repair of the latest proof and diagnostics replaces the synthesis context35.19198 min · $42.54
  6. v6Third call restarts independently from the theorem with a different strategy31.48292 min · $71.61
  7. v7Token caps reallocated from 4500/4000/3500 to 4500/4500/300035.19322 min · $82.78

7 snapshots, 1,093 steps, 5h51m, $93.26 - every comparison was a full 54-row run. v5 to v7 were all reverted to v4, which sits below its own v2.

On the hidden set

held-out metricreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
this run40.0% (16/40)0.4
513 minwall clock
$131.48spend
236.1Mtokens
18versions, 9 kept
15.0 22.5 30.0 37.5 45.0 $0 $30 $60 $90 $120 cumulative spend on the run visible-set compile accuracy, 54 rows, % v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17
keptrevertedno scoreturning point
  1. v0Inherited seed harness, unchanged, taken as the baseline snapshot14.811 min · $0.20
  2. v1Extractor recognizes a direct 'by' body before theorem-level ':= by'Local 'have h := by' was truncating every proof at the first ':= by'; the fix alone added 5 rows across four strata.24.0716 min · $2.17
  3. v2Universal kernel-checked 'by native_decide' fast path before any model callTry a deterministic kernel check first; it closed 9 computational rows with zero model calls.29.6337 min · $5.55
  4. v3Third model call becomes a second diagnostic repair; caps 5k/4k/3k tokens33.3354 min · $9.20
  5. v4native_decide routed to executable shapes only; final call returns two bodies37.0478 min · $14.62
  6. v5Lean 4.9 expert system prompt, elaboration checks, 'sorry' placeholder droppedName the exact Lean version and frame diagnostics as untrusted; medium rows doubled and the run peaked at 23/54.42.59104 min · $21.90
  7. v6Hard rows get an explicit planning call, then two synthesis candidates33.33128 min · $29.02
  8. v7Second call on hard rows switched from repair to an independent proposal35.19158 min · $38.85
  9. v8Completion caps reallocated from 5k/4k/3k to 4.5k/3.5k/4k37.04183 min · $47.48
  10. v9Rejected proof replayed as an assistant turn, diagnostics as user feedback35.19210 min · $57.20
  11. v10Diagnostic class chooses between local repair and independent re-synthesis29.63270 min · $80.79
  12. v11Compact domain-general tactic playbook added to the system prompt29.63298 min · $93.70
  13. v12Quantified propositions excluded from the native_decide route33.33328 min · $95.78
  14. v13Final two-candidate call given both rejected proofs and both compiler reports37.04354 min · $98.99
  15. v14Diversity moved to the first call: two proposals, then repair each branch35.19383 min · $102.96
  16. v15Raw diagnostic tails replaced with bounded head-and-tail excerpts35.19409 min · $107.37
  17. v16Superseded first proof dropped from the final prompt, its diagnostic kept29.63461 min · $118.03
  18. v17Hard rows routed to the smaller latest-proof final prompt; easy/medium keep two37.04488 min · $124.53

18 snapshots, 1,785 steps, 8h33m, $131.48. Every checkpoint ran all 54 visible rows; repeats of v5 and v13 swung by about 3 rows.

On the hidden set

held-out metricreward
no sealed theorem compiles0.00%0.00
every sealed theorem compiles100.00%1.00
this run40.0% (16/40)0.4