provers¶
The concrete provers and the registry/factory over them. Each prover subclasses
AutomatedProver and funnels its output through the
shared Verifier. The agentic provers compose an
agent harness (the agent concern) with a
ComputeBackend (the compute concern).
Base¶
The base prover abstraction. An AutomatedProver is a
candidate generator; the base class owns the shared lifecycle (the public prove:
generate, then verify in the sandbox) so subclasses only implement _generate.
- class open_atp.provers.base.AutomatedProver(*, backend: ComputeBackend, timeout_s: int = 1800)[source]¶
Generate candidate proofs, then verify them in a shared sandbox.
The sandbox image (its tag plus the Lean toolchain + Mathlib pins the shared verifier checks every project against) comes from
backend– a prover inherits whatever image its backend runs.- Parameters:
- backend
ComputeBackend The one backend for this prover. Agentic provers reuse it (via a live session) for generation, then verify in that hot sandbox; Aristotle uses it only for the final check.
- timeout_s
int, default 1800 Wall-clock budget for the generation run, in seconds.
- backend
- property max_duration_s: int¶
Maximum wall-clock duration of a healthy
prove()run, in seconds.The total wall-clock time is the sum of: - the proof generation budget - post-generation verification - and the backend’s overhead
- abstractmethod auth_status() AuthStatus[source]¶
Report the status of the credential required by this prover.
- Returns:
AuthStatusWhere the credential lives, whether it is there, and when it expires.
- prove(task: ProofTask, output_dir: Path | str) ProofResult[source]¶
Full lifecycle: reject-on-mismatch, generate, verify, write the result.
The credential is checked up front: an expired one raises, and one with less than
EXPIRY_WARNINGleft is logged as a warning – a run outlives that window – but does not stop the run.- Parameters:
- task
ProofTask The unit of work: the lake project to complete, the optional
targetsto focus on, and anyuser_promptguidance.- output_dir
pathlib.Pathorstr Caller-chosen output directory, populated as
output_dir/{wd,logs}/:wdis the completed lake project (the proof output) andlogsis the run record (the agentstdout.txt/stderr.txt,result.json, and any harness-specific rich logs).
- task
- Returns:
ProofResultThe outcome of the run, pointing at the populated
wdandlogs_dir.
- Raises:
ToolchainMismatchIf the project’s toolchain differs from the backend image’s. Checked up front, before any run starts – so this raises rather than returning an empty result.
MathlibRevMismatchIf the project records a Mathlib revision that differs from the backend image’s. Checked up front, before any run starts.
MissingCredentialsIf a credential the run needs is absent or already expired, or if the agent’s provider rejected the one it was given. Either way no proof was attempted, so this raises rather than returning an empty result.
ProvisionErrorIf the compute sandbox fails to come up (daemon down, image missing, capacity). Raised before generation, so the run never started.
- class open_atp.provers.base.ProofResult(prover: str, verification: ~open_atp.verify.VerificationReport | None, output_dir: ~pathlib.Path, completed_files: dict[str, str] = <factory>, cost_usd: float | None = None, duration_s: float | None = None, metadata: dict[str, object] = <factory>, error: str | None = None, error_msg: str | None = None, status: ~open_atp.provers.base.ProofStatus = ProofStatus.ERROR)[source]¶
What a prover returns from
AutomatedProver.prove().The prover writes its artifacts into the caller-chosen
output_dir, laid out asoutput_dir/{wd,logs}/:wdis the completed lake project (the proof output) andlogsis the run record (the streamed agentstdout.txt,stderr.txt,result.json, and any harness-specific rich logs). This object just records where those live, plus the verification verdict and run metadata.- Parameters:
- prover
str Name of the prover that produced this result.
- verification
VerificationReportorNone The shared verification of the completed project, or
Nonewhen the run failed before a candidate could be verified (seeerror).- output_dir
pathlib.Path The run’s output directory. Holds the
wd(proof project) andlogs_dir(run record) subdirectories the prover populated.- completed_files
dict[str,str], optional The completed
.leansources, keyed by file path relative to the project root. Defaults to an empty mapping.- cost_usd
float, optional Estimated USD cost of the run.
Nonewhen the prover does not report cost.- duration_s
float, optional Wall-clock duration of the run, in seconds.
- metadata
dict[str,object], optional Harness-specific run metadata (token counts, run summaries, …). Defaults to an empty mapping.
- error
str, optional The failing exception’s class name; set when status is ERROR or TIMEOUT.
- error_msg
str, optional The failing exception’s message; set when status is ERROR or TIMEOUT.
- status
ProofStatus, defaultProofStatus.ERROR Status of the proof generation run.
- prover
- property wd: Path¶
The completed working directory,
output_dir/wd.A complete lake project holding the completed
.leanfiles – the proof output.
- property logs_dir: Path¶
The run’s logs directory,
output_dir/logs.Holds the captured agent stream (
stdout.txt),stderr.txt,result.json, and any harness-specific rich record (Vibe’s session log, ax-prover’s per-target logs, Aristotle’s events).
- class open_atp.provers.base.ProofStatus(*values)[source]¶
Coarse status for a
ProofResult.
- exception open_atp.provers.base.ProverError[source]¶
Parent Exception class for prover-side failure the run anticipates.
- exception open_atp.provers.base.GenerationTimeout[source]¶
Bases:
ProverErrorThe proof generation consumed its wall-clock budget before finishing.
Provers¶
The concrete candidate generators.
- class open_atp.provers.agent_prover.AgentProver(*, backend: ComputeBackend, name: str | None = None, harness: Harness | None = None, skills: list[str] | None = None, timeout_s: int = 1800)[source]¶
Bases:
AutomatedProverGenerate proofs by driving an agent CLI harness in a compute backend.
Composes an agent harness (the agent concern) with a
ComputeBackend(the compute concern): the harness edits the staged.leanfiles in place, then the sharedVerifierdoes the final compile/sorry/axiom check. Most entries inSTANDARD_PROVERSare this prover on a different harness.- Parameters:
- backend
ComputeBackend The sandbox the agent runs in. Generation reuses it via a live session and verification runs in that same hot sandbox.
- name
str, optional The prover’s reported name (in log events and
ProofResult.prover). Defaults to the harness’s name; the standard catalog passes the registry key soclaude/leanstralreport their user-facing name rather than the harness name (claude_code/vibe).- harness
Harness, optional The harness to drive, carrying
model/effortplus any harness-specific knobs. Defaults toClaudeCodeHarness.- skills
list[str], default [“lean-proof”] Skills to mount into the agent workdir, each a name (resolved from the vendored
leanprover/skillscatalog) or a full path to aSKILL.mdtree. An empty list mounts none. Staged into every skill-supporting harness’s location; ignored by ax-prover.- timeout_s
int, default 1800 Wall-clock budget for the generation run, in seconds.
- backend
Examples
Construct the prover directly, wiring up a harness and a backend:
>>> from open_atp.backends.docker import DockerBackend >>> from open_atp.harness import CodexHarness >>> from open_atp.provers.agent_prover import AgentProver >>> backend = DockerBackend() >>> prover = AgentProver(harness=CodexHarness(effort="high"), backend=backend) >>> prover.harness.model 'gpt-5.5'
Or build the same prover from the standard catalog by name, taking its baked-in defaults (see
standard_prover()):>>> from open_atp import standard_prover >>> prover = standard_prover("codex", backend=DockerBackend()) >>> prover.name, prover.harness.name ('codex', 'codex')
Complete a task’s
sorrys withprove(), here on a bundled example (this runs the agent in Docker and bills it):>>> import tempfile >>> from open_atp.examples import EXAMPLE, example_task >>> task = example_task(EXAMPLE.MUL_REORDER) >>> result = prover.prove(task, tempfile.mkdtemp()) >>> result.success True
- auth_status() AuthStatus[source]¶
Report the credential the agent CLI runs on.
- Returns:
AuthStatusThe agent harness’s own credential status.
- class open_atp.provers.numina.NuminaProver(*, backend: ComputeBackend, skills: list[str] | None = None, max_rounds: int = 20, max_consecutive_limits: int = 2, oauth_token: str | None = None, helper_env_keys: tuple[str, ...] = ('GEMINI_API_KEY', 'OPENAI_API_KEY', 'LEAN_LEANDEX_API_KEY', 'ANTHROPIC_API_KEY'), guard_statements: bool = True, on_statement_change: Literal['error', 'warn'] = 'error', timeout_s: int = 1800, env: dict[str, str] | None = None)[source]¶
Bases:
AgentProverRun the Numina coordinator/subagent scaffold as an
AgentProver.Numina’s vendored scaffold – coordinator prompt, skills, and subagent prompts – is staged into the sandbox’s
.claude/tree; generation and the sharedVerifierwork exactly as in the base agent prover.The harness is not configurable: Numina is claude-CLI driven and ships its own scaffold in place of plugins.
- Parameters:
- backend
ComputeBackend The sandbox the agent runs in. Generation reuses it via a live session and verification runs in that same hot sandbox.
- skills
list[str], optional Extra named/path skills to mount alongside Numina’s vendored scaffold. Defaults to none – Numina’s coordinator skill is staged from
vendor/numina/skills, not this list.- max_rounds
int, default 20 Maximum number of coordinator rounds before the run stops.
- max_consecutive_limits
int, default 2 Reset (start a fresh session) after this many consecutive LIMIT rounds.
- oauth_token
str, optional The
CLAUDE_CODE_OAUTH_TOKENto forward into the sandbox;None(default) reads it from the host env var.- helper_env_keys
tuple[str, …], optional Helper-skill credentials forwarded into the sandbox when present in the host env; skills degrade/skip when their key is absent. Defaults to the Leandex, Gemini, OpenAI, and Anthropic key names.
- guard_statementsbool, default
True Whether to snapshot the target theorems and reject runs that weaken or delete them.
- on_statement_change{“error”, “warn”}, default “error”
Behavior on a weakened/deleted target theorem:
errorstops the run and restores the originals;warnrestores and continues. The default rejects, which is the safe choice.- timeout_s
int, default 1800 Wall-clock budget for the generation run, in seconds.
- env
dict[str,str], optional Extra literal environment variables forwarded into the agent sandbox (Numina pins its harness, so its env knobs live here). Defaults to no extra variables.
- backend
Examples
Construct the prover directly:
>>> from open_atp.backends.docker import DockerBackend >>> from open_atp.provers.numina import NuminaProver >>> backend = DockerBackend() >>> prover = NuminaProver(backend=backend) >>> prover.max_rounds 20
Or build the same prover from the standard catalog by name, taking its baked-in defaults (see
standard_prover()):>>> from open_atp import standard_prover >>> prover = standard_prover("numina", backend=DockerBackend()) >>> prover.name 'numina'
Complete a task’s
sorrys withprove(), here on a bundled example (this runs the Numina scaffold in Docker and bills it):>>> import tempfile >>> from open_atp.examples import EXAMPLE, example_task >>> task = example_task(EXAMPLE.INTER_UNION_DISTRIB) >>> result = prover.prove(task, tempfile.mkdtemp()) >>> result.success True
- class open_atp.provers.aristotle.AristotleProver(*, backend: ComputeBackend, api_key: str | None = None, allow_agent_questions: bool = False, max_connection_retries: int = 5, retry_backoff_seconds: float = 5.0, poll_interval_s: float = 15.0, timeout_s: int = 1800)[source]¶
Bases:
AutomatedProverProve by handing the whole project to Harmonic’s hosted Aristotle agent.
Generation happens over the network (submit the lake project, wait, download the result archive, unpack it over the workdir); the shared
Verifierthen runs the same local compile/sorry/axiom check. Generation is network-only, so the backend is used solely for that final check – unlike the agentic provers, there is no live session to reuse.- Parameters:
- backend
ComputeBackend The sandbox used only for the final verify; Aristotle generates over the network, so there is no live session to reuse.
- api_key
str, optional The Harmonic API key.
None(default) reads it from the hostARISTOTLE_API_KEYenv var.- allow_agent_questionsbool, default
False Whether to let the hosted agent ask clarifying questions. This is a headless API path, so a prompt for stdin would hang the run.
- max_connection_retries
int, default 5 Bounds retries of each API call when a connection drops. The hosted run lives server-side, so a dropped connection is recoverable: re-fetch rather than reporting the run failed.
- retry_backoff_seconds
float, default 5.0 Initial sleep between retries of a failed call, doubling (capped) between tries.
- poll_interval_s
float, default 15.0 Seconds between polls of the task’s status while waiting for generation.
- timeout_s
int, default 1800 Hard wall-clock cap on the generation wait, in seconds. When it elapses we stop waiting and verify whatever Aristotle has produced so far; if that does not verify the run’s status is
TIMEOUT. The run keeps going and billing server-side regardless – this only bounds the client.
- backend
Examples
Construct the prover directly (network-only generation, so the backend is just the verify backend):
>>> from open_atp.backends.docker import DockerBackend >>> from open_atp.provers.aristotle import AristotleProver >>> backend = DockerBackend() >>> prover = AristotleProver(backend=backend) >>> prover.name 'aristotle'
Or build the same prover from the standard catalog by name, taking its baked-in defaults (see
standard_prover()):>>> from open_atp import standard_prover >>> prover = standard_prover("aristotle", backend=DockerBackend()) >>> prover.name 'aristotle'
Complete a task’s
sorrys withprove(), here on a bundled example (this hits the hosted Aristotle API, needingARISTOTLE_API_KEY, and runs Docker for the verify):>>> import tempfile >>> from open_atp.examples import EXAMPLE, example_task >>> task = example_task(EXAMPLE.ABS_MUL_LT) >>> result = prover.prove(task, tempfile.mkdtemp()) >>> result.success True
- auth_status() AuthStatus[source]¶
Report the
ARISTOTLE_API_KEYthe hosted API is called with.- Returns:
AuthStatusThe API key status, read from api_key constructor or host environment.
Standard catalog¶
The standard catalog names each ready-to-run default prover and builds it against a
compute backend. standard_prover() maps a catalog name to a
constructed AutomatedProver, wiring in the shared
image/toolchain from the backend; a caller then drives it directly via
prove(), which returns a
ProofResult with verification and cost. Agentic provers
run generation in a live session over that backend and verify in the same hot sandbox.
This is the top-level surface re-exported from open_atp itself.
standard_provers() lists the accepted names.
Each builds its class’s baked-in defaults — to customize any knob, construct the
prover class directly.
- open_atp.config.standard_prover(name: str, *, backend: ComputeBackend) AutomatedProver[source]¶
Construct a standard default prover
nameagainstbackend.nameis aSTANDARD_PROVERSkey, as listed bystandard_provers(). The prover is built with its class’s baked-in defaults; to customize any knob (model, effort, skills, …), construct the prover class directly instead.The sandbox image (and the toolchain + Mathlib pins projects are checked against) comes from
backend, not a parameter here.Examples
>>> from open_atp.backends.docker import DockerBackend >>> prover = standard_prover("claude", backend=DockerBackend()) >>> prover.harness.model 'claude-opus-4-8'
- open_atp.config.standard_provers() list[str][source]¶
The names
standard_prover()accepts: theSTANDARD_PROVERSkeys.
- open_atp.config.STANDARD_PROVERS: dict[str, dict[str, object]] = {'aristotle': {'type': 'aristotle'}, 'axproverbase': {'harness': {'type': 'axproverbase'}, 'type': 'agent'}, 'claude': {'harness': {'type': 'claude_code'}, 'type': 'agent'}, 'codex': {'harness': {'type': 'codex'}, 'type': 'agent'}, 'deepseek': {'harness': {'auth': 'api_key', 'model': 'deepseek-v4-pro', 'provider': 'deepseek', 'type': 'opencode'}, 'type': 'agent'}, 'grok': {'harness': {'auth': 'login', 'model': 'grok-4.5', 'provider': 'xai', 'type': 'opencode'}, 'type': 'agent'}, 'kimi': {'harness': {'type': 'kimi'}, 'type': 'agent'}, 'leanstral': {'harness': {'type': 'vibe'}, 'type': 'agent'}, 'numina': {'type': 'numina'}, 'spark': {'harness': {'auth': 'api_key', 'model': 'muse-spark-1.1', 'provider': 'meta', 'type': 'opencode'}, 'type': 'agent'}}¶
The standard catalog: a friendly name -> the canonical
proverspec for that ready-to-run default, and the source of truth for which names the CLI accepts. Most entries are the sharedAgentProveron a different harness – several share theopencodeharness, differing only in provider and auth strategy – and the rest are standalone provers. Build one withstandard_prover().