Skip to main content

g6-lean

Use when you have written or been handed a Lean 4 proof and are about to say it proves a theorem, or when a claim needs a machine-checked proof that a person will inspect, and you need to know the theorem is really declared, has no sorry, uses only Lean's standard axioms and states what you expect; or when you have a statement in Lean and want G6 to search for a proof it then checks the same way.

Source facts

Repository
basicisabc1994/g6solver
Last source activity
October 26, 2026 at 01:52
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
g6-lean
description
Use when you have written or been handed a Lean 4 proof and are about to say it proves a theorem, or when a claim needs a machine-checked proof that a person will inspect, and you need to know the theorem is really declared, has no sorry, uses only Lean's standard axioms and states what you expect; or when you have a statement in Lean and want G6 to search for a proof it then checks the same way.
# G6 Lean Checks a Lean 4 proof of a named theorem, out of process, with Lean installed on your machine. A proof passes only when Lean itself reports that the theorem is declared in your file, is a `theorem`, has no `sorry`, uses only Lean's standard axioms and, when you give one, has the statement you expect. `prove` searches for such a proof of your own statement, and `sketch` asks your model for an outline in words. ## When to use it - You are about to say "the proof checks" or "Lean accepts it" about a file. - A proof was generated and may hide a `sorry`, an added `axiom`, `native_decide` or a theorem of a different statement. - A claim must rest on a formal proof a person will inspect, and you want the statement Lean actually checked. - You have the statement in Lean and want a proof found: `prove` tries the core tactics, then your model's proposals. ## When not to use it - A decision you can get without a proof (satisfiable, valid, a counterexample): use `formal`. - An algebra identity or an integral: use `algebra`. - A proof that needs Mathlib or a Lake project: only `Init` and `Std` may be imported here, so nothing in G6 checks it. - Lean is not installed and the person running the server has not allowed it: say so, and ask them. - The claim is still in prose: a formal statement is a draft for a person to read before anything is proved about it. ## Ops - `check_proof`: `source` (one Lean 4 file) is screened, then Lean elaborates it and G6 reads Lean's own `#check`, `#print` and `#print axioms` on `theorem` (its full name); `expected_statement` is optional (sensitive: runs Lean, a program on your machine, on your source). - `lean_version`: which Lean would check, from `lean --version`; nothing is checked (sensitive: runs Lean). - `prove`: `statement` (written as Lean prints it), optional `preamble` (your definitions), `budget` (at most 32 candidates) and `deadline_s` (the whole search, 600 seconds by default, at most 3600; each check gets what is left): G6 writes `theorem g6_goal : <statement> := <proof>` for `by rfl`, `by decide`, `by simp`, `by omega`, then for proofs the block's injected model proposes, each told Lean's errors on the last one; every candidate is judged only by `check_proof` against your statement (sensitive: runs Lean). - `sketch`: the block's injected model outlines a proof of `statement` in words; Lean checks nothing. ## Reading the result The reply gives `kind`, `statement` (as Lean prints it), `statement_matched`, `axioms`, `messages` (Lean's file, line, column and text, on your own line numbers) and `refusals` (what the screen refused, with line and column). - `qualified-draft`, `G6_E_LEAN_PROOF_CHECKED` (on `warning_card.code`; the result's own `code` is empty): Lean's kernel accepted a proof of exactly this statement, with only the axioms listed. Whether the statement says what you mean, with the definitions in your file, is for a person to inspect; nothing here is `verified`. - `blocked-escalated`: `G6_E_LEAN_SORRY`, `G6_E_LEAN_NONSTANDARD_AXIOM`, `G6_E_LEAN_AXIOM_DECLARED`, `G6_E_LEAN_STATEMENT_MISMATCH`, `G6_E_LEAN_THEOREM_MISSING`, `G6_E_LEAN_NOT_A_THEOREM`, `G6_E_LEAN_CODE_EXECUTION`, `G6_E_LEAN_IMPORT_REFUSED`, `G6_E_LEAN_ELABORATION_FAILED`, `G6_E_LEAN_LEAN_MISSING` and the other screen codes. `prove` returns `proof`, the `source` checked and `check` (that `check_proof` result, unchanged) on `G6_E_LEAN_PROOF_FOUND`, `qualified-draft`; `candidates` lists each one tried with its code. It is `blocked-escalated` with `G6_E_LEAN_SEARCH_EXHAUSTED` when none passed, or at once with Lean's own code when no candidate could change the outcome (Lean missing, with how to install it; a statement Lean prints otherwise), and with `G6_E_LEAN_TIME_LIMIT` when the deadline passes. A model reply that declares its own theorem is `G6_E_LEAN_PROPOSAL_REFUSED` and never checked. `sketch` is `qualified-draft` with `G6_E_LEAN_SKETCH_UNCHECKED`, a draft only; with no model, `G6_E_LEAN_NO_MODEL`. ## Alone and composed Alone, from Python: `from g6.lean import check_proof, prove, sketch` (`prove(statement, llm=your_caller)`), or `LeanBlock(llm=your_caller)`; `get_registry().invoke("lean", ...)` builds a block with no model unless you pass `llm=`. Over MCP: `g6_invoke("lean", "check_proof", params_json)`, which runs only when the person running the server allows it; `ops` and `help` answer anywhere. Over MCP, `prove` and `sketch` use the model the person set in `~/.g6/.env` (for example `G6_LLM_PROVIDER=claude_code`), and with none `prove` uses the core tactics only. Composed: - Decide the claim with `formal` first when it is decidable; write the Lean proof for what needs one. - Give `coding_agent_gate`'s `verify_answer` the reply as an evidence item of kind `log` before reporting a proof as done (it counts as caller-declared), and hand the printed `statement` to a person to inspect. ## Failure modes it catches - `unfinished_proof` (a proof that only looks finished): the screen refuses `sorry`, `admit`, `stop`, `axiom` and `opaque` declarations and anything that runs code (`#eval`, `native_decide`, `unsafe`, `implemented_by` and the like) before Lean runs; then Lean's own `#print axioms`, on lines G6 appends and marks with a nonce the source cannot know, must list only `propext`, `Classical.choice` and `Quot.sound`. `sorryAx` is `G6_E_LEAN_SORRY`, any other axiom `G6_E_LEAN_NONSTANDARD_AXIOM`. - `misstated_theorem` (a proof of a different statement from the one you meant): with `expected_statement`, the statement Lean prints must match it, and Lean must accept the theorem as a proof of the expected statement read on its own, so `2 + 2 = 4` over `Fin 3` does not pass for one over the naturals (`G6_E_LEAN_STATEMENT_MISMATCH`). Lean must also report the name unknown before your source, so a theorem from an import cannot pass as yours. - `unchecked_maths_claim` (a maths claim accepted without a machine-checked proof): `prove` takes a candidate only when `check_proof` passed it for `g6_goal` and your statement, by Lean's report, whatever the model says of its own proof; G6 writes the theorem line itself, so a reply cannot swap in another statement. ## Blind spots - Without `expected_statement`, a proof of some other statement under the right name passes; give it whenever you can. - A statement can match and still not mean what the claim in words means, through a definition or instance in your file. A person must read it. - The screen refuses whole classes of constructs (new syntax, most attributes and options, imports beyond `Init` and `Std`), so some sound proofs are refused, not checked. - The screen reads comments and strings too: `theorem two_add : 2 + 2 = 4 := rfl -- not sorry` is refused with `G6_E_LEAN_SORRY`. Keep refused words out of comments. - Trust rests on Lean's kernel and the standard axioms; G6 does not check Lean itself. - Time and memory caps can stop a long elaboration. - `prove` tries a fixed order and only `Init` and `Std`: tactics from Mathlib (`ring`, `linarith`) are not there, and a model proposing them fails. Write the statement as Lean prints it (`2 + 2 = 4`, not `2+2=4`), or every candidate is a mismatch. - A `sketch` is a model's words: it may be wrong and nothing checks it. - `deadline_s` cannot cut a model call short (the caller takes no time limit): a proposal can overrun the deadline by up to your caller's own timeout; the search then stops before checking anything more. ## What it needs - Lean 4, an outside program G6 never installs. Install elan, Lean's toolchain manager, as the Lean project describes at lean-lang.org (it puts `lean` on PATH), or set `G6_LEAN` to a `lean` executable. G6 never installs a toolchain, Lake or Mathlib and never downloads anything. No pip extra. - Over MCP the three Lean ops run only when the person running the server lists them (or `lean`) in `G6_MCP_SENSITIVE`; an agent never edits that setting to grant itself one. - For model proposals and sketches, your own `g6.core.llm` caller, passed from Python; G6 never picks a model. ## Examples ```g6-example op=check_proof {"source": "theorem two_add : 2 + 2 = 4 := rfl", "theorem": "two_add", "expected_statement": "2 + 2 = 4"} ``` With Lean installed and the op allowed, the reply is `qualified-draft` with `G6_E_LEAN_PROOF_CHECKED`, `kind` `theorem`, `statement` `2 + 2 = 4`, `statement_matched` true and `axioms` empty. The screen and the declaration check run before Lean is looked for, so this runs anywhere: ```python from g6.lean import check_proof gap = check_proof("theorem two_add : 2 + 2 = 4 := by sorry", "two_add") assert gap.completion_state == "blocked-escalated" and gap.code == "G6_E_LEAN_SORRY" assert gap.refusals[0]["line"] == 1 assert check_proof("-- nothing here", "two_add").code == "G6_E_LEAN_THEOREM_MISSING" assert check_proof("def two_add : Nat := 4", "two_add").code == "G6_E_LEAN_NOT_A_THEOREM" ``` `sketch` with no model configured says so, and how to configure one: ```g6-example op=sketch {"statement": "∀ (n : Nat), n + 0 = n"} ``` A model's reply that declares its own theorem is refused before any check, and a sketch is labelled a draft: ```python from g6.core.llm import FakeLLM from g6.lean import prove, sketch swapped = prove("2 + 2 = 5", llm=FakeLLM(default="theorem g6_goal : True := trivial"), tactics=(), budget=1) assert swapped.completion_state == "blocked-escalated" and swapped.code == "G6_E_LEAN_SEARCH_EXHAUSTED" assert swapped.candidates[0]["code"] == "G6_E_LEAN_PROPOSAL_REFUSED" draft = sketch("2 + 2 = 4", llm=FakeLLM(default="Both sides reduce to 4.")) assert draft.warning_card["code"] == "G6_E_LEAN_SKETCH_UNCHECKED" and draft.check is None ``` ## The four open problems - **Learning signals.** Not yet: it writes nothing to a run ledger itself. Each catch is a labelled result with its own `G6_E_LEAN_*` code, Lean's messages and the screen's refusals in `evidence`; `prove` lists each candidate's code, the success per tactic on your own goals; recording it is the caller's step today. - **Evaluators the model cannot influence.** The judge is Lean's own report, read from lines G6 appends with a nonce the source cannot know, plus the package's own screen. A model only proposes text for `prove`; Lean, through `check_proof`, judges it. The caller supplies the proof, never the check. - **Transfer by failure mode.** The checks belong to proofs, not to a task: an axiom list or a `sorry` means the same in any file. Not yet: its catch rate on task families it was not built on has not been measured, nor any transfer of the tactic order by goal shape. - **Enough held-out cases.** Not yet: its own catch rate and false-alarm rate are not judged by a sequential test; the tactic order is fixed, not tuned on any goals. A repeated workflow that uses it can be tuned with `g6.metaharness`, whose gate judges a change on held-out cases.
View on GitHub