From Prompts to Protocols

AI-Assisted Lean Development with lean4-skills
Cameron Freer
Research Scientist (MIT)
Guest Lecture
UCSD Math 157: Mathematical Formalization
Kiran Kedlaya
Friday, May 29, 2026
Lean

The workflow is a bottleneck we can control

LLM coding agents are already useful Lean copilots. For large formalizations, model intelligence is not the only bottleneck. One bottleneck we can control is the workflow: knowing when to search, inspect, edit, checkpoint, review, and stop.

Prompt

Useful, but local and easy to dilute across a long session. The agent is left to infer how to behave.

Protocol

  • what to search
  • when to inspect the goal
  • when to trust Lean’s feedback
  • when to stop, checkpoint, review, replan
  • how to keep the human in control
Lean

After the fundamentals

  • You have already seen Lean and Mathlib, tactics, blueprints, and AI-assisted formalization. I’ll assume you know what goals and tactics look like.
  • So this is not “what is Lean?” It is: how do we engineer a human–agent–Lean workflow that survives a real project, one that lasts weeks or months?
  • Lean makes AI assistance unusually attractive: there is an external judge. The model proposes edits; Lean checks whether the proof term exists.
  • But Lean checks a proof against a statement. It does not tell you whether the statement is the theorem you meant, whether the API is maintainable, or whether the proof survives a Mathlib update.
Lean

Contents

  1. A formalization story: de Finetti in Lean.
  2. Why good models still need workflow.
  3. How lean4-skills turns prompts into a protocol.
  4. Demo: a real captured session.
  5. Evaluation: how do we know it improved?
  6. What you can use on your own projects.
Lean

de Finetti’s theorem

An exchangeable {0,1}-sequence: the joint law is unchanged by permuting finitely many coordinates. Informally, the order of the observations carries no information.

A canonical way to get an exchangeable sequence:

  1. first draw a hidden bias Θ;
  2. then flip i.i.d. Bernoulli(Θ) coins.

It turns out this is the general story.

The theorem says this is the general picture: there is a latent random probability measure, conditional on which the sequence is i.i.d.

The Lean formalization is the standard-Borel, measure-theoretic version of this story. This involves kernels, conditional laws, and uniqueness of product measures.

Lean

de Finetti–Ryll-Nardzewski in Lean 4

Informally: an infinite exchangeable sequence (one whose joint law is invariant under finite permutations of coordinates) is conditionally i.i.d., given a directing random probability measure.

Ryll-Nardzewski’s formulation packages this as an equivalence involving contractability; exchangeability is one side of that equivalence.

The library’s headline theorem, stated in Lean:

theorem deFinetti_RyllNardzewski_equivalence
    [StandardBorelSpace Ω]
    {α : Type*} [MeasurableSpace α] [StandardBorelSpace α] [Nonempty α]
    {μ : Measure Ω} [IsProbabilityMeasure μ]
    (X : ℕ → Ω → α) (hX_meas : ∀ i, Measurable (X i)) :
    Contractable μ X ↔ Exchangeable μ X ∧ ConditionallyIID μ X

The proof engineering involves standard Borel spaces, conditional laws as kernels, conditional independence, product-measure uniqueness, and L¹/L² convergence.

Lean

2.5 pages of math → tens of thousands of lines of Lean

~2.5 ppKallenberg’s three short proofs
interfaces, APIs, bridge lemmas, checking
42,745lines · 112 files (ITP paper)
Mathlib · golfing · better models
26,767lines · 105 files (main, 2026-05-27)
  • The blow-up is not just verbosity. It is bridging informal math to existing Mathlib APIs: conditional expectation, kernels, σ-algebras, product-measure uniqueness, L¹/L² transport.
  • Since the ITP artifact, the project has shrunk by ~37% through paper-review cleanup: more Mathlib reuse, proof golfing, dead-code removal, and stronger model-assisted refactoring.
  • Formalization is library and interface engineering, and it keeps evolving: as Mathlib grows and tooling improves, the artifact gets smaller.
