Isabelle Automation
Python tooling for automated theorem proving in Isabelle
Isabelle is an interactive prover: a person writes a theory, and the prover
checks it and shows the proof state as they go. Automating that loop — reading
proof states, trying proof methods, replacing a sledgehammer call with the
proof it found — needs programmatic access to what the editor sees. These
Python packages provide it, both from a running Isabelle and statically from
the theory files alone. All four are MIT-licensed and work in progress.
Packages
- python-lsp-client
(
lsp_client) — an asynchronous client for the Language Server Protocol, with the protocol’s messages as pydantic models. It knows nothing about Isabelle and aims to be correct with respect to the official specification; until version 1 it is incomplete. - python-isabelle-lsp-client
(
isabelle_lsp_client) — startsisabelle vscode_serverand talks to it over LSP. Callbacks subscribe to Isabelle’s PIDE notifications, and moving the caret elaborates the theory up to that point, so the proof state after any single tactic can be read back — finer-grained than a client that talks to the Isabelle server process. Theauto_sledgeexample replaces everysledgehammercall in a theory with the proof Isabelle finds. It supports Isabelle 2023 and 2024, and needs the isabelle-emacs fork, whose plain-text output mode it depends on. - python-isabelle-parser
(
isabelle_parser) — an Earley grammar for the outer syntax of Isabelle/Isar on top of Lark. It turns a.thyfile — theory header, document markup, specifications, Isar proofs and ML commands — into a parse tree to walk, without an Isabelle installation. - isabelle-check
(
isabelle_check) — incremental checking for agents that edit theories. Instead of re-runningisabelle buildafter every edit, it keeps a session warm in a residentisabelle serverand re-checks only the theories whose files changed and the theories that import them, reporting errors asfile:linewith the failed goal. It ships as a command-line tool and a Claude Code skill, and has no Python dependencies.
Which one to use
Both the LSP client and isabelle-check drive a running Isabelle, but for
different jobs. Use isabelle-check for the edit–check loop of an agent or
script that edits theory files and needs to know what broke. Use the LSP
client to look inside a proof: read proof states, try steps in memory, run
sledgehammer, ask for hover information (tbl1).
| Task | LSP client | isabelle-check |
|---|---|---|
| Check theories after editing files on disk | Possible, but you drive it: open the documents, mirror the edits, collect the diagnostics | Built for this: isabelle-check check re-reads the files and reports errors as file:line |
| Check a whole project | The theories you open, and their imports | All sessions in the project’s ROOT files |
| Read the proof state after a single step | Yes: move the caret, read PIDE/dynamic_output |
No; only the goal shown in an error message |
| Edit in memory, re-check, revert | Yes: edits go to Isabelle as textDocument/didChange |
No; it works on the files on disk |
Replace sledgehammer with the proof found |
Yes: the auto_sledge example |
No |
| Hover, go to definition, completion, symbols | Yes | No |
| What is re-checked after an edit | Edits are sent as ranges, so Isabelle can keep what comes before the edit | The edited theory in full, and every theory importing it |
Report leftover sorry and oops |
No | Yes; a result is clean only without them |
| Interface | Asynchronous Python library, callbacks on PIDE notifications | Command-line tool with short text or JSON reports; Claude Code skill |
| Isabelle | The isabelle-emacs fork (plain-text output); Isabelle 2023 and 2024 | The stock isabelle server; tested with Isabelle 2025 (isabelle-emacs fork) |
python-isabelle-lsp-client on python-lsp-client) and isabelle-check offer, by task. [tbl1]Package dependencies
#direction: down
#spacing: 50
#padding: 10
#.external: dashed fill=#ffffff
[<package> python-isabelle-lsp-client|isabelle_lsp_client]
[<package> python-lsp-client|lsp_client]
[<package> python-isabelle-parser|isabelle_parser]
[<external> Isabelle|isabelle vscode_server (isabelle-emacs fork)]
[<external> lark]
[<package> isabelle-check|isabelle_check]
[<external> Isabelle server|isabelle server]
[python-isabelle-lsp-client] -> [python-lsp-client]
[python-isabelle-lsp-client] --> JSON-RPC over stdio [Isabelle]
[python-isabelle-parser] -> [lark]
[isabelle-check] --> JSON over TCP [Isabelle server]
Solid arrows are Python package dependencies; dashed boxes are not part of this project. The Isabelle client imports the generic LSP client and runs Isabelle as a subprocess, speaking JSON-RPC over its standard input and output (the dashed arrow). The parser stands apart: it needs neither the clients nor Isabelle, which makes it the cheap way to look at a theory — and the only way when there is no prover to ask. The checker stands apart too: it speaks Isabelle’s own server protocol rather than LSP, so it needs neither client.
Incremental checking for agents
An agent working on a proof edits a theory, checks it, reads the errors and
edits again, many times over. Checking with isabelle build re-proves every
theory of the session each time. isabelle-check keeps the checked theories
in a running Isabelle session, so a check costs only the theories an edit
affects (tbl2).
| Situation | isabelle build |
isabelle-check |
|---|---|---|
| First check | 17.9 s | 25–27 s |
| No change | 3.3 s | 0.3 s |
| Edit in the last theory | 18.8 s | 10.4 s |
| Edit in the first theory | 18.8 s | 21.1 s |
Concurrent_Revisions (7 theories in a chain) after an edit, measured on 27 September 2026. [tbl2]The first check is slower than a build, but it happens once per session. After
that, the saving depends on where the edit falls. Reuse works per theory:
Isabelle’s headless session re-checks a changed theory in full, together with
every theory importing it. An edit at the end of an import chain is cheap, and
one at its root costs about a full build. The interactive session also accepts
sorry without complaint, so the checker scans the files for sorry and
oops and reports a result as clean only if there are none, as a strict build
would.
What a check costs an agent in tokens is its output, which lands in the
agent’s context. isabelle-check reports errors as relative file:line with
the failed goal, and a clean result in one line; isabelle build adds absolute
paths, goals broken across lines, timing and a pointer to the build log. Per
check, that makes isabelle-check 2–4.5 times cheaper than isabelle build,
and 8–33 times cheaper than isabelle build -v (tbl3).
| Situation | isabelle build |
isabelle build -v |
isabelle-check |
|---|---|---|---|
| No change | 11 | 325 | 20 |
| Clean edit in the last theory | 91 | 668 | 20 |
| One error | 217 | 737 | 85 |
| Three errors in two theories | 429 | 949 | 183 |
Only a sorry left |
166 | 686 | 79 |
Concurrent_Revisions, counted with the Claude token-counting API (Claude Opus tokenizer), measured on 27 September 2026. Paths in the build output are normalised to a typical project path. [tbl3]In absolute terms these are small numbers: a check costs tens to a few hundred tokens either way, while each turn of an agent re-reads tens of thousands. A hundred edit–check cycles save some 10,000–30,000 tokens. The larger gain is time: checking just the edited theory took 0.3 s against 25 s for the build.
Parser coverage on the AFP
The parser’s coverage of the Archive of Formal Proofs
is still low-ish. Of a seeded random sample of 500 theory files from the
afp-2026-09-11 release, measured on 14 September 2026, only 29% parse
(tbl4).
| Outcome | Files | Share |
|---|---|---|
| Parsed | 145 | 29.0% |
| Rejected | 236 | 47.2% |
| Timed out | 119 | 23.8% |
afp-2026-09-11, with a 15 s timeout per file. [tbl4]The measure is strict: one unsupported statement fails the whole file. The rejections come from inner-syntax terms with exotic notation, some proof methods and a few rarely used commands the grammar does not cover yet. The timeouts come from the Earley chart, which grows super-linearly on large or highly ambiguous files; even a file that parses takes a median of 2.1 s. A successful parse is authoritative, and a failure means “not yet supported” rather than “invalid Isabelle”. The numbers are refreshed weekly in the parser’s README.
Installation
None of the packages is on PyPI yet. Install from GitHub, the LSP client first, since the Isabelle client depends on it:
|
|
The checker needs no other package. On a git checkout of Isabelle, such as the
isabelle-emacs fork, create the distribution id that isabelle server expects
once (the checker’s README shows how).