Lean

Three roads to the same theorem

Core symmetry API exchangeability, contractability Canonical σ-algebras conditioning interface Shared infrastructure conditional-expectation & -independence bridges · tail σ-algebras product-measure uniqueness π-system bridge lemmas Route A reverse martingales (standard Borel) Route B L² / CDF / Stieltjes (real L²) Route C Koopman / mean ergodic (real L²) Common ending finite conditional factorization → probability kernel → conditionally i.i.d. de Finetti–Ryll-Nardzewski equivalence

Three independent routes discharge one shared interface.

Lean

One common ending

All three routes had to produce the same intermediate interface: finite conditional factorization relative to a sub-σ-algebra. From there a single shared “common ending” builds:

  1. a directing probability kernel;
  2. the right measurability package;
  3. the conditionally i.i.d. conclusion.

Module import graph for the exchangeability project

100+ files converging on a shared interface. The common ending is both a mathematical interface and a workflow guardrail.

Lean

The common ending as supervision

Forcing every route through one shared interface turns divergence into an error signal.

Three independent routes feeding one interface gave continual cross-checks. They caught:

  • route-specific definitions creeping into shared APIs;
  • missing measurability hypotheses;
  • overly specialized helper lemmas;
  • statement drift: locally convenient but globally wrong interfaces.

For AI-assisted formalization this matters: the agent gets a hard, reusable target, and divergence between routes is immediately visible.

Lean

Predictable failures without guardrails

Coding agents are often surprisingly useful at Lean. But without guidance, agents do not automatically follow the discipline that Lean developers learn over time. Common failure modes (stated charitably):

  • Reprove a lemma Mathlib already has.
  • Ignore Lean diagnostics or the goal state.
  • Change the theorem statement to make the proof easier.
  • Add brittle local adapters instead of using Mathlib idioms.
  • Paper over typeclass problems by adding more instances.
  • Loop on the same stalled proof path.
  • Produce code that compiles but is slow, obscure, or hard to upstream.

These operational failures are predictable consequences of optimizing locally without enough process.

Lean

From a prompt file to a protocol layer

lean4-skills grew out of the de Finetti project. When an agent hit a rough spot (a bad instance, a skipped Mathlib search, mishandled conditional-expectation bookkeeping), I often moved the relevant context into a separate skill-development session. That session folded in the new knowledge, keeping enough of the real example to stay concrete, and turned it into reusable guidance.

  • Recurring situations became reference files.
  • Repeated action sequences became command workflows.
  • Tool-use lessons became Lean LSP MCP guidance.
lean4-skills is a workflow pack for Lean coding agents: references, command recipes, subagents, hooks, scripts, and checks around the model.

The shift: from wording a single prompt to structuring the whole interaction: search, inspect, bound, checkpoint, review, replan, hand off.

Lean

Protocol countermeasures

Generic agent failureProtocol countermeasure
Reproves existing Mathlib lemmasSearch Mathlib & local code before proving
Misses diagnostics or the exact goalInspect LSP goals & diagnostics before editing
Changes the statement to make it easierStatement/header stability rule + human approval
Adds brittle local adaptersPrefer Mathlib idioms & reusable bridge lemmas
Loops on a stalled proof pathBounded attempts + stop / replan / handoff
Compiles, but unmaintainableReview → refactor → golf → minimize imports
Hides trust-base changeslake build, sorry checks, axiom checks

Each row is a failure observed in real agent work, paired with the protocol response.

Stopping with a clear obstruction is a success, not a failure. It lets the user replan, and keeps the coding agent from going in circles.

Lean

The lean4-skills architecture

Humanowns statements & review
LLM coding agentproposes edits, searches
lean4-skills protocol layer
Referencesconventions, idioms, failure modes
Commands/draft /formalize /prove /review /refactor /golf /learn …
Agents · hooks · scriptsproof repair, sorry/axiom checks, import min.
Bounded proof loopplan · work · checkpoint · review · replan
Lean kernelauthoritative judge
Mathlib + projectreusable APIs
lean-lsp-mcpgoals, diagnostics, search

The skill does not bypass Lean. Lean and Mathlib feedback stays external and authoritative.

Lean

Anatomy: a skill is not one prompt

lean4-skills is a layered Lean workflow pack. The current plugin distribution targets Claude Code, but the core skill content is mostly host-agnostic. The model sees a short contract first, and pulls in detail only when the situation calls for it.

  • SKILL.md: a short, always-visible contract.
  • commands/ (11): /draft, /formalize, /autoformalize, /prove, /autoprove, /checkpoint, /review, /refactor, /golf, /learn, /doctor.
  • agents/ (4): proof-repair, proof-golfer, sorry-filler-deep, axiom-eliminator.
  • references/ (39): cycle engine, LSP tools, Mathlib search, compilation errors, measure theory, instance pollution, golfing, performance, and more.
  • Scripts and hooks: sorry analysis, axiom checks, search wrappers, import minimization, a command-argument parser, UserPromptSubmit validation, and shell guardrails.

Durable rules stay in front of the model; specialized advice loads only when it is relevant.

Lean

Progressive disclosure: the always-visible contract

SKILL.md is deliberately short and normative: a small constitution the agent sees on every task.

  • Search before prove.
  • Build incrementally (the type-checker is the test suite).
  • Respect scope: one sorry, a file, or everything (ask if unclear).
  • Use Lean / Mathlib conventions; 100-character lines.
  • Never change declaration headers, theorem statements, or signatures, or add axioms, without permission.
  • Prefer LSP goal / diagnostic / search tools before script fallback.
  • Use commands for multi-cycle work; finish with checks (build, no stray sorries, standard axioms, statements preserved).

A routing layer: pick the workflow first, then disclose the right details.

Lean

SKILL.md: the contract, from the source

plugins/lean4/skills/lean4/SKILL.mdcropped source excerpt · v4.4.10 · eae7b88
## Core Principles

**Search before prove.** …
**Build incrementally.** …
**Respect scope.** …
**Use 100-character line width for Lean files.** …
**Never change statements or add axioms without explicit permission.** …

This is the part the model sees every time: a small operational constitution, not one clever paragraph.

Lean

Where each lesson goes

Recurring issueWhere it livesWhy there
Reproving Mathlib lemmasMathlib-search guidea habit, not theorem-specific
Statement drifttop-level rule + header fencemust be visible every time
Conditional-expectation bookkeepingmeasure-theory referencedomain-specific, high detail
Instance pollution / timeoutsinstance-pollution referenceimportant but situational
Repeated sorry-filling loop/prove · /autoprovea workflow, not a note
Repetitive or longwinded proof/golf + proof-golferpost-success optimization

Rule of thumb: universal invariant → SKILL.md; detailed Lean lore → reference; repeated procedure → command; bounded specialist task → subagent or script.

Lean

How it grew: patterns and antipatterns → references → workflows → contracts

  • Oct 2025: During the de Finetti project I wrote down the failures that cost me time (conditional expectation, σ-algebra bookkeeping, instance pollution, elaboration timeouts, Mathlib-search misses, statement drift) into a reference library; progressive disclosure appeared almost immediately.
  • Oct 2025: Repeated manual workflows became slash commands.
  • Jan–Feb 2026: Lean LSP MCP guidance; guided /prove split from autonomous /autoprove; a shared cycle engine; build-verification ladder; /learn.
  • Mar 2026: MCP-first subagents, header fences, capability profiles, and /refactor.
  • Apr–May 2026: Hardening: stop budgets, one-editor-per-file rules, scratch-file policy, portability, CI, and community PRs.

When a failure repeated often enough, I wrote it down as a reference, promoted it to a workflow, or hardened it into a contract.

Lean

Where the protocol is actually enforced

modelSKILL.md: “do not change statements”advisory
toollean-lsp-mcp: real goals and diagnostics as evidence, not model guessworkevidence
scriptsorry_analyzer.py and axiom checks flag stray sorries and axiomsevidence/gate
hookvalidate_user_prompt.py and guardrails.sh (blocking for covered cases)blocking
kernellake build and Lean checking (a sorry still builds, with a warning)blocking

Not every rule is enforced at the same layer. Part of building the skill was deciding whether a rule should be a prompt, a tool habit, a script, a hook, or a build gate. Sorry-freedom and standard-axiom checks are separate gates.

Lean

The bounded proof loop

Plan target, scope, allowed changes Work search, inspect goals, test tactics, edit Checkpoint file/build check, sorry & axiom checks Review → Replan classify failure; split, refactor, re-route Continue? loop, or stop Handoff ✓ summary, remaining goals, risks no yes
  • The loop is the same whether the agent is driven interactively or runs autonomously with a budget.
  • Stopping early counts as success. A clear obstruction summary beats another hour of failing edits.
  • It externalizes the routine discipline a good Lean user learns: search, inspect, test, checkpoint, review, and stop when stuck.
Lean

/lean4:prove in detail

A slash command constrains the process, not just the final answer. /prove runs that bounded loop one cycle at a time, asking before each new cycle:

  • Plan: discover sorries via LSP; search Mathlib (up to 3 tools, ~30s); show the plan and confirm.
  • Work: refresh the goal, search, generate 2–3 candidates, test with lean_multi_attempt, validate diagnostics, apply Lean’s “Try this” code actions, then edit.
  • Checkpoint: stage only accepted files; build the agreed scope.
  • Review: read-only quality gate (drift, API, performance).
  • Replan: split, refactor, escalate to deep mode, or mark stuck.
  • Continue? ask the human before the next cycle.

The fast path caps candidates per sorry, keeps diffs small, forbids cross-file refactors, and treats declaration headers as immutable; deep mode adds snapshot and rollback under a line budget.

Lean

/lean4:prove: the agent may not change the theorem

plugins/lean4/commands/prove.mdcropped source excerpt · v4.4.10 · eae7b88
**Constraints:** … NO statement changes, … Declaration headers are immutable — if deep mode suggests a header change, it must stop and recommend `/lean4:formalize`.

…

## Stuck Definition

A sorry is **stuck** when: same failure 2-3x, same build error 2x, …

**When stuck:** … Handoff must include LSP queries attempted, top candidates, and `lean_multi_attempt` outcomes.

The agent does not get to prove a different theorem. When stuck, it stops with evidence.

Lean

Subagent: proof-repair

A subagent is a specialized prompt with a narrow input/output contract. proof-repair turns an antipattern (“after a local error, rewrite too much”) into an interface: structured error context in, a unified diff out.

plugins/lean4/agents/proof-repair.mdcropped source excerpt · v4.4.10 · eae7b88
## Constraints

- Output ONLY unified diff (no explanations)
- Change ONLY 1-5 lines per call
- Stay within stage budget
- May NOT rewrite entire functions
- May NOT try random tactics
- May NOT skip mathlib search
- May NOT modify declaration headers (header fence). …
…

Intentionally boring: classify the error, make the smallest plausible edit, return a diff. That is an antipattern promoted to a contract.

Lean

What kind of prompting is this?

Mostly operational and constraint-heavy: what evidence to collect, what not to touch, when to escalate, and what shape the output takes.

  • Before proving: inspect the goal; search Mathlib; record candidate lemmas.
  • While proving: try only 2–3 candidates; test with Lean; prefer the simplest robust passing candidate; don’t edit declaration headers.
  • When failing: classify the blocker; don’t loop on one error; summarize searches and attempts; replan or hand back.
  • When repairing or finishing: a minimal 1-5 line diff, no random tactics; check diagnostics, sorries, axioms; preserve statements.
The prompt is less “be good at Lean” and more “behave like a careful Lean collaborator.” That is also why it transfers across models and hosts.
Lean

lean-lsp-mcp: the agent’s InfoView and Mathlib search

Humans use the Lean InfoView to read goals, hypotheses, errors, and types, and they search Mathlib for lemmas. lean-lsp-mcp gives the agent analogous feedback and search tools. Without it, the agent loses much of the feedback loop human Lean users rely on.

See the stategoals, hypotheses, diagnostics
Understand nameshover, declaration file, outline
Test editsrun code, verify, build
Search the libraryLeanSearch, Loogle, local search

The protocol tells the agent when to reach for each: search before proving, inspect the goal before editing, verify before claiming done.

Lean

Demo transcript

One small Lean snippet, three easy sorries, captured with Claude Code / Opus 4.8. Prompt:

❯ let's work on these: example (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by sorry
  example [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card := by sorry
  example (p q : Prop) : p ∧ q → q ∧ p := by sorry

The theorems are intentionally small so the workflow is visible. In a hard theorem the same loop is still there, just harder to see because the math dominates.

Lean
 ▐▛███▜▌   Claude Code v2.1.156
▝▜█████▛▘  Opus 4.8 (1M context) with xhigh effort · Claude Max
  ▘▘ ▝▝    ~/work/spherepacking-repos/Sphere-Packing-Lean


❯ let's work on these: example (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by sorry
  example [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card := by sorry
  example (p q : Prop) : p ∧ q → q ∧ p := by sorry


 I'll start by invoking the Lean 4 skill since we're working on Lean proofs.

 Skill(lean4:lean4)
    Successfully loaded skill

 These are three straightforward warm-up examples. Let me verify the proofs compile using the Lean REPL.

 Now let me verify all three proofs compile:

  Called lean-lsp (ctrl+o to expand)

 I need to prime the project path first. Let me check a file in the project.

  Searched for 1 pattern, called lean-lsp 2 times (ctrl+o to expand)

 The proofs are correct — the only errors are because autoImplicit is off in this project, so I need to explicitly bind α. Let me add the type variable:

  Called lean-lsp (ctrl+o to expand)

 All three compile cleanly (the remaining message is just a whitespace-linter artifact from the snippet layout). Here are the proofs:

  example {α : Type*} (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by
    rw [List.length_append]

  example {α : Type*} [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card :=
    Finset.card_le_card h

  example (p q : Prop) : p ∧ q → q ∧ p :=
    fun ⟨hp, hq⟩ => ⟨hq, hp⟩

  Notes:

  1. List length — List.length_append is the statement, so rw closes it. Even shorter: := List.length_append (term mode) or by simp.
  2. Finset card — Finset.card_le_card : s ⊆ t → s.card ≤ t.card applies directly. (DecidableEq isn't actually needed for this lemma, but it's harmless to keep.)
  3. Conjunction swap — pure term mode: destructure the hypothesis and rebuild swapped. Equivalent alternatives: fun h => ⟨h.2, h.1⟩ or And.comm.mp.

  One thing to flag: your originals wrote List α / Finset α without binding α. This project sets autoImplicit false, so I added {α : Type*} explicitly — without it the snippets don't compile
  here. If you're dropping these into a section that already has a variable {α : Type*}, you can omit the per-example binder.

  Want me to place these somewhere in the repo, or were they just warm-up exercises?

✻ Worked for 1m 30s
Lean

Transcript detail: Lean feedback changes the answer

 ▐▛███▜▌   Claude Code v2.1.156
▝▜█████▛▘  Opus 4.8 (1M context) with xhigh effort · Claude Max
  ▘▘ ▝▝    ~/work/spherepacking-repos/Sphere-Packing-Lean


❯ let's work on these: example (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by sorry
  example [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card := by sorry
  example (p q : Prop) : p ∧ q → q ∧ p := by sorry


03:08 PM claude-opus-4-8
 I'll start by invoking the Lean 4 skill since we're working on Lean proofs.

 Skill(lean4:lean4)
    Successfully loaded skill

03:08 PM claude-opus-4-8
 These are three straightforward warm-up examples. Let me verify the proofs compile using the Lean REPL.

03:08 PM claude-opus-4-8
 Now let me verify all three proofs compile:

 lean-lsp - Run Code (MCP)(code: "import Mathlib\n\nexample (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by\n  rw [List.length_append]\n\nexample [DecidableEq α] (s t :
  Finset α) (h : s ⊆ t) : s.card ≤ t.card :=\n  Finset.card_le_card h\n\nexample (p q : Prop) : p ∧ q → q ∧ p :=\n  fun ⟨hp, hq⟩ => ⟨hq, hp⟩")

03:09 PM claude-opus-4-8
 I need to prime the project path first. Let me check a file in the project.

 Bash(find /home/freer/work/spherepacking-repos/Sphere-Packing-Lean -name "*.lean" -path "*ContourEndpoints*" 2>/dev/null | head -5)
    /home/freer/work/spherepacking-repos/Sphere-Packing-Lean/SpherePacking/MagicFunction/a/ContourEndpoints.lean

 lean-lsp - Diagnostics (MCP)(file_path: "/home/freer/work/spherepacking-repos/Sphere-Packing-Lean/SpherePacking/MagicFunction/a/ContourEndpoints.lean")
    {
       "result": {
         "success": false,
         "timed_out": false,
         "items": [],
         "failed_dependencies": []
       }
     }

 lean-lsp - Run Code (MCP)(code: "import Mathlib\n\nexample (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by\n  rw [List.length_append]\n\nexample [DecidableEq α] (s t :
  Finset α) (h : s ⊆ t) : s.card ≤ t.card :=\n  Finset.card_le_card h\n\nexample (p q : Prop) : p ∧ q → q ∧ p :=\n  fun ⟨hp, hq⟩ => ⟨hq, hp⟩")
    {
       "success": false,
       "timed_out": false,
       "diagnostics": [
         {
           "severity": "error",
           "message": "Unknown identifier `α`\n\nNote: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.",
           "line": 3,
           "column": 23
         },
         {
           "severity": "error",
           "message": "Unknown identifier `α`\n\nNote: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.",
           "line": 3,
           "column": 23
         },
         {
           "severity": "error",
           "message": "Unknown identifier `α`\n\nNote: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.",
           "line": 6,
           "column": 22
         },
         {
           "severity": "error",
           "message": "Unknown identifier `α`\n\nNote: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.",
           "line": 6,
           "column": 39
         },
         {
           "severity": "error",
           "message": "Unknown identifier `α`\n\nNote: It is not possible to treat `α` as an implicitly bound variable here because the `autoImplicit` option is set to `false`.",
           "line": 6,
           "column": 39
         },
         {
           "severity": "warning",
           "message": "'' starts on column 26, but all commands should start at the beginning of the line.\n\nNote: This linter can be disabled with `set_option linter.style.whitespace
     false`",
           "line": 10,
           "column": 27
         }
       ]
     }

03:09 PM claude-opus-4-8
 The proofs are correct — the only errors are because autoImplicit is off in this project, so I need to explicitly bind α. Let me add the type variable:

 lean-lsp - Run Code (MCP)(code: "import Mathlib\n\nexample {α : Type*} (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by\n  rw [List.length_append]\n\nexample {α : Type*}
  [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card :=\n  Finset.card_le_card h\n\nexample (p q : Prop) : p ∧ q → q ∧ p :=\n  fun ⟨hp, hq⟩ => ⟨hq, hp⟩")
    {
       "success": true,
       "timed_out": false,
       "diagnostics": [
         {
           "severity": "warning",
           "message": "'' starts on column 26, but all commands should start at the beginning of the line.\n\nNote: This linter can be disabled with `set_option linter.style.whitespace
     false`",
           "line": 10,
           "column": 27
         }
       ]
     }

03:10 PM claude-opus-4-8
 All three compile cleanly (the remaining message is just a whitespace-linter artifact from the snippet layout). Here are the proofs:

  example {α : Type*} (xs ys : List α) : (xs ++ ys).length = xs.length + ys.length := by
    rw [List.length_append]

  example {α : Type*} [DecidableEq α] (s t : Finset α) (h : s ⊆ t) : s.card ≤ t.card :=
    Finset.card_le_card h

  example (p q : Prop) : p ∧ q → q ∧ p :=
    fun ⟨hp, hq⟩ => ⟨hq, hp⟩

  Notes:

  1. List length — List.length_append is the statement, so rw closes it. Even shorter: := List.length_append (term mode) or by simp.
  2. Finset card — Finset.card_le_card : s ⊆ t → s.card ≤ t.card applies directly. (DecidableEq isn't actually needed for this lemma, but it's harmless to keep.)
  3. Conjunction swap — pure term mode: destructure the hypothesis and rebuild swapped. Equivalent alternatives: fun h => ⟨h.2, h.1⟩ or And.comm.mp.

  One thing to flag: your originals wrote List α / Finset α without binding α. This project sets autoImplicit false, so I added {α : Type*} explicitly — without it the snippets don't compile
  here. If you're dropping these into a section that already has a variable {α : Type*}, you can omit the per-example binder.

  Want me to place these somewhere in the repo, or were they just warm-up exercises?

✻ Worked for 1m 30s
Lean

/learn: keeping human understanding in the loop

When agents write more of the proof, humans can learn less, unless explanation is built into the workflow. /learn explores Mathlib around a topic, explains why a lemma applies, and grounds informal claims in checked definitions.

  Pedagogy: Socratic mode, starting from the user’s prior knowledge to calibrate depth.

  What does it mean for a function to be “measurable” between two measurable spaces?
  If someone hands you f : α → β and both carry a MeasurableSpace instance,
  what property must f satisfy?
When explanation is built into the workflow, the user learns the mathematics instead of just collecting opaque proof scripts. /learn is not “explain after the fact”; it keeps the human inside the proof loop.
Lean

How do we know it got better?

If I change the skill, how do I know the change helped? There is no single number.

Quality is layered, and a change can improve one layer while harming another:

  • A change can be good because it makes the agent less aggressive: stop sooner, search first, hand off more clearly.
  • A change can prove more toy theorems while making real projects worse by rewarding brittle hacks.
Lean

A practical evaluation harness

Fix

  • repo + Lean/Mathlib version
  • task set: sorries, refactors, reviews
  • model version + budget
  • skill version A vs B

Record

  • build success; statement changes
  • unintended axioms / remaining sorries (e.g. #print axioms deFinetti → propext, Quot.sound, Classical.choice)
  • imports added; local lemmas introduced
  • proof elaboration time; search calls
  • failed edit loops; obstruction-summary quality

Review

  • Maintainable?
  • Used existing APIs?
  • Stopped at the right time?
  • Would I keep the diff?
The unit of evaluation is not “did the theorem close?” It is “would I merge this diff, and did the trace make review easier?”
Lean

The evaluation stack

Teaching & understandingCan the human explain what the agent did?
Workflow behaviorBounded attempts? Checkpoints? Clear obstruction summaries?
MaintainabilityReadable, fast, small imports, localized instances?
Mathlib integrationDid it search first? Avoid duplicate lemmas?
Statement / API qualityRight statements, hypotheses, generality?
ReproducibilityPinned toolchain? CI? Repeatable scripts?
Kernel correctnessDoes it build? Any unintended sorries or axioms? (necessary, not sufficient)

Kernel correctness is the floor; quality and workflow gates sit above it. A failed attempt can still improve the workflow if it stops earlier with a useful obstruction summary.

Lean

Why A/B testing skill changes is hard

  • Model behavior is stochastic.
  • Long Lean tasks branch heavily, with many hidden paths.
  • Skill changes often affect tool-use patterns indirectly.
  • A “successful” proof can be low quality; a “failed” one useful if its obstruction summary is accurate.
  • One benchmark can reward brittle behavior a real project would reject.

So evaluation mixes:

regression tasks · build/sorry/axiom checks · trace review · qualitative failure analysis · project dogfooding · user reports · benchmark batches when available.

Lean

/lean4:golf: golfing is review, not line-count hacking

plugins/lean4/commands/golf.mdcropped source excerpt · v4.4.10 · eae7b88
Score candidates by: correctness → directness → clarity/inference burden → performance/determinism → length. …

**Hard reject if:** introduces naked `;` · introduces `<;>` on non-identical goals (per semicolon policy) … collapsed term > ~80 chars or dot-chain > 2 …

A shorter proof can be worse; golfing is a win only if it stays at least as direct, clear, and deterministic.

Lean

/lean4:review: evaluation as a command

plugins/lean4/commands/review.mdrendered · v4.4.10 · eae7b88

Read-only review of Lean proofs for quality, style, and optimization opportunities.

Scope levels:

ScopeDescription
sorrySingle sorry at --line (requires target file + --line)
depsSorry + same-file helpers and directly referenced lemmas
fileAll sorries in target file
changedFiles modified since last commit (git diff)
projectEntire project (requires confirmation)

next_action (stuck mode): continue (retryable), deep (needs escalation), repair (compiler blocker), redraft (statement-shape blocker), golf (sorry-free), stop (no path).

A review command makes quality checks repeatable. It reports the project’s state; it does not prove anything.

Lean

How skill changes evolve

Add capability

  • record proof patterns
  • search methods
  • golfing strategies
  • common errors

Consolidate capability

  • compress references
  • improve discoverability
  • add commands, simplify control flow
  • remove sharp edges
References are lower-risk when accurate and discoverable: the model may ignore them, but they rarely force behavior. Commands, subagents, hooks, and scripts need stricter review: they actively shape behavior.
Lean

Projects where I’ve refined or used lean4-skills

exchangeability: de Finetti, the origin story (measure-theoretic probability)
graphon: graph limits, cut distance, weak regularity, counting lemmas
infinitary-logic: L∞ω, Lω₁ω, Scott sentences, Karp’s theorem
Sphere-Packing-Lean: Viazovska’s dim-8 theorem; maintained by Birkbeck, Hariharan, Mehta, Lee
Noperthedron: certificate / small-checker style around the Steininger–Yurkevich non-Rupert result; Lean by Reed & Renshaw

The workflow is portable (measure theory, combinatorics, logic, computation), but each domain needs its own interfaces and review norms.

E8 root system (Petrie projection)

E8 root system. (J. Gregory Moxness)

A cube has Rupert’s property. (David Renshaw / Quanta)

The Noperthedron

The Noperthedron does not. (David Renshaw)

Lean

Where this is heading

  • Harder proofs, still bounded: parallel branch search, budget-aware conclusion phases, multiple independent proof attempts with review gates, durable stuck/handoff artifacts.
  • More of the formalization-engineering loop: commands for theorem generalization and lemma extraction; session proof notebooks and typed run artifacts.
  • Richer agent feedback: semantic and name-based Mathlib search, minimal-hypothesis extraction, more robust lean-lsp-mcp startup.
  • Review beyond hygiene: from proof cleanup to full Mathlib-style review (naming, generality, API), with a shared review taxonomy.
  • Upstreaming as a workflow: pre-contribution readiness checks, import and docstring hygiene, attribution tracking, CI/blueprint/docs scaffolding.
  • Shared infrastructure: a host-neutral core with adapters for many agents and IDEs; community patterns and insights flowing back; realistic shared benchmarks.

Some of this is already live; some is active design work, tracked in the open issues and PRs on lean4-skills and lean-lsp-mcp.

Lean is the judge, not the whole reviewer.
Formalization is interface engineering.
AI assistance improves when embedded in a checked workflow.
Good protocols keep the human in charge.

Stronger models help; the leverage is in the protocols that let humans, agents, Lean, and Mathlib work together.

Cameron Freer
Research Scientist (MIT) · freer@mit.edu
UCSD Math 157 · May 29, 2026