{"version": "1023f1f148f87f4a893e7697dbb8a2643dc8bd5e", "pages": [{"slug": "index", "title": "Home", "html_url": "https://axle.axiommath.ai/v1/docs/", "markdown": "# AXLE - Axiom Lean Engine\n\n## Proof manipulation as infrastructure, not an afterthought\n\nAXLE is a set of Lean utilities for theorem proving: validating candidate proofs, splitting theorems from larger files, converting proofs to `sorry`, and more. They are written as Lean metaprograms to be robust.\n\n## Features\n\n- **Proof Verification**: Validate proofs against formal statements\n- **Code Analysis**: Check Lean code for errors and extract theorems\n- **Code Transformation**: Rename declarations, convert keywords, simplify proofs\n\n## Usage Methods\n\nAXLE can be accessed through:\n\n1. **Web Interface** - Interactive UI at [https://axle.axiommath.ai/](https://axle.axiommath.ai/)\n2. **Python API** - `pip install axiom-axle` ([Python API Reference](python-api.md))\n3. **CLI** - `axle verify-proof`, `axle check`, etc. ([CLI Reference](cli-reference.md))\n4. **HTTP API** - Direct REST calls with `curl`\n\nSee the [Quick Start](quickstart.md) tutorial for examples of each method.\n\n## Available Tools\n\n| Endpoint | Description |\n|----------|-------------|\n| `verify_proof` | Validate a proof against a formal statement |\n| `check` | Check Lean code for errors |\n| `extract_theorems` | Split file into individual theorems with dependencies |\n| `extract_decls` | Split file into individual declarations with dependencies |\n| `extract_proof_states` | Extract the proof state at each line of a proof |\n| `rename` | Rename declarations |\n| `theorem2lemma` | Convert between `theorem` and `lemma` keywords |\n| `theorem2sorry` | Strip proofs from theorems |\n| `merge` | Combine multiple Lean files |\n| `simplify_theorems` | Simplify theorem proofs |\n| `repair_proofs` | Attempt to repair broken proofs |\n| `have2lemma` | Extract `have` statements to standalone lemmas |\n| `have2sorry` | Replace `have` statements with `sorry` |\n| `sorry2lemma` | Extract `sorry` and errors to standalone lemmas |\n| `disprove` | Attempt to disprove theorems |\n| `normalize` | Standardize Lean formatting |\n\nSee tools documentation for detailed parameters and response fields.\n\n## Links\n\n- [Installation Guide](installation.md)\n- [Quick Start Tutorial](quickstart.md)\n- [Python API Reference](python-api.md)\n- [CLI Reference](cli-reference.md)\n- [Configuration](configuration.md)\n- [Troubleshooting](troubleshooting.md)\n\n## Citing AXLE\n\nTo cite AXLE, please cite the [technical report](https://arxiv.org/abs/2606.26442):\n\n```bibtex\n@misc{xin2026axle,\n  title={AXLE: A Cloud Infrastructure for Lean 4 Theorem Proving Utilities},\n  author={Xin, Jimmy and Schneidman, Alex and Cummins, Chris and Ram, Karun and Ganesh, Srihari and Limperg, Jannis},\n  year={2026},\n  eprint={2606.26442},\n  archivePrefix={arXiv},\n  primaryClass={cs.LO},\n  url={https://arxiv.org/abs/2606.26442},\n}\n```\n\n## Public Deployments\n\nAXLE's public deployment follows a weekly release schedule:\n\n- **Maintenance Window**: Every Wednesday at 10:00 AM Pacific Time\n- **Expected Downtime**: Brief interruption (typically under 5 minutes) during restart\n- **Updates**: New features, bug fixes, and improvements deployed weekly\n\nAfter each deployment, the [changelog](https://github.com/AxiomMath/axiom-lean-engine/blob/main/CHANGELOG.md) is updated with details on what changed.\n\n## Submitting Issues\n\nIf you encounter bugs, unexpected behavior, or have feature requests:\n\n<a href=\"https://github.com/AxiomMath/axiom-lean-engine/issues/new?title=Bug%20report%3A%20AXLE&body=%23%23%23%20Bug%20Description%0A%3C%21--%20Please%20describe%20the%20issue%20you%20encountered%20--%3E%0A%0A%0A%0A%23%23%23%20Expected%20Behavior%0A%3C%21--%20What%20did%20you%20expect%20to%20happen%3F%20--%3E%0A%0A%0A%0A%23%23%23%20Actual%20Behavior%0A%3C%21--%20What%20actually%20happened%3F%20--%3E%0A%0A%0A%0A%23%23%23%20Reproduction%20Details%0A-%20%2A%2ATool%3A%2A%2A%20%0A-%20%2A%2AEnvironment%3A%2A%2A%20%0A-%20%2A%2ALink%3A%2A%2A%20%0A%0A%23%23%23%20Additional%20Context%0A%3C%21--%20Add%20any%20other%20context%20about%20the%20problem%20here%20--%3E%0A\" target=\"_blank\" class=\"file-bug-btn\">\n  <svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\">\n    <path d=\"M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z\"/>\n  </svg>\n  FILE A BUG\n</a>\n\n## Resources\n\n- [Axiom Homepage](https://axiommath.ai/)\n- [Lean Homepage](https://lean-lang.org/)\n- [Mathlib](https://leanprover-community.github.io) - Mathematics library for Lean 4\n- [Lean Zulip](https://leanprover.zulipchat.com/) - Community discussion\n- [AXLE on Zulip](https://leanprover.zulipchat.com/#narrow/channel/219941-Machine-Learning-for-Theorem-Proving/topic/Axiom.20Lean.20Engine/with/577859288) - Discussion thread for AXLE\n- [axiom-axle-mcp](https://pypi.org/project/axiom-axle-mcp/) - MCP server for AXLE\n- [Pantograph](https://github.com/lenianiva/Pantograph) - Machine-to-machine interaction interface for Lean\n\n## Machine-Readable Docs\n\nThis documentation is also served in machine-readable form:\n\n| Endpoint | Description |\n|----------|-------------|\n| [`/v1/docs/pages.json`](https://axle.axiommath.ai/v1/docs/pages.json) | Page manifest in navigation order: slug, title, and HTML URL for every page |\n| [`/v1/docs/all.json`](https://axle.axiommath.ai/v1/docs/all.json) | The same manifest with each page's source markdown inlined |\n| `/v1/docs/raw/{slug}.md` | One page's source markdown, e.g. [`/v1/docs/raw/quickstart.md`](https://axle.axiommath.ai/v1/docs/raw/quickstart.md) |\n\n```bash\ncurl -s https://axle.axiommath.ai/v1/docs/pages.json | jq\n```\n"}, {"slug": "installation", "title": "Installation", "html_url": "https://axle.axiommath.ai/v1/docs/installation/", "markdown": "# Installation\n\n## Requirements\n\n- Python 3.11 or higher\n- An internet connection (to reach the AXLE API)\n\n## Install from PyPI\n\n```bash\npip install axiom-axle\n```\n\n<!-- Disable homebrew for now (not set up)\n## Install CLI via Homebrew (macOS/Linux)\n\n```bash\nbrew install axle\n```\n-->\n\n## Install from Source\n\n```bash\ngit clone https://github.com/AxiomMath/axiom-lean-engine.git\ncd axiom-lean-engine\npip install -e .\n```\nor\n```bash\npip install git+ssh://git@github.com/AxiomMath/axiom-lean-engine\n```\n\n## Development Installation\n\nFor development, install with dev dependencies:\n\n```bash\ngit clone https://github.com/AxiomMath/axiom-lean-engine.git\ncd axiom-lean-engine\nmake setup-env\n```\n\nThis will:\n\n- Install all dependencies (including dev tools)\n- Set up pre-commit hooks\n- Install the package in editable mode\n\n## Verify Installation\n\n```bash\n# Check CLI\naxle --version\n\n# Check Python package\npython -c \"from axle import AxleClient; print('OK')\"\n```\n\n## Next Steps\n\nSee the [Quick Start](quickstart.md) tutorial to get started.\n"}, {"slug": "quickstart", "title": "Quick Start", "html_url": "https://axle.axiommath.ai/v1/docs/quickstart/", "markdown": "# Quick Start\n\nTry AXLE in your browser with the [interactive demo notebook](https://colab.research.google.com/github/AxiomMath/axiom-lean-engine/blob/main/examples/starting_demo.ipynb).\n\n## MCP Server\n\nThe [`axiom-axle-mcp`](https://pypi.org/project/axiom-axle-mcp/) package wraps AXLE as a [Model Context Protocol](https://modelcontextprotocol.io/) server, so an AI agent can call `check`, `verify_proof`, `extract_theorems`, and the rest of AXLE's tools directly.\n\nSupported clients include:\n\n- **Claude Code, Cursor, Windsurf, VS Code, Cline**, and other editors that speak MCP \u2014 point them at the `axiom-axle-mcp` package locally.\n- **Claude web, desktop, and mobile** \u2014 connect to the hosted server at `https://mcp.axiommath.ai/mcp` under Settings > Connectors.\n\nSee the [PyPI page](https://pypi.org/project/axiom-axle-mcp/) for detailed installation and config instructions.\n\n## Prerequisites\n\nBefore using AXLE, set your API key:\n\n```bash\nexport AXLE_API_KEY=your-api-key\n```\n\nSee [Configuration](configuration.md#authentication) for more details.\n\n## Basic Usage\n\n### Check Lean Code\n\nThe simplest operation is checking if Lean code is valid:\n\n#### Python\n\n```python\nimport asyncio\nfrom axle import AxleClient\n\nasync def main():\n    async with AxleClient() as client:\n        result = await client.check(\n            content=\"import Mathlib\\ntheorem citation_needed : 1 + 1 = 2 := by decide\",\n            environment=\"lean-4.28.0\",\n        )\n        # okay means it compiled; failed_declarations catches sorry, disallowed axioms, etc. that leave okay true.\n        print(f\"Compiles: {result.okay}\")\n        print(f\"Valid proof: {result.okay and not result.failed_declarations}\")\n        print(\"Errors:\", result.lean_messages.errors)\n\nasyncio.run(main())\n```\n\n#### CLI\n\n```bash\n# From file\naxle check mytheorem.lean --environment lean-4.28.0\n\n# From stdin\necho \"def meaning_of_life := 42\\n#print meaning_of_life\" | axle check - --environment lean-4.28.0\n```\n\n#### HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/check \\\n    -H \"Authorization: Bearer $AXLE_API_KEY\" \\\n    -H \"Content-Type: application/json\" \\\n    -d '{\"content\": \"import Mathlib\\ntheorem citation_needed : 1 + 1 = 2 := by decide\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Next Steps\n\n- [Python API Reference](python-api.md) - Full API documentation\n- [CLI Reference](cli-reference.md) - All CLI commands\n- [Configuration](configuration.md) - Environment variables and options\n"}, {"slug": "configuration", "title": "Configuration", "html_url": "https://axle.axiommath.ai/v1/docs/configuration/", "markdown": "# Configuration\n\nAXLE can be configured via environment variables or constructor arguments.\n\n## Authentication\n\nAXLE uses API key authentication for active request rate limiting. Requests without an API key are limited to 10 concurrent active requests.\n\nTo obtain an API key, visit [axle.axiommath.ai/app/console](https://axle.axiommath.ai/app/console). If you need higher rate limits, you can [request more capacity](https://forms.gle/CdLKu45tEsRXtFQ29).\n\n### Setting Your API Key\n\nThe recommended way to configure your API key is via the `AXLE_API_KEY` environment variable:\n\n```bash\nexport AXLE_API_KEY=your-api-key\n```\n\nYou can also pass it directly when creating the client (see [Python Configuration](#python-configuration) below).\n\n\n## Environment Variables\n\n| Variable | Default | Description |\n|----------|---------|-------------|\n| `AXLE_API_KEY` | \u2014 | API key for authentication |\n| `AXLE_API_URL` | `https://axle.axiommath.ai` | API server URL |\n| `AXLE_TIMEOUT_SECONDS` | `1_800` | Base timeout in seconds for retry window if service is temporarily unavailable |\n| `AXLE_MAX_CONCURRENCY` | `20` | Max concurrent requests |\n\n### Example\n\n```bash\nexport AXLE_API_KEY=your-api-key\nexport AXLE_API_URL=https://axle.axiommath.ai\nexport AXLE_TIMEOUT_SECONDS=600\nexport AXLE_MAX_CONCURRENCY=50\n```\n\n## Python Configuration\n\n### Constructor Arguments\n\n```python\nfrom axle import AxleClient\n\n# API key from environment variable (recommended)\nclient = AxleClient()\n\n# Explicit API key\nclient = AxleClient(api_key=\"your-api-key\")\n\n# Custom URL, timeout, and concurrency\nclient = AxleClient(\n    api_key=\"your-api-key\",\n    url=AxleClient.DEFAULT_URL,\n    base_timeout_seconds=600,\n    max_concurrency=50,\n)\n```\n\nAll constructor arguments fall back to environment variables if not provided.\n\n## CLI Configuration\n\nThe CLI reads the `AXLE_API_KEY` environment variable automatically. Set it before running CLI commands:\n\n```bash\nexport AXLE_API_KEY=your-api-key\n```\n\n### Global Options\n\n```bash\n# Custom API URL\naxle --url https://axle.axiommath.ai check file.lean --environment lean-4.28.0\n\n# JSON output\naxle --json check file.lean --environment lean-4.28.0\n\n# Output to file\naxle theorem2sorry input.lean --environment lean-4.28.0 -o output.lean\n```\n\n\n## Lean Environments\n\nAXLE supports multiple Lean environments, each containing a specific Lean version and set of dependencies (e.g., Mathlib). Every API request requires an `environment` parameter specifying which environment to use. To get started, we recommend using the latest Lean + Mathlib version, which at the time of writing is packaged in `lean-4.28.0`.\n\n### Discovering Available Environments\n\nYou can query the available environments using any access method:\n\n#### Python\n\n```python\nimport asyncio\nfrom axle import AxleClient\n\nasync def main():\n    async with AxleClient() as client:\n        environments = await client.environments()\n        for env in environments:\n            print(f\"{env.name}: {env.description}\")\n\nasyncio.run(main())\n```\n\n#### CLI\n\n```bash\naxle environments\n```\n\n#### HTTP API\n\n```bash\ncurl -s -H \"Authorization: Bearer $AXLE_API_KEY\" https://axle.axiommath.ai/v1/environments | jq\n```\n\nThe `Authorization` header is required for all requests.\n\n### Environment Response Format\n\nEach environment includes the following fields:\n\n| Field | Type | Description |\n|-------|------|-------------|\n| `name` | `str` | Environment identifier to use in requests (e.g., `\"lean-4.28.0\"`) |\n| `lean_toolchain` | `str` | Lean toolchain version (e.g., `\"leanprover/lean4:v4.26.0\"`) |\n| `repo_url` | `str | null` | Git repository URL for custom environments |\n| `revision` | `str | null` | Git revision/commit hash for custom environments |\n| `subdir` | `str | null` | Subdirectory within the repository |\n| `imports` | `str` | Default imports available (e.g., `\"import Mathlib\"`) |\n| `description` | `str` | Human-readable description |\n\n### Example Environments\n\n```json\n[\n  {\n    \"name\": \"lean-4.21.0\",\n    \"lean_toolchain\": \"leanprover/lean4:v4.21.0\",\n    \"imports\": \"import Mathlib\",\n    \"description\": \"Lean 4.21.0 with Mathlib\"\n  }]\n```\n\nSee [Import Mismatches](troubleshooting.md#import-mismatches) for important notes on how AXLE handles import statements.\n"}, {"slug": "troubleshooting", "title": "Troubleshooting", "html_url": "https://axle.axiommath.ai/v1/docs/troubleshooting/", "markdown": "# Troubleshooting\n\nCommon issues when working with AXLE and how to resolve them.\n\n## Reading Error Messages\n\n### Fatal Errors\n\nWhen a request fails entirely, the response includes an error type at the top level:\n\n| Error Type | Meaning | Action |\n|------------|---------|--------|\n| `user_error` | Invalid request (missing parameters, bad arguments, import mismatch) | Fix the request\u2014check your inputs |\n| `internal_error` | Server bug | [Report it](https://github.com/AxiomMath/axiom-lean-engine/issues) |\n| `error` | Runtime failure that exhausted internal retries (executor crash) | Rarely worth retrying\u2014AXLE already retried internally |\n| `error` + `error_type: LeanResourceExceeded` | Lean worker hit a resource cap (e.g. memory) for this input | Reduce the problem's memory allocation |\n| `error` + `error_type: LeanTimeout` | Lean worker exceeded its time budget for this input | Simplify the input or raise the timeout |\n\nIn the Python client, these map to exceptions: `AxleInvalidArgument`, `AxleInternalError`, `AxleRuntimeError`, `LeanResourceExceeded`, and `LeanTimeout`. See [Error Handling](python-api.md#error-handling) for details on catching and handling these exceptions, and for an exhaustive list of all AXLE exceptions, including networking errors.\n\n### Resource Limits\n\nEach request runs in a worker with a default time budget of **120 seconds** and a cap of **16 GB** on allocatable working memory. Exceeding the time budget yields a `LeanTimeout`; exceeding the memory cap yields a `LeanResourceExceeded`. The timeout is configurable per request via the `timeout_seconds` parameter, up to a maximum of 900 seconds (15 minutes).\n\n### Non-Fatal Errors\nWhen troubleshooting, start by examining the messages in the response. AXLE responses include two message fields, each containing `errors`, `warnings`, and `infos` arrays:\n\n| Field | Contents |\n|-------|----------|\n| `lean_messages` | Output from the Lean compiler itself; an empty `errors` list means the code compiles |\n| `tool_messages` | AXLE-specific logs and validation results |\n\n**Message severity:**\n\n- **errors** \u2014 Something is wrong; the result may be unusable\n- **warnings** \u2014 Something is suspicious but not fatal\n- **infos** \u2014 Informational output (timing, debug info, etc.)\n\nFor most tools, `tool_messages.errors` is empty; fatal issues are raised at the response level (see above). The exceptions are `verify_proof`, which uses `tool_messages.errors` to report strict proof verification failures, and `repair_proofs`, which uses it to report failed repairs.\n\n## Common Issues\n\n### Tool Not Working As Expected\n\n**Symptom:** A tool returns unexpected results, fails to transform code correctly, or produces output with errors.\n\n**Cause:** AXLE was built to handle certain categories of errors\u2014particularly those restricted to the proof body (e.g., failed tactics, `sorry`). Errors outside the proof body (malformed declarations, syntax errors, unresolved imports) may cause tools to behave unexpectedly.\n\n**What AXLE handles well:**\n\n- Code that compiles cleanly\n- Proofs with localized errors (e.g., a tactic that doesn't close the goal)\n\n**What may cause issues:**\n\n- Syntax errors or malformed declarations\n- Unresolved identifiers outside proof bodies\n- Unsupported constructs (see [Unsupported Constructs](#unsupported-lean-constructs))\n\n**Rule of thumb:** If the input compiles, the output should compile. For best results, use AXLE with code that already compiles.\n\n```python\nresult = await axle.rename(content=code, declarations={\"old\": \"new\"}, environment=\"lean-4.28.0\")\n\nif result.lean_messages.errors:\n    print(\"Output has compilation errors:\")\n    for msg in result.lean_messages.errors:\n        print(f\"  {msg}\")\nelse:\n    print(result.content)\n```\n\n\n### Import Mismatches\n\n**Symptom:** Your code's imports don't match the environment's default header, and you get an info/warning message or unexpectedly slow (or incorrect) results.\n\n**Cause:** Every environment has a default header derived from its `imports` field. AXLE keeps a pre-built environment for that header so requests run fast. When your code's imports differ from it, AXLE's behavior depends on the `ignore_imports` flag.\n\n**Behavior:**\n\n- **`ignore_imports=True` (default):** AXLE ignores the imports in your `content` and substitutes the environment's default header. This reuses the cached environment, so it is fast. The substituted code is returned in the `content` field, and an info message notes the override.\n\n- **`ignore_imports=False`:** AXLE processes your imports exactly as written. This is *significantly slower* because the cached environment cannot be reused, and it may produce **inconsistent or incorrect results** if a required dependency (such as `Mathlib.Tactic`) is missing. AXLE returns a warning in these cases.\n\n**Recommendation:** Leave `ignore_imports` at its default (`True`) unless you specifically need custom imports. To discover the expected imports for an environment, query the [environments endpoint](configuration.md#discovering-available-environments).\n\n### Unsupported Lean Constructs\n\n**Symptom:** Unexpected behavior or errors with certain Lean code patterns.\n\n**Cause:** AXLE was designed with simple imports, theorems, and definitions in mind.\n\n**Potentially unsupported constructs:**\n\n- Non-standard declaration types\n- `open` commands\n- `section`/`namespace` blocks\n- Complex macro usage\n\n**Resolution:** Use the [`normalize`](tools/normalize.md) tool to detect unsupported constructs early. We attempt to support these patterns and fail fast when we can't, but we make no guarantees about stability.\n\n### Interpreting the `okay` Field\n\nFor every tool, `okay` is `true` exactly when `lean_messages.errors` is empty (the code compiles) and `tool_messages.errors` is empty (no tool-specific errors). What differs is what each tool reports as a tool error:\n\n- **`check`** \u2014 compilation only. It produces no tool errors: validation findings (a declaration that uses `sorry`, a disallowed axiom, or an unsafe definition) are reported as `tool_messages` warnings, with the offending names in `failed_declarations`, and do **not** move `okay`. To treat `check` as a pass/fail for a real proof, require `result.okay and not result.failed_declarations`, or better yet, use `verify_proof`.\n- **`verify_proof`** \u2014 compilation + strict proof verification. Tool errors are verification failures: the findings above, plus a signature mismatch or a missing declaration. Any of them sets `okay: false`.\n- **`repair_proofs`** \u2014 compilation + no failed repairs. A failed repair \u2014 a repair was detected as necessary, but no successful change could fix it (e.g. a `sorry` that no terminal tactic could prove, or a `native_decide` that can't be safely replaced) \u2014 is a tool error and sets `okay: false`.\n\nIf you just want a single \"is this a complete, valid proof\" answer, use `verify_proof`.\n\n### \"All Executors Failed After N Attempts\"\n\n**Symptom:** Request fails with an error like `all executors failed after N attempts`.\n\n**Cause:** This indicates a runtime error or crash on the server side. The most likely cause is an out-of-memory (OOM) condition, where the server kills runaway Lean processes that exceed memory limits.\n\n**Resolution:** Check your input for patterns that might cause excessive memory usage:\n\n- Very large files or deeply nested expressions\n- Proofs that trigger expensive elaboration\n- Tactics that generate large proof terms\n\nTry simplifying your input or breaking it into smaller pieces.\n\n### Limited Concurrency\n\n**Symptom:** Requests are being throttled or you're hitting concurrency limits.\n\n**Resolution:**\n\n1. **Get and set an API key.** Authenticated requests have higher rate limits. See [Configuration](configuration.md) for details.\n\n2. **Increase client-side concurrency.** Set the `AXLE_MAX_CONCURRENCY` environment variable to allow more concurrent requests from your client. See [Configuration](configuration.md) for details.\n\n3. **Request more capacity.** If you need higher rate limits, you can [request more capacity](https://forms.gle/CdLKu45tEsRXtFQ29).\n\n### Slow Requests / Timing Mismatches\n\n**Symptom:** Requests take longer than expected, or reported timings don't match end-to-end latency.\n\n**Cause:** Several server-side factors can affect request duration:\n\n- **Warmup time** \u2014 Cold environments need initialization\n- **Queue delays** \u2014 Requests may wait for available executors or hit rate limits\n- **Server load** \u2014 Shared infrastructure can experience slowdowns\n\n**Note:** The request timeout does not necessarily correspond to end-to-end delay. Server-reported timings reflect processing time, not total round-trip time including queue wait.\n\nIf Lean execution time itself is too slow, you may need to consult the next section.\n\n### Slow Lean Execution\n\nThis section addresses issues with Lean execution time itself being too slow. You can view more detailed timing information in the returned `info` and `timings` field returned by each tool.\n\n1. **Use the default header.** The default header (typically `import Mathlib`) is cached, so files using that header are significantly faster than reloading imports from scratch.\n\n2. **Turn off the `reparse` option.** Some transformations come with a `reparse` toggle (default: `true`) that re-elaborates the file to return fresh Lean messages. If you don't need those messages, there is no point in reparsing, so you can cut the parse time in half by skipping it.\n\n3. **Use the `names` and `indices` fields.** If you only need to check / transform some declarations, you can specify them in the `names` or `indices` field. This skips proof elaboration for any unselected declarations, which can greatly increase execution speed.\n\n4. **Use `permitted_sorries`.** Proof elaboration is skipped for `permitted_sorries` (default: empty), so skipping checks for trusted parts of the file can lead to significant improvements. Use this option with caution -- errors or disallowed axioms inside those proofs now go unnoticed as well, not just explicit sorries. Any final verification should always be done with this field empty. See the [`verify_proof` page](tools/verify_proof.md) for details.\n\n5. **Turn off the `elab_proofs` option.** `extract_decls` comes with the `elab_proofs` option (default: `true`). If you only need to inspect theorem statements and not the proofs, you can turn this toggle off to skip processing proofs. Note that this will break any proof-dependent fields, such as `content`, any `value` dependencies, `proof_length`, etc. Statement-level fields (`type`, `type_hash`, `signature`, type dependencies, positions) are still computed, and non-theorem declarations are unaffected. Use this to cheaply list a file's declarations (e.g. to feed the `names`/`indices` parameters of other tools). See the [`extract_decls` page](tools/extract_decls.md) for details.\n\n\n### Tool-Specific Issues\n\nFor troubleshooting specific to individual tools, see the documentation for that tool in the [Tools](tools/verify_proof.md) section.\n\n### HTTP 302 to a browser sign-in (`AxleBrowserLoginRequiredError`)\nYou may be attempting to access a forbidden internal tier.\n\n### `verify_proof` rejects a proof with a user-provided answer\n\nSome questions take the following format:\n\n> Find `x` such that `x * x = 4`.\n\n> Prove or disprove that the sum of `1/p` over prime `p` converges.\n\nWe call such examples \"find the answer\" problems. `verify_proof` has special handling for such cases; see the [`verify_proof` page](tools/verify_proof.md#find-the-answer-problems) for details.\n"}, {"slug": "python-api", "title": "Python API", "html_url": "https://axle.axiommath.ai/v1/docs/python-api/", "markdown": "# Python API Reference\n\n## Axle Client\n\nThe main client for interacting with the AXLE API.\n\n### Basic Usage\n\n```python\nimport asyncio\nfrom axle import AxleClient\n\nasync def main():\n    async with AxleClient() as client:\n        result = await client.check(content=\"import Mathlib\\ndef x := 1\", environment=\"lean-4.28.0\")\n        # okay means it compiled; failed_declarations catches sorry, disallowed axioms, etc. that leave okay true.\n        print(f\"Compiles: {result.okay}\")\n        print(f\"Valid proof: {result.okay and not result.failed_declarations}\")\n\nasyncio.run(main())\n```\n\n### Constructor\n\n```python\nAxleClient(\n    api_key: str | None = None,          # default: AXLE_API_KEY env var\n    url: str | None = None,              # default: AXLE_API_URL env var\n    max_concurrency: int | None = None,  # default: AXLE_MAX_CONCURRENCY env var\n    base_timeout_seconds: float | None = None,  # default: AXLE_TIMEOUT_SECONDS env var\n)\n```\n\nAll arguments fall back to their corresponding environment variables if not provided. See [Configuration](configuration.md#environment-variables) for details.\n\n## Error Handling\n\nAll errors raise exceptions. Use try/except to handle them:\n\n```python\nfrom axle.exceptions import (\n    AxleIsUnavailable,\n    AxleRuntimeError,\n    AxleInternalError,\n    AxleInvalidArgument,\n    AxleRateLimitedError,\n    AxleForbiddenError,\n    AxleNotFoundError,\n    AxleConflictError,\n    AxleBrowserLoginRequiredError,\n)\n\ntry:\n    result = await axle.check(content=code, environment=\"lean-4.28.0\")\nexcept AxleIsUnavailable as e:\n    print(f\"API unavailable at {e.url}: {e.details}\")\nexcept AxleInvalidArgument as e:\n    print(f\"Invalid request: {e}\")\nexcept AxleInternalError as e:\n    print(f\"Server error: {e}\")\nexcept AxleRuntimeError as e:\n    print(f\"Operation failed: {e}\")\n```\n\n| Exception | HTTP Status | Cause | Action |\n|-----------|-------------|-------|--------|\n| `AxleIsUnavailable` | 503 | Cannot reach API server (connection refused, DNS failure, service unavailable, service upgrade) | Automatically retried; check network if persistent |\n| `AxleRateLimitedError` | 429 | Too many requests | Automatically retried with backoff |\n| `AxleInvalidArgument` | 400 | Malformed request (missing parameters, invalid arguments) | Fix the request |\n| `AxleForbiddenError` | 403 | Access denied | Check credentials/permissions |\n| `AxleNotFoundError` | 404 | Resource not found | Check the endpoint or resource ID |\n| `AxleConflictError` | 409 | Request conflicts with current state | Resolve the conflict |\n| `AxleInternalError` | 500 | Server bug | [Report it](https://github.com/AxiomMath/axiom-lean-engine/issues) |\n| `AxleBrowserLoginRequiredError` | 302 | Endpoint is gated behind interactive browser sign-in | Access via browser, or use an endpoint intended for CLI access |\n| `AxleRuntimeError` | \u2014 | Operation reached the executors but failed | Usually not worth retrying \u2014 AXLE already retried across executors internally |\n| `LeanResourceExceeded` | \u2014 | Lean worker hit a resource cap (e.g. memory) for this input | Reduce the problem's memory allocation |\n| `LeanTimeout` | \u2014 | Lean worker exceeded its time budget for this input | Simplify the input or raise the timeout |\n\n### Automatic Retries\n\nThe client automatically retries transient errors with exponential backoff:\n\n- `AxleIsUnavailable` (503, connection errors)\n- `AxleRateLimitedError` (429)\n\nNon-retryable errors like `AxleInternalError` (500) and client errors (4xx) are raised immediately.\n\nBeyond the client's own retries, the AXLE gateway retries transient executor failures (worker crashes) across multiple executors before giving up \u2014 so an `AxleRuntimeError` usually means those internal retries are already exhausted and re-retrying rarely helps. `LeanResourceExceeded` and `LeanTimeout` are deterministic for a given input and are never retried: the same input yields the same outcome.\n\nTo catch all API errors at once, use the base class `AxleApiError`.\n"}, {"slug": "cli-reference", "title": "CLI Reference", "html_url": "https://axle.axiommath.ai/v1/docs/cli-reference/", "markdown": "# CLI Reference\n\n## Global Options\n\n```\naxle [OPTIONS] COMMAND [ARGS]\n\nOptions:\n  --version          Show version and exit\n  --url URL          API server URL\n  --json             Force JSON output\n```\n\n## Common Patterns\n\nAll commands require `--environment` (e.g., `--environment lean-4.28.0`).\n\nMost commands accept a file path or `-` for stdin:\n\n```bash\naxle check file.lean --environment lean-4.28.0\ncat file.lean | axle check - --environment lean-4.28.0\n```\n\nMany commands support `-o/--output` for writing to a file:\n\n```bash\naxle theorem2sorry solution.lean -o problem.lean --environment lean-4.28.0\n```\n\n### Parameter Formats\n\n**List parameters** use comma-separated values:\n\n```bash\naxle theorem2sorry file.lean --names foo,bar,baz --environment lean-4.28.0\naxle verify-proof statement.lean proof.lean --permitted-sorries helper1,helper2 --environment lean-4.28.0\n```\n\n**Dict parameters** use `key=value` pairs or a JSON file:\n\n```bash\naxle rename file.lean --declarations foo=bar,old_name=new_name --environment lean-4.28.0\naxle rename file.lean --declarations '{\"foo\": \"bar\"}' --environment lean-4.28.0\n```\n\n**Boolean flags** that default to `true` use `--no-` prefix to disable:\n\n```bash\naxle verify-proof statement.lean proof.lean --no-use-def-eq --environment lean-4.28.0\naxle normalize file.lean --no-failsafe --environment lean-4.28.0\n```\n\n## Exit Codes\n\n- `0` - Success (operation completed without errors)\n- `1` - Failure (general error)\n- `2` - File exists error (use -f to overwrite)\n- `3` - Validation failed (when using --strict flag)\n- `130` - Interrupted (Ctrl+C)\n"}, {"slug": "changelog", "title": "Changelog", "html_url": "https://axle.axiommath.ai/v1/docs/changelog/", "markdown": "# Changelog\n\nAll notable changes to this project will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n\n\n\n## [Releases]\n\n## v1.7.0 - August 12, 2026\n\nThis update reworks how global Lean options are handled in the (new) `global_options`, `delab_options`, `mathlib_options`, and `verbosity` fields. It also comes with new machine-readable docs endpoints under [`/v1/docs`](https://axle.axiommath.ai/v1/docs), which the MCP server uses to pull instructions for tools before using them.\n\n### Removed\n- Removed Lean 4.32.0 due to a [kernel soundness bug](https://leodemoura.github.io/blog/2026-8-1-postmortem-for-kernel-soundness-bug-14576/) in Lean and replaced with Lean 4.32.2, which patches the bug. Note that although we only remove Lean 4.32.0, this bug affects all prior versions as well.\n### Added\n- Added Lean 4.33.0, and added Lean 4.32.2 as a replacement for Lean 4.32.0.\n- Various additions to options configurations across all tools:\n\n    - The `mathlib_options` field (previously only on `check`, `verify_proof`, and `highlight`) is now accepted by every tool. It applies to everything the request elaborates. Note that this can change tool behavior; for example, the `enable_autoImplicit` pass in `repair_proofs` is mostly inert with Lean's defaults, but relevant with Mathlib options enabled.\n    - The pretty-printing tools (`normalize`, `extract_decls`, `extract_theorems`, `have2lemma`, `sorry2lemma`, `disprove`) now accept a `delab_options` field: Lean pretty-printer options, e.g. `{\"pp.fieldNotation\": false}`, applied on top of the options that tool pretty-prints with. Only `pp.*` options are accepted. See the `delab_options` and `verbosity` field documentation for each tool for more details.\n    - Every tool now accepts a `global_options` field: Lean options, e.g. `{\"maxHeartbeats\": 400000}`, applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. The format is the same as `delab_options`, but any registered option can be set, and values may be booleans, integers, or strings to match the type the option was declared with.\n\n- This documentation is now also served in machine-readable form under [`/v1/docs`](https://axle.axiommath.ai/v1/docs): `pages.json` (a page manifest in navigation order), `all.json` (the manifest with each page's source markdown inlined), and `raw/{slug}.md` (one page's source markdown). See the documentation homepage for examples.\n\n### Fixed\n- `verify_proof` with `mathlib_options` enabled now parses `formal_statement` with Mathlib settings as well; previously they only applied to `content`.\n- `repair_proofs` now properly counts all time dedicated to file elaboration in the `parse_ms` timing field, not just the initial parse.\n- Stray, ambiguous warnings are no longer emitted when a `simplify_theorems` iteration fails and falls back to the last stable version of the file.\n- Fixed a rare case in `simplify_theorems` where removing an entire unused tactic sequence could cause the simplification pass to fail in older Lean versions. For example, [this request](https://axle.axiommath.ai/simplify_theorems#r=5acc9f84-6d3b-4e6d-ac51-241b28f2f624) used to fail and return the original content unchanged, because removing `norm_num` is invalid. Now, a synthetic `skip` tactic is inserted to ensure the content re-elaborates cleanly.\n- Fixed a set of redundant reparses in `extract_decls` when computing each document's `content` field. Files containing many declarations should now expect a significant speed increase. (Up to 10x faster!)\n\n\n## v1.6.0 - July 29, 2026\n\nThis update ships three sets of features: first, a new tool, [`extract_proof_states`](https://axle.axiommath.ai/v1/docs/tools/extract_proof_states), for proof state analysis in bulk. Second, better support for \"find-the-answer\"-style problems, e.g. problems where the candidate solution must provide an explicit answer in addition to the formal proof; you can read more about such problems [here](https://axle.axiommath.ai/v1/docs/tools/verify_proof#find-the-answer-problems). Third, various fine-tuned efficiency improvements that customize elaboration to skip unnecessary work. Most users can ignore these settings, but instructions are available on the [troubleshooting page](https://axle.axiommath.ai/v1/docs/troubleshooting/#slow-lean-execution).\n\n### Added\n- Added a new tool: `extract_proof_states`, which returns the tactic proof state at the end of each line of the given Lean code, as shown in an editor's goal panel, as a list of `{line, proof_state}` objects. Output is capped at 10,000,000 characters; past the cap, states are omitted and the `truncated` field is set. See the [`extract_proof_states` page](https://axle.axiommath.ai/v1/docs/tools/extract_proof_states) for details.\n- `extract_decls` now accepts an `elaborate_proofs` option (default `true`). When set to `false`, every theorem proof is replaced with `sorry` before elaboration, which is much faster on proof-heavy files. Statement-level fields (`type`, `type_hash`, `signature`, type dependencies, positions) are still computed, and non-theorem declarations are unaffected. Some fields are empty in this mode, and others are incomplete, so use with caution. Use this to cheaply list a file's declarations (e.g. to feed the `names`/`indices` parameters of other tools). See the [`extract_decls` page](https://axle.axiommath.ai/v1/docs/tools/extract_decls) for details. Regular users can safely disregard this option.\n- Various transformation tools (`theorem2sorry`, `theorem2lemma`, `rename`, `merge`, `have2sorry`, `have2lemma`, `sorry2lemma`) now accept a `reparse` option (default `true`). These tools re-elaborate their output to report fresh `lean_messages`; setting `reparse` to `false` skips that step (often the most expensive part of the request) and returns empty `lean_messages`. Use it when you don't need compiler feedback on the transformed output. Regular users can safely disregard this option.\n- Added a `remove_opens` pass to `normalize`. It removes `open` commands, including `open ... in` prefixes. Combine it with `expand_decl_names` and `expand_scoped_notations` so that names and notations no longer rely on the removed `open`s; a tool warning is emitted otherwise. See the [`normalize` page](https://axle.axiommath.ai/v1/docs/tools/normalize) for details.\n\n### Changed\n- `verify_proof` no longer elaborates the proofs of declarations listed in `permitted_sorries`, which is faster but means errors or disallowed axioms inside those proofs now go unnoticed instead of failing verification. This is a change from before, when only explicit sorries are permitted but errors/disallowed axioms are still registered as failures.\n- When `verify_negation` is set, sorried `def`s in `formal_statement` are now universally quantified in the negated theorem types: for `def answer : T := sorry` and `theorem main : P answer`, the disproof must prove `\u2200 answer : T, \u00ac P answer` instead of `\u00ac P answer`. The quantifiers follow the declaration order of the sorried defs, and the disproof no longer needs to declare them. See the `verify_negation` parameter documentation for details and limitations. This affects [\"find the answer\"-style problems](https://axle.axiommath.ai/v1/docs/tools/verify_proof#find-the-answer-problems).\n\n### Fixed\n- Fixed proofs containing `bv_decide`, which previously failed with an unintelligible message. Now supported as in vanilla Lean.\n- Sorried-out definitions in formal statements are no longer unfolded in `verify_proof`. For example, [this case](https://axle.axiommath.ai/verify_proof#r=c46426e9-365d-4238-b585-d60a4484a49b) used to fail because with `use_def_eq=True`, the `answer` would be unfolded into different values in the formal statement and the candidate solution, causing `verify_proof` to reject the proof with the message \"Theorem 'problem' does not match expected signature\". This affects [\"find the answer\"-style problems](https://axle.axiommath.ai/v1/docs/tools/verify_proof#find-the-answer-problems).\n- Fixed two bugs affecting the `normalize` tool:\n\n  - The `expand_scoped_notations` option incorrectly failed to expand notations added to the namespace via the `open scoped` command, and sometimes incorrectly expanded namespaced global notations.\n  - The `expand_decl_names` option sometimes incorrectly expanded synthetic identifiers that were attached to the same source range as an identifier.\n\n\n\n\n## v1.5.0 - July 15, 2026\n\nThis update comes with new `LeanTimeout`/`LeanResourceExceeded` exceptions, `names`/`indices` selection on `check` and `extract_decls`/`extract_theorems` (to skip elaboration for faster runs), and new fields and options (`unfolded_type_hash`, `verify_negation`, `verbosity`).\n\n### Added\n- Added `LeanResourceExceeded` and `LeanTimeout` exceptions. The Lean worker exceeding its memory cap or time budget now raises a distinct, non-retryable exception instead of the generic `AxleRuntimeError`, so callers can record it as a deterministic outcome rather than retrying.\n- `check`, `extract_decls`, and `extract_theorems` now accept optional `names` and `indices` arguments (as `theorem2sorry` and similar tools already do) to select which declarations to process. The returned documents / messages are restricted to them, and elaboration is skipped for the proofs of unselected declarations. This is a speed feature and should be used when elaborating the whole file is too slow. Note that this changes the behavior of the tool: the returned Lean messages will be incomplete, and the per-document `content` field for `extract_decls` and `extract_theorems` is returned empty. See the [`check`](https://axle.axiommath.ai/v1/docs/tools/check) and [`extract_decls`](https://axle.axiommath.ai/v1/docs/tools/extract_decls) pages for more details. Regular users with no speed concerns can disregard these options.\n- `extract_decls` documents now include an `unfolded_type_hash` field: the type hash after unfolding module-local elaboration auxiliaries (e.g. `foo.match_1`), so types differing only by such an auto-generated name deduplicate where `type_hash` would not. See the [`extract_decls` page](https://axle.axiommath.ai/v1/docs/tools/extract_decls) for details. The behavior of the base `type_hash` field is unchanged.\n- `verify_proof` now accepts a `verify_negation` option (default `false`). When set, `verify_proof` additionally checks whether `content` proves the *negation* of `formal_statement` \u2014 i.e. whether it disproves the statement \u2014 and reports the result in a new `negation` field, which carries the same `okay`, `tool_messages`, and `failed_declarations` as the top-level result. The field is omitted unless `verify_negation` is set. Regular users can ignore this option.\n- `disprove` and `extract_decls` now accept a `verbosity` parameter (0=default, 1=robust, 2=extra robust), which affects the pretty-printed types output by the tools (the `negated` field in `disprove`, and the per-document `type` field in `extract_decls`). Higher verbosity levels make the pretty-printer more explicit, which helps when the default output re-elaborates ambiguously. This is the same behavior as existing tools with the `verbosity` parameter, e.g. `have2lemma`, `sorry2lemma`.\n- Added `AxleClient.get_latest_environment()`, which fetches the available Lean+Mathlib environments and returns the latest one.\n- Added instructions for citing AXLE in the documentation. See [Citing AXLE](https://axle.axiommath.ai/v1/docs/#citing-axle).\n- Added Lean 4.32.0 support.\n\n### Fixed\n- Fixed a bug in tools that analyze term-mode goals (`have2lemma`, `sorry2lemma`, `disprove`) causing goal extraction to silently fail with `include_whole_context=false`. For example, [this request](https://axle.axiommath.ai/sorry2lemma#r=604f708c-99f1-49f0-9c4a-dd987d2ad024) failed in previous versions, leaving the content unchanged.\n- `verify_proof` is now module-aware. Previously, module dependencies would be treated as disallowed axioms when not using the default header. For example, [this request](https://axle.axiommath.ai/verify_proof#r=24ac6a65-5afc-4119-bfb8-a7a1e6af43dd) was wrongfully rejected but now passes.\n\n### Changed\n- Various tools now *skip proof elaboration* for unselected declarations when `names` or `indices` is provided. This is a speed change; however, any outputs pertaining to the unselected declarations (e.g. Lean messages from their proofs) are unreliable and should not be used.\n\n\n\n\n## v1.4.0 - July 1, 2026\n\nAXLE will be presented at the 3rd AI for Math Workshop at ICML 2026 as a contributed talk! Read the technical report on [arXiv](https://arxiv.org/abs/2606.26442).\n\nThis update comes with two notable changes to `ignore_imports` and the `okay`/`tool_messages` fields, and a variety of additional features:\n\n### Changed\n\n- `ignore_imports` now defaults to `true`. When your code's imports don't match the environment's default header, AXLE substitutes the default header (reusing the cached environment) instead of raising an error. Setting `ignore_imports=false` no longer errors on a mismatch; instead AXLE processes your imports as written, which is significantly slower and may give incorrect results if a required dependency such as `Mathlib.Tactic` is missing (a warning is returned in these cases). See [Import Mismatches](https://axle.axiommath.ai/v1/docs/troubleshooting/#import-mismatches) for details.\n\n- Reworked the `tool_messages` and `okay` fields for a few tools. See [Interpreting the `okay` field](https://axle.axiommath.ai/v1/docs/troubleshooting/#interpreting-the-okay-field) for details.\n\n    - `check` now reports validation findings (`sorry`, disallowed axioms, unsafe definitions) as `tool_messages` warnings instead of errors. `okay` continues to reflect compilation only, and the offending declarations remain listed in `failed_declarations`.\n    - `repair_proofs` now reports failed repairs (e.g. terminal tactics that fail to prove a `sorry`) as `tool_messages` errors instead of warnings, so `okay` is `True` only when the repaired code compiles *and* all repairs succeed.\n\n- Many tools now inspect values of opaques: `opaque` and `partial def` (and `theorem` in 4.30+).\n\n    - `merge` now considers opaque bodies, not just types, when de-duplicating.\n    - `extract_decls` now populates value fields for opaques.\n\n- In `check` and `verify_proof`, file-level validation errors now invalidate all contained declarations \u2014 e.g. use of `open private` results in every declaration being added to `failed_declarations`.\n- Reworded one of the `verify_proof`/`check` error messages to be more descriptive.\n\n    - Before: `Declaration '{name}' uses 'sorry' which is not allowed in a valid proof`\n    - After: `Declaration '{name}' is incomplete (uses 'sorry' or has errors)`\n\n### Added\n- Added Lean 4.30.0 and 4.31.0 support.\n- Added a `relax_defeq_transparency` repair pass to `repair_proofs` (on by default). Lean 4.29's `backward.isDefEq.respectTransparency` (default `true`) keeps `isDefEq` from unfolding reducible/instance definitions when unifying implicit arguments, breaking proofs that relied on it. Mathlib turns it off per-theorem. This repair prepends `set_option backward.isDefEq.respectTransparency false in` when the fix gets the proof further (all errors resolved, or the first error appears later in the source). On environments without the option, the repair is a no-op.\n- `extract_decls` and `extract_theorems` report four new per-declaration fields: `type_depth`, `term_depth`, `wall_ms`, and `heartbeats`. See the [`extract_decls` page](https://axle.axiommath.ai/v1/docs/tools/extract_decls) for more details.\n\n### Removed\n- Removed the `http2` parameter from the `AxleClient` constructor, which was slowing the client down. The client now uses HTTP/1.1 exclusively (via `aiohttp`); the optional HTTP/2 transport and its `httpx` dependency have been dropped. Code that passed `http2=...` should remove that argument.\n\n### Fixed\n- Fixed `extract_decls` bug for opaques where value dependencies were misclassified as type dependencies.\n- Fixed `disprove` bug negating only the goal instead of negating the entire declaration type. The negated goal is now returned in a new `negated` field (a map from theorem name to negated goal) instead of being appended to each `results` message.\n- Browser-based clients (web apps, extensions, in-page demos) can call AXLE directly! The HTTP API now supports cross-origin (CORS) requests: `OPTIONS` preflights return `204` with `Access-Control-Allow-*` headers, and every `/api/v1/` response carries `Access-Control-Allow-Origin`.\n- Fixed HTTP status codes on `/api/v1/` endpoints: an unknown tool name now returns `404` (previously `200` with a `user_error` body), and methods other than `POST` return `405` with an `Allow` header (previously `422`).\n- Fixed a rare bug resulting in lost executor slots. This bug used to cause a rare user-side failure or, more commonly, very high latency for some requests.\n\n\nThanks to @SSingh-07 on Github for submitting a few issues, which we have fixed in this update!!\n\n## v1.3.0 - June 3, 2026\n\nThis update comes with support for all declaration kinds, a reworked `repair_proofs`, link shortening, and broader MCP support.\n\n### Added\n\n- Added *link shortening* to the gateway. The web UI has been updated correspondingly. Try it out: [https://axle.axiommath.ai/check#r=7d70453f-813f-4d19-8de9-44793dafa835](https://axle.axiommath.ai/check#r=7d70453f-813f-4d19-8de9-44793dafa835)\n- Added Claude web, desktop, and mobile support to the [`axiom-axle-mcp`](https://pypi.org/project/axiom-axle-mcp/) MCP server via a hosted endpoint at `https://mcp.axiommath.ai/mcp`. See the [Quick Start](https://axle.axiommath.ai/v1/docs/quickstart/#mcp-server) for details. Thanks to Andrew Sutherland for suggestions on setting up this hosted instance.\n- Added three new fields to the `info` field of every response to identify the executor version your request was handled on: `_executor_commit_sha`, `_executor_docker_image_id`, and `_executor_artifact_sha256`.\n\n### Changed\n\n- Added a new option `theorems_only` (default `true`) to all tools that select over theorems/lemmas. These tools now have the ability to select over **all declaration kinds**: `theorem2lemma`, `theorem2sorry`, `simplify_theorems`, `repair_proofs`, `have2lemma`, `have2sorry`, `sorry2lemma`, `disprove`:\n\n    - To use this feature, set `theorems_only` to `false`. For backwards compatibility (default), keep `theorems_only` set to `true`.\n    - You can now sorry out any declaration body, simplify/repair any declaration containing a proof, and extract lemmas from any `sorry` locations and any `have` statement locations in any declarations, including definitions, opaques, instances, etc.\n    - For `theorem2lemma` and `disprove`, the new setting is a no-op on non-theorem kinds.\n    - Note that the value of `theorems_only` affects what the `names` and `indices` fields select over. When `theorems_only` is `false`, names and indices refer to **all** declarations, not just theorem kinds.\n\n- Reworked `repair_proofs` (the first of several planned changes):\n\n    - Added two new passes to `repair_proofs`: `remove_unknown_options`, which strips unknown options both at the command-level and within proofs/terms, and `enable_autoImplicit`, which restores the `autoImplicit` option at the beginning of a theorem if an unknown identifier error occurs in a theorem's type signature.\n    - Added command-level re-elaboration to `repair_proofs`, allowing repairs to stack (for example, when applying terminal tactics reveals another error to fix).\n    - `replace_unsafe_tactics` now warns the user when replacing `native_decide` with `decide +kernel` fails. The tactic location is now left untouched.\n    - `apply_terminal_tactics` now warns when no terminal tactics could be successfully applied at a given location in `repair_proofs`.\n    - Fixed a bug in `apply_terminal_tactics` allowing malformed proofs with metavariables to be counted as successes in `repair_proofs`.\n\n- `verify_proof` now permits `partial def` and `opaque`. These checks were overly strict previously and do not raise soundness concerns.\n- `merge` now deduplicates other declaration kinds: axioms, opaques, inductives, classes, structures, etc. Previously, only theorems and definitions were eligible for deduplication.\n- `extract_decls` now names anonymous declarations (examples, anonymous instances) by their start position, line then column (e.g. `_example_12_0`), rather than a running counter (e.g. `_example_0`), so the placeholder is stable and remains unique across a file even when several share a line.\n- Added the `merge_duplicates` (default `false`) option to `sorry2lemma`, which merges extracted lemmas that are duplicates (either with other lemmas, or to the existing top-level theorem/lemma from which they are extracted) by definitional equality into a single lemma with all callsites pointing at it. Existing behavior can be retained with the default setting `merge_duplicates=false`.\n\n\n### Fixed\n\n- Added faster, more graceful retries on certain classes of connection errors. Minor change.\n\n\n## v1.2.1 - April 29, 2026\n\nThis is a minor update deprecating `extract_theorems`, switching to HTTP/2, and adding an extra pass to `normalize`.\n\n### Deprecated\n\n- `extract_theorems` has been deprecated and will no longer be updated. Please use `extract_decls` instead, which supports all declaration kinds (def, theorem, lemma, abbrev, instance, structure, etc.).\n\n### Changed\n\n- The AXLE client now uses HTTP/2 by default. We don't expect any significant performance differences from this change, but feel free to file a bug report if this is not the case. Users may set the `http2` parameter to false in the client constructor to revert back to the original HTTP/1.1 settings.\n\n### Added\n\n- Added a new option `expand_scoped_notations` to the `normalize` tool, which delaborates scoped notations into their expanded forms. See the [`normalize` documentation page](https://axle.axiommath.ai/v1/docs/tools/normalize/#available-normalizations) for details.\n\n### Fixed\n\n- Fixed a bug in the executors causing requests to hang, occasionally resulting in abnormally high latencies.\n\n## v1.2.0 - April 15, 2026\n\nNew `extract_decls` tool for extracting all declaration kinds, and corresponding updates to `extract_theorems`. Users using `extract_theorems` (which will be deprecated in a future update) should migrate to `extract_decls`.\n\n### Added\n\n- Added two new fields in `extract_theorems` to be consistent with `extract_decls` (see below):\n    - `kind`: always `theorem` for `extract_theorems`.\n    - `declaration_messages`: same content as `theorem_messages`. `theorem_messages` is now deprecated and will be removed in a future update.\n- Added `extract_decls`, an upgraded version of `extract_theorems` that extracts all declaration kinds.\n    - New `kind` field in each document. Possible values: `theorem`, `def`, `abbrev`, `axiom`, `opaque`, `structure`, `class`, `class inductive`, `inductive`, `instance`, `example`, `unknown`\n    - Note: Not all fields are meaningful for all declaration kinds (e.g., `proof_length`/`tactic_counts` only apply to theorems/lemmas with tactic proofs.)\n    - This tool should be used instead of `extract_theorems` as it is a strict superset of functionality. `extract_theorems` will be deprecated in a future update.\n\n### Fixed\n\n- Added \"Last Used\" and \"Requests (24h)\" columns to the API key console page for better visibility into API key usage.\n\n\n## v1.1.1 - April 8, 2026\n\nThis is a minor update coming with default option changes, a Lean version bump, and bug fixes.\n\n### Changed\n\n- [!] We are turning *on* the `autoImplicit` and turning *off* the `pp.unicode.fun` Lean options. AXLE will now automatically insert implicit variables when they are missing. **This is a significant behavioral change, check your code!** These settings are consistent with Lean's default. The previous options were remnants from internal use preferences.\n- [!] **We have renamed `mathlib_linter` to `mathlib_options`**, which now sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. Use this toggle to enable the stricter defaults that Mathlib uses by convention.\n\n### Added\n\n- Added Lean 4.29.0 support.\n- Added support for glob patterns in the `permitted_sorries` field for `verify_proof`. See the `verify_proof` documentation page under the `permitted_sorries` field for example use cases.\n\n### Fixed\n\n- Fixed a bug causing timeouts to be capped at 10 minutes. All requests now max out at 15 minutes (with documentation updated correspondingly).\n\n## v1.1.0 - April 1, 2026\n\n\ud83c\udf89 After mass feedback from the public, we're excited to announce that AXLE is switching from Lean to Rocq. The new name will be **AXRE** (Axiom Rocq Engine). All existing Lean proofs will be automatically translated using GPT-2. \ud83d\ude80\n\n### Changed\n\n- [!] Removed `document_messages` from the response of `extract_theorems` \u2014 to replicate old behavior, run the `content` field of the resulting documents through the `check` tool. This change significantly improves the speed of `extract_theorems`.\n- [!] `includeEndPos` has been turned on for Lean messages. This changes the format from:\n`-:4:38: error: Function expected at...`\nto (when endPos is available):\n`-:4:38-4:43: error: Function expected at...`\nThis change affects all tools with Lean messages.\n- Significantly reworked the Lean executor pool backend.\n    - Latency has been decreased by 50% in most cases. For longer requests, the new executors can be more than 5 times faster!\n    - Previously, the first request to each environment required a ~10s warmup. This is no longer the case, and so requests will be more faithful to their Lean timeout limits (not including queueing / waiting for available slots).\n    - Eliminates a security risk involving persistent Lean workers.\n- Improved the Lean worker warm-up pipeline. Worker scale-up is also more aggressive than before. In the worst case, when all workers are completely occupied / offline, users should expect no more than a 2-3 minute delay before more worker capacity spins up.\n\n### Fixed\n\n- Removed redundant parsing resulting in occasional speedups in `repair_proofs`, `normalize`, etc. when content does not change.\n- Pruned missing executors from the gateway registry. Fixes a bug with autoscaling improperly triggering.\n\n\n## v1.0.2 - March 18, 2026\n\nThis is a minor patch improving some return values / error messages and shipping efficiency speedups.\n\n### Added\n\n- Added explicit `okay` return value to `repair_proofs`.\n\n### Changed\n\n- Improved error messages for unknown options in `simplify_theorems`, `repair_proofs`, `normalize`\n- Improved error messages for `ignore_imports` error (with links to relevant docs)\n- Improved the efficiency of `merge`, bringing down the time spent on large requests by 20-30%.\n\n\n## v1.0.1 - March 11, 2026\n\nThis is a minor patch with new documentation pages, increased rate limits, and bug fixes.\n\n### Added\n\n- Added [Changelog](https://axle.axiommath.ai/v1/docs/changelog/) and [Troubleshooting](https://axle.axiommath.ai/v1/docs/troubleshooting/) to the documentation pages.\n\n### Fixed\n\n- Increased request limits and fixed a typo in the documentation. Users with an API key are now limited to 20 active requests, and anonymous users are limited to 10 active requests.\n- Increased maximum timeout to 15 minutes (from 5 minutes).\n- Environments are now sorted by prefix (alphabetically) and then by version number (more recent versions first)\n- Fixed a bug with `disprove` failing to recognize implicit local variables. This bug was [found by Bulhwi Cha](https://leanprover.zulipchat.com/#narrow/channel/219941-Machine-Learning-for-Theorem-Proving/topic/Axiom.20Lean.20Engine/near/578064991) on Lean Zulip.\n\n\n## v1.0.0 - March 4, 2026\n\nWe're excited to release AXLE to the public! AXLE provides proof verification and manipulation primitives we've used across all of our research efforts, including training AI models and AxiomProver's 12/12 on Putnam 2025.\n\n[Playground](https://axle.axiommath.ai) | [API docs](https://axle.axiommath.ai/v1/docs/) | [Why we built AXLE](https://axiommath.ai/territory/releasing-axle) | [Request more capacity](https://forms.gle/CdLKu45tEsRXtFQ29) | axle@axiommath.ai\n\nJoin the discussion, ask questions, and share feedback on the [Lean Zulip](https://leanprover.zulipchat.com/#narrow/channel/113486-announce/topic/Axiom.20Lean.20Engine/with/577609358).\n\n### Added\n\n- Initial release of AXLE Python client\n- Async client (`AxleClient`) with all 14 API tools:\n    - `verify_proof` - Verify proofs against formal statements\n    - `check` - Check Lean code for errors\n    - `extract_theorems` - Extract theorems with dependencies\n    - `rename` - Rename declarations\n    - `theorem2lemma` - Convert theorem/lemma keywords\n    - `theorem2sorry` - Replace proofs with sorry\n    - `merge` - Combine multiple Lean files\n    - `simplify_theorems` - Simplify proofs\n    - `repair_proofs` - Repair broken proofs\n    - `have2lemma` - Extract have statements to lemmas\n    - `have2sorry` - Replace have statements with sorry\n    - `sorry2lemma` - Extract sorries and errors to lemmas\n    - `disprove` - Attempt to disprove theorems\n    - `normalize` - Standardize formatting\n- CLI tool with commands for all tools\n- Helper functions for string manipulation\n- Configuration via environment variables\n- Type hints and PEP 561 compliance\n- Comprehensive documentation\n"}, {"slug": "tools/verify_proof", "title": "verify_proof", "html_url": "https://axle.axiommath.ai/v1/docs/tools/verify_proof/", "markdown": "# verify_proof\n\nValidate a candidate Lean theorem and check that it conforms to the given formal statement.\n\n[Try this example in the web UI](https://axle.axiommath.ai/verify_proof#data=eyJmb3JtYWxfc3RhdGVtZW50IjoiZGVmIEEgOj0gNFxudGhlb3JlbSBtYWluIDogQSA9IDUgOj0gc29ycnkiLCJjb250ZW50IjoiZGVmIEEgOj0gNVxudGhlb3JlbSBtYWluIDogQSA9IDUgOj0gcmZsIiwibWF0aGxpYl9vcHRpb25zIjpmYWxzZSwidXNlX2RlZl9lcSI6dHJ1ZSwiaWdub3JlX2ltcG9ydHMiOnRydWUsImVudmlyb25tZW50IjoibGVhbi00LjI3LjAiLCJ0aW1lb3V0X3NlY29uZHMiOjEyMH0%3D)\n\n## \"Find the Answer\" Problems\n\nThe primary use case of `verify_proof` is simple: pass in a formal statement and a candidate solution, and check that the candidate is a valid proof of the formal statement.\n\nHowever, some questions take a different format, like:\n\n> Find `x` such that `x * x = 4`.\n\n> Prove or disprove that the sum of `1/p` over prime `p` converges.\n\nWe call such examples \"find the answer\" problems. Typically, the way we formalize this in Lean is as follows:\n\n```\ndef answer : Nat := sorry\n\ntheorem question (n : Nat) : P n \u2194 n = answer := sorry\n```\nfor some predicate `P`. That is, the formal statement contains a sorried-definition that needs to be filled in, in addition to the main theorem statement. A simple candidate solution might look like this:\n\n```\ndef answer : Nat := 3\n\ntheorem question (n : Nat) : n = 2 + 1 \u2194 n = answer := by grind [answer]\n```\n\n`verify_proof` handles such cases:\n\n1. Definitions that are sorried out in the formal statement can take on any value in the candidate and still pass verification.\n\n2. These definitions will also not be unfolded during reduction if the `use_def_eq` option is toggled. This ensures that the definition does not unfold to `sorry` in the formal statement, in which case the problem statement and solution statement will not match.\n\n3. To disprove such a problem, set `verify_negation`. The disproof must show that no answer satisfies the theorem: the negated theorem universally quantifies over the sorried definitions. See the `verify_negation` parameter for details.\n\nNote that `verify_proof` does not perform any checks on the form of `answer`. This is important because it is easy to find an \"answer\" that trivially satisfies the theorem statement. The main challenge is in finding a \"closed-form solution\" to the question statement. But what counts as a \"closed-form solution\" is ambiguous and perhaps a philosophical question that cannot be answered in Lean, so we do not address it here. This is a common point of discussion among the community, and we recommend visiting the [Lean Zulip](https://leanprover.zulipchat.com) for more information on this point.\n\n## See Also\n\nIn the interest of scalability, `verify_proof` trusts the Lean environment to behave correctly. That's usually fine, but a sufficiently creative adversary can exploit this to make invalid proofs appear valid with Lean metaprogramming.\n\nThis is a known limitation that we don't expect to address, since the alternatives below cover adversarial use cases.\n\nIf you're verifying untrusted code, consider additionally using these other resources which perform a similar check. These run proofs in isolated environments and are less susceptible to known exploits, at the cost of speed:\n\n- [lean4checker](https://github.com/leanprover/lean4checker): Lean FRO-developed .olean verifier\n- [Comparator](https://github.com/leanprover/comparator): Lean FRO-developed gold standard for proof judges\n- [SafeVerify](https://github.com/GasStationManager/SafeVerify): battle-tested public proof checker\n\nWe recommend reading the [Lean4 reference page on this topic](https://lean-lang.org/doc/reference/latest/ValidatingProofs/) for more discussion.\n\nSee the corresponding [Github issue](https://github.com/AxiomMath/axiom-lean-engine/issues/2).\n\n## Input Parameters\n\n??? \"`formal_statement` \u00b7 str \u00b7 required \u00b7 Sorried theorem to verify against\"\n    The formal statement defines what the proof must satisfy. It should contain\n    `sorry` placeholders where proofs are expected. AXLE extracts all declarations\n    from this and checks that `content` provides valid implementations.\n\n    ```lean\n    -- formal_statement: defines the theorem signature\n    import Mathlib\n    theorem add_comm (a b : Nat) : a + b = b + a := by sorry\n    ```\n\n    ```lean\n    -- content: provides the actual proof\n    import Mathlib\n    theorem add_comm (a b : Nat) : a + b = b + a := Nat.add_comm a b\n    ```\n\n    Definitions and other declarations are also checked\u2014if `formal_statement`\n    contains `def foo := 5`, then `content` must define `foo` with the same value.\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Candidate proof to verify\"\n    The Lean source code containing the proof(s) to validate against the formal statement.\n\n??? \"`permitted_sorries` \u00b7 list[str] \u00b7 Theorems allowed to contain `sorry`\"\n    Use this when your proof relies on helper lemmas you haven't proven yet.\n    Theorems listed here won't trigger proof-related errors, so errors and disallowed axioms inside\n    them go unnoticed. Their statements are still checked.\n\n    ```python\n    result = await axle.verify_proof(\n        formal_statement=\"...\",\n        content=\"...\",\n        permitted_sorries=[\"helper_lemma\"],\n    )\n    ```\n\n    Names not present in the code are silently ignored.\n\n    This option is also useful for enabling tactics like `native_decide`, which introduce extra axioms:\n\n    - **Lean 4.28.0 and below:** include `Lean.trustCompiler`, `Lean.ofReduceBool`, and `Lean.ofReduceNat`.\n    - **Lean 4.29.0 and above:** `native_decide` axioms were reworked (see [here](https://github.com/leanprover/lean4/pull/12217)). Use glob patterns, e.g. `<theorem_name>._native.native_decide.*`, to allow all `native_decide`-related axioms for a given theorem.\n\n    **Note:** glob patterns do not defend against an adversary deliberately crafting malicious axioms with matching names, so we don't recommend using them with untrusted code.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`use_def_eq` \u00b7 bool \u00b7 default: `True` \u00b7 Use definitional equality for type comparison\"\n    When `false`, types are compared at face value (faster, but may very rarely reject valid proofs).\n\n    When `true`, types are compared after kernel reduction. However, locally-defined `def`s whose body is `sorry` and that appear in the type being compared are **not unfolded** during that reduction (in either file).\n\n    If the formal `def` is sorried, its body is not checked; the candidate may supply a concrete definition, provided the type still matches.\n\n    For example:\n\n    ```lean\n    -- formal_statement\n    def F : Nat \u2192 Type := sorry\n    theorem T : \u2200 n, Nonempty (F n) := sorry\n\n    -- content\n    def F : Nat \u2192 Type := fun _ => Unit\n    theorem T : \u2200 n, Nonempty (F n) := fun n => \u27e8()\u27e9\n    ```\n\n??? \"`verify_negation` \u00b7 bool \u00b7 default: `False` \u00b7 Also check whether `content` proves the negation of the statement\"\n    When `true`, AXLE additionally checks whether `content` proves the *negation* of\n    `formal_statement` (i.e. disproves it) and reports the result in the `negation`\n    field. `negation.okay` is `true` when `content` is a valid proof of the negation.\n\n    For [\"find the answer\" problems](#find-the-answer-problems), the sorried `def`s in\n    `formal_statement` represent answers the statement claims exist. The negation\n    universally quantifies over them: for `def answer : T := sorry` and\n    `theorem main : P answer`, the disproof must prove `theorem main : \u2200 answer : T, \u00ac P answer`.\n    The quantifiers appear in the order the sorried defs are declared in `formal_statement`.\n    The disproof does not need to declare the sorried defs; they are skipped in the\n    negation check (the main verification still requires them).\n\n    Limitations:\n\n    - A universe-polymorphic sorried def is not quantified over. It remains a constant in the negated type.\n    - A sorried def is only quantified over when it appears directly in a theorem's type. If it is reachable only through the body of another local def, it remains a constant in the negated type.\n    - When `formal_statement` contains several theorems, each is negated independently. This may be logically inconsistent but is out of the scope of `verify_proof`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`okay` \u00b7 bool \u00b7 True if proof passes verification\"\n    Returns `true` if the candidate proof is valid and matches the formal statement. Check `tool_messages.errors` for details when `false`.\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from verify_proof tool\"\n    Messages from the AXLE verification tool with `errors`, `warnings`, and `infos` lists.\n\n    Errors here mean `content` was compiling Lean code, but not a satisfactory proof of `formal_statement`.\n    Common errors include: \"Missing required declaration\", \"does not match expected signature\", \"uses sorry\".\n\n??? \"`failed_declarations` \u00b7 list \u00b7 Declaration names that failed validation\"\n    List of declaration names that have compilation or validation errors. These are declarations that do not compile, use `sorry`, use disallowed axioms, etc. A file-level validation finding (e.g. use of `open private`) marks every declaration in the file as failed.\n\n??? \"`negation` \u00b7 dict \u00b7 Result of verifying `content` against the negation of `formal_statement`\"\n    Present only when `verify_negation` is `true`. Reports whether `content` is a proof\n    of the *negation* of `formal_statement` \u2014 i.e. whether it disproves the statement \u2014\n    with the same `okay`, `tool_messages`, and `failed_declarations` fields, computed\n    against the negated theorem types.\n\n    `negation.okay` is `true` when `content` is a valid proof of the negation.\n    For [\"find the answer\" problems](#find-the-answer-problems), the sorried defs in\n    `formal_statement` are universally quantified in the negated theorem types and\n    are not required in `content`; see `verify_negation` above.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Verification Error Messages\n\n`tool_messages.errors` will match one of the following patterns:\n\n| Pattern | Meaning |\n|---------|---------|\n| `Missing required declaration '{name}'` | A symbol in `formal_statement` is missing from `content` |\n| `Kind mismatch for '{name}': candidate has {X} but expected {Y}` | Mismatch between definition kinds (e.g., `theorem` vs `def`) |\n| `Theorem '{name}' does not match expected signature: expected {X}, got {Y}` | Type of theorem has been changed |\n| `Definition '{name}' does not match expected signature: expected {X}, got {Y}` | Type or value of definition has been changed |\n| `Unsafe function '{name}' detected` | Use of an `unsafe` function |\n| `In '{name}': Axiom '{axiom}' is not in the allowed set of standard axioms` | Use of a disallowed axiom |\n| `Declaration '{name}' is incomplete (uses 'sorry' or has errors)` | Theorem is not proven. This error indicates one of two things: an explicit `sorry`, or an error while elaborating the proof. |\n| `Candidate uses banned 'open private' command` | Use of disallowed `open private` command |\n\n## Python API\n\n```python\nresult = await axle.verify_proof(\n    formal_statement=\"import Mathlib\\ntheorem citation_needed : 1 = 1 := by sorry\",\n    content=\"import Mathlib\\ntheorem citation_needed : 1 = 1 := rfl\",\n    environment=\"lean-4.28.0\",\n    permitted_sorries=[\"helper\"],  # Optional\n    mathlib_options=False,          # Optional\n    ignore_imports=True,          # Optional\n    timeout_seconds=120,           # Optional\n)\n\nprint(result.okay)  # True if proof is valid\nprint(result.content)  # The processed Lean code\n```\n\n## CLI\n\n**Usage:** `axle verify-proof FORMAL_STATEMENT CONTENT [OPTIONS]`\n\n```bash\n# Basic usage\naxle verify-proof statement.lean proof.lean --environment lean-4.31.0\n# With permitted sorries\naxle verify-proof statement.lean proof.lean --permitted-sorries helper1,helper2 --environment lean-4.31.0\n# Pipeline usage\ncat proof.lean | axle verify-proof statement.lean - --environment lean-4.31.0\n# Exit non-zero if proof is invalid\naxle verify-proof statement.lean proof.lean --strict --environment lean-4.31.0\n# Use in shell conditionals\nif axle verify-proof statement.lean proof.lean --strict --environment lean-4.31.0 > /dev/null; then\n    echo \"Proof valid\"\nfi\n# Specify different environment\naxle verify-proof statement.lean proof.lean --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/verify_proof \\\n    -d '{\"content\": \"import Mathlib\\ntheorem citation_needed : 1 = 1 := rfl\", \"formal_statement\": \"import Mathlib\\ntheorem citation_needed : 1 = 1 := by sorry\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"okay\": false,\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := rfl\\n\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [\n      \"Theorem 'foo' does not match expected signature: expected type 2 = 2, got 1 = 1\"\n    ],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"timings\": {\n    \"total_ms\": 160,\n    \"formal_statement_ms\": 3,\n    \"declarations_ms\": 0,\n    \"candidate_ms\": 28\n  },\n  \"failed_declarations\": [\"foo\"]\n}\n```\n"}, {"slug": "tools/check", "title": "check", "html_url": "https://axle.axiommath.ai/v1/docs/tools/check/", "markdown": "# check\n\nEvaluate Lean code and collect all messages (errors, warnings, and info). Use this to check if code compiles without verification against a formal statement, or to get the output of `#check` / `#eval` statements.\n\n> **Looking to confirm a proof?** `check` reports compilation only \u2014 its `okay` field stays `true` even when a declaration uses `sorry` or a disallowed axiom. If you want a single pass/fail for \"is this a complete, valid proof of a given statement,\" use [`verify_proof`](verify_proof.md) instead, which folds those failures into `okay`.\n\n[Try this example in the web UI](https://axle.axiommath.ai/check#data=eyJjb250ZW50IjoiI2NoZWNrIE5hdFxuI2NoZWNrIExpc3RcbiNldmFsIDEgKyAxIiwibWF0aGxpYl9vcHRpb25zIjpmYWxzZSwiaWdub3JlX2ltcG9ydHMiOnRydWUsImVudmlyb25tZW50IjoibGVhbi00LjI3LjAiLCJ0aW1lb3V0X3NlY29uZHMiOjEyMH0%3D)\n\n## See Also\n\nFor interactive compilation feedback without an API, try the [Lean 4 Web Playground](https://live.lean-lang.org).\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`okay` \u00b7 bool \u00b7 True if the Lean code compiles\"\n    Returns `true` if the code compiles without errors. Warnings don't affect this value.\n\n    This only reflects compilation. It does **not** mean the code is a complete, valid proof: a declaration that uses `sorry`, disallowed axioms, or unsafe definitions still compiles and leaves `okay` as `true`. Those findings are reported in `tool_messages.warnings` (with the offending names in `failed_declarations`). If you need to know whether the input is a real proof, also check that `failed_declarations` is empty, or better yet, use [`verify_proof`](verify_proof.md).\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from check tool\"\n    Messages from the check tool with `errors`, `warnings`, and `infos` lists.\n\n    Validation findings \u2014 uses of `sorry`, disallowed axioms, or unsafe definitions \u2014 are reported as warnings here. Use [`verify_proof`](verify_proof.md) to treat them as errors.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`failed_declarations` \u00b7 list \u00b7 Declaration names that failed validation\"\n    List of declaration names that have compilation or validation errors. These are declarations that do not compile, use `sorry`, use disallowed axioms, etc. A file-level validation finding (e.g. use of `open private`) marks every declaration in the file as failed.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.check(\n    content=\"import Mathlib\\n#eval 2+2\",\n    environment=\"lean-4.28.0\",\n    mathlib_options=False,     # Optional\n    ignore_imports=True,     # Optional\n    timeout_seconds=120,      # Optional\n)\n\nprint(result.okay)  # True if code compiles\nprint(result.okay and not result.failed_declarations)  # True if code compiles AND contains only complete, valid proofs\nprint(result.content)  # The processed Lean code\nprint(result.lean_messages.infos)  # [\"4\\n\"]\n```\n\n## CLI\n\n**Usage:** `axle check CONTENT [OPTIONS]`\n\n```bash\n# Basic usage\naxle check theorem.lean --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle check - --environment lean-4.31.0\n# Exit non-zero if code is invalid\naxle check theorem.lean --strict --environment lean-4.31.0\n# Use in shell conditionals\nif axle check theorem.lean --strict --environment lean-4.31.0 > /dev/null; then\n    echo \"Valid Lean code\"\nfi\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/check \\\n    -d '{\"content\": \"import Mathlib\\n#eval 2+2\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"okay\": true,\n  \"content\": \"import Mathlib\\n\\n#eval 2+2\\n\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": [\"4\\n\"]\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"timings\": {\n    \"parse_ms\": 30,\n    \"total_ms\": 62\n  },\n  \"failed_declarations\": []\n}\n```\n"}, {"slug": "tools/extract_theorems", "title": "extract_theorems", "html_url": "https://axle.axiommath.ai/v1/docs/tools/extract_theorems/", "markdown": "# extract_theorems\n\n!!! warning \"Deprecated\"\n    `extract_theorems` is deprecated and will be removed in a future release. Use [`extract_decls`](extract_decls.md) instead, which supports all declaration kinds (def, theorem, lemma, abbrev, instance, structure, etc.).\n\nSplit a file containing one or more theorems into smaller units, each containing a single theorem along with any required dependencies.\n\n[Try this example in the web UI](https://axle.axiommath.ai/extract_theorems#data=eyJjb250ZW50IjoiZGVmIGRvdWJsZSAobiA6IE5hdCkgOiBOYXQgOj0gMiAqIG5cbnRoZW9yZW0gZG91YmxlX2V2ZW4gOiDiiIAgbiA6IE5hdCwg4oiDIGsgOiBOYXQsIGRvdWJsZSBuID0gMiAqIGsgOj0gYnkgc29ycnlcbnRoZW9yZW0gZG91YmxlX3BvcyA6IOKIgCBuIDogTmF0LCBuID4gMCDihpIgZG91YmxlIG4gPiAwIDo9IGJ5IHNvcnJ5IiwiaWdub3JlX2ltcG9ydHMiOnRydWUsImVudmlyb25tZW50IjoibGVhbi00LjI3LjAiLCJ0aW1lb3V0X3NlY29uZHMiOjEyMH0%3D)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from extraction tool\"\n    Messages from the extraction tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`documents` \u00b7 dict \u00b7 Theorem names mapped to self-contained documents\"\n    Dictionary mapping theorem names to self-contained Lean code documents. Each key is a theorem name, and the value is a self-contained breakdown of the theorem, including a content field containing that theorem plus all dependencies it needs (imports, definitions, etc.).\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Document Fields\n\nEach document in the `documents` dictionary contains:\n\n??? \"`kind` \u00b7 str \u00b7 The kind of declaration\"\n    The kind of the declaration. For `extract_theorems`, this is always `\"theorem\"`. For `extract_decls`, possible values are: `theorem`, `def`, `abbrev`, `axiom`, `opaque`, `structure`, `class`, `class inductive`, `inductive`, `instance`, `example`, `unknown`.\n\n??? \"`declaration` \u00b7 str \u00b7 The declaration source code\"\n    The raw source code of this declaration.\n\n??? \"`content` \u00b7 str \u00b7 Standalone content including declaration and dependencies\"\n    Complete, self-contained Lean code that includes the declaration and all its local dependencies. Can be compiled independently.\n\n    **Empty** when the request specifies `names` or `indices`. In that mode only the selected declarations are returned and the unselected ones are *not* elaborated (a large speedup), so their transitive dependencies can no longer be computed. A `tool_messages` warning is emitted; all other fields (`type`, dependency lists, `is_sorry`, etc.) are still populated. Call the tool without `names`/`indices` to get the self-contained `content`.\n\n    Also empty when the request sets `elaborate_proofs=false`.\n\n??? \"`tokens` \u00b7 list[str] \u00b7 Raw tokens from the declaration\"\n    The declaration's source code split into tokens.\n\n??? \"`signature` \u00b7 str \u00b7 Declaration signature (everything before the body)\"\n    The declaration signature, e.g., `theorem foo (x : Nat) : x = x` or `def bar : Nat`.\n\n??? \"`type` \u00b7 str \u00b7 Pretty-printed type of the declaration\"\n    The type of the declaration as pretty-printed by Lean.\n\n??? \"`type_hash` \u00b7 int \u00b7 Hash of the canonical type expression\"\n    Hash of the canonical, alpha-invariant type expression. Useful for deduplication.\n\n??? \"`unfolded_type_hash` \u00b7 int \u00b7 Hash after unfolding local elaboration auxiliaries\"\n    Hash of the type after unfolding module-local elaboration auxiliaries; useful for deduplication.\n\n??? \"`type_depth` \u00b7 int \u00b7 Structural depth of the type expression\"\n    The nesting depth of the declaration's type as a Lean expression. This field maxes out at 255.\n\n??? \"`term_depth` \u00b7 int \u00b7 Structural depth of the value expression\"\n    The nesting depth of the declaration's value or proof as a Lean expression, or 0 when the declaration has no value. This field maxes out at 255.\n\n    Always `0` for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`is_sorry` \u00b7 bool \u00b7 Whether the declaration contains a sorry\"\n    True if the declaration contains a `sorry`.\n\n    Always `false` for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`index` \u00b7 int \u00b7 0-based index in original file\"\n    Position of this declaration in the original file. Note: indices may not be contiguous (mutual definitions share indices).\n\n??? \"`line_pos` \u00b7 int \u00b7 1-based line number where declaration starts\"\n    Line number where the declaration begins.\n\n??? \"`end_line_pos` \u00b7 int \u00b7 1-based line number where declaration ends\"\n    Line number where the declaration ends.\n\n??? \"`proof_length` \u00b7 int \u00b7 Approximate number of tactics in proof\"\n    Rough measure of proof complexity based on tactic count. Only meaningful for theorems/lemmas with tactic proofs.\n\n??? \"`tactic_counts` \u00b7 dict[str, int] \u00b7 Map of tactic names to occurrence counts\"\n    Breakdown of which tactics are used and how often. Only meaningful for theorems/lemmas with tactic proofs.\n\n??? \"`wall_ms` \u00b7 int \u00b7 Wall-clock milliseconds to elaborate the command\"\n    How long this command took to elaborate. This field reports wall-clock time, so it can vary from run to run.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`heartbeats` \u00b7 int \u00b7 Heartbeats consumed elaborating the command\"\n    Lean heartbeats consumed while elaborating this command.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`local_type_dependencies` \u00b7 list[str] \u00b7 Local dependencies of the type\"\n    Local declarations that the declaration's type depends on (non-transitive).\n\n??? \"`local_value_dependencies` \u00b7 list[str] \u00b7 Local dependencies of the body\"\n    Local declarations that the declaration's body/proof depends on (non-transitive).\n\n    Empty for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`external_type_dependencies` \u00b7 list[str] \u00b7 Immediate external dependencies of the type\"\n    External constants (builtins, imports) that appear in the type.\n\n??? \"`external_value_dependencies` \u00b7 list[str] \u00b7 Immediate external dependencies of the body\"\n    External constants (builtins, imports) that appear in the body/proof.\n\n    Empty for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`local_syntactic_dependencies` \u00b7 list[str] \u00b7 Local constants explicitly written in source\"\n    Local constants that appear literally in source (not from notation/macro expansion).\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`external_syntactic_dependencies` \u00b7 list[str] \u00b7 External constants explicitly written in source\"\n    External constants that appear literally in source (not from notation/macro expansion).\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`declaration_messages` \u00b7 dict \u00b7 Messages specific to this declaration\"\n    Lean messages (`errors`, `warnings`, `infos`) specific to this declaration in the original document.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`theorem_messages` \u00b7 dict \u00b7 (Deprecated) Messages specific to this declaration\"\n    Lean messages (`errors`, `warnings`, `infos`) specific to this declaration. For `extract_theorems`, this contains the same data as `declaration_messages`. For `extract_decls`, this is always empty.\n\n    !!! warning \"Deprecated\"\n        This field is deprecated. Use `declaration_messages` instead for new code.\n\n## Python API\n\n```python\nresult = await axle.extract_theorems(\n    content=\"import Mathlib\\ntheorem foo : 1 = 1 := rfl\\ntheorem bar : 2 = 2 := rfl\",\n    environment=\"lean-4.28.0\",\n    ignore_imports=True,  # Optional\n    timeout_seconds=120,   # Optional\n)\n\nprint(result.content)  # The processed Lean code\nfor name, doc in result.documents.items():\n    print(f\"{name}: {doc.signature}\")\n    print(f\"  Dependencies: {doc.local_value_dependencies}\")\n```\n\n## CLI\n\n**Usage:** `axle extract-theorems CONTENT [OPTIONS]`\n\n```bash\n# Extract to default directory\naxle extract-theorems combined.lean --environment lean-4.31.0\n# Extract to custom directory\naxle extract-theorems combined.lean -o my_theorems/ --environment lean-4.31.0\n# Force overwrite\naxle extract-theorems combined.lean -o my_theorems/ -f --environment lean-4.31.0\n# Pipeline usage\ncat combined.lean | axle extract-theorems - -o output/ --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/extract_theorems \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := rfl\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := rfl\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"timings\": {\n    \"total_ms\": 92,\n    \"parse_ms\": 87\n  },\n  \"documents\": {\n    \"foo\": {\n      \"kind\": \"theorem\",\n      \"declaration\": \"theorem foo : 1 = 1 := rfl\",\n      \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := rfl\",\n      \"tokens\": [\"theorem\", \"foo\", \":\", \"1\", \"=\", \"1\", \":=\", \"rfl\"],\n      \"signature\": \"theorem foo : 1 = 1\",\n      \"type\": \"1 = 1\",\n      \"type_hash\": 1326858781,\n      \"type_depth\": 5,\n      \"term_depth\": 4,\n      \"is_sorry\": false,\n      \"index\": 0,\n      \"line_pos\": 2,\n      \"end_line_pos\": 2,\n      \"proof_length\": 1,\n      \"tactic_counts\": {},\n      \"wall_ms\": 1,\n      \"heartbeats\": 4,\n      \"local_value_dependencies\": [],\n      \"local_type_dependencies\": [],\n      \"external_value_dependencies\": [\"rfl\", \"Nat\", \"OfNat.ofNat\", \"instOfNatNat\"],\n      \"external_type_dependencies\": [\"Eq\", \"Nat\", \"OfNat.ofNat\", \"instOfNatNat\"],\n      \"local_syntactic_dependencies\": [],\n      \"external_syntactic_dependencies\": [\"rfl\"],\n      \"theorem_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []},\n      \"declaration_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []}\n    }\n  }\n}\n```\n"}, {"slug": "tools/extract_decls", "title": "extract_decls", "html_url": "https://axle.axiommath.ai/v1/docs/tools/extract_decls/", "markdown": "# extract_decls\n\nSplit a file containing one or more declarations into smaller units, each containing a single declaration along with any required dependencies. This is the replacement for the deprecated [`extract_theorems`](extract_theorems.md) tool, and works for all declaration kinds (def, theorem, lemma, abbrev, instance, structure, etc.).\n\n[Try this example in the web UI](https://axle.axiommath.ai/extract_decls#data=eyJjb250ZW50Ijoic3RydWN0dXJlIFdlaWdodCB3aGVyZVxuICB2YWwgOiBOYXRcbiAgcG9zIDogdmFsID4gMCA6PSBieSBvbWVnYVxuXG5jbGFzcyBXZWlnaHRlZCAozrEgOiBUeXBlKSB3aGVyZVxuICB3ZWlnaHQgOiDOsSDihpIgV2VpZ2h0XG5cbmRlZiB0cml2aWFsV2VpZ2h0IDogV2VpZ2h0IDo9IOKfqDEsIGJ5IG9tZWdh4p+pXG5cbmluc3RhbmNlIDogV2VpZ2h0ZWQgTmF0IHdoZXJlXG4gIHdlaWdodCBfIDo9IHRyaXZpYWxXZWlnaHQiLCJpZ25vcmVfaW1wb3J0cyI6dHJ1ZSwiZW52aXJvbm1lbnQiOiJsZWFuLTQuMjguMCIsInRpbWVvdXRfc2Vjb25kcyI6MTIwfQ%3D%3D)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`verbosity` \u00b7 float \u00b7 default: `0` \u00b7 Pretty-printer verbosity level (0-2)\"\n    Preset buckets of pretty-printer options, addressing the ambiguity problem described under `delab_options`:\n\n    - `verbosity=0` (default): Standard pretty-printing options\n    - `verbosity=1`: Robust options with additional explicitness\n    - `verbosity=2`: Extra robust options with maximum explicitness (e.g. `pp.explicit=true`)\n\n    **Rule of thumb:** If you encounter type inference errors in the output\u2014especially involving coercions, casts, or polymorphic functions\u2014try increasing the verbosity level. Do note that at `verbosity=2`, type signatures may become incredibly complex and unreadable, so it should be used sparingly.\n\n    For finer-grained control over individual pretty-printer options, see `delab_options`, whose overrides apply on top of this preset.\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`elaborate_proofs` \u00b7 bool \u00b7 default: `True` \u00b7 Elaborate theorem proofs\"\n    If `true` (default), proofs are fully elaborated. Set to `false` to replace every theorem proof with `sorry` before elaboration (a large speedup on proof-heavy files). Statement-level fields and all fields of non-theorem declarations are unaffected.\n\n    When `false`, emits a `tool_messages` warning and:\n\n    - These fields are empty \u2014 do not use them: `content`; for theorems `is_sorry`, `term_depth`, `local_value_dependencies`, `external_value_dependencies`.\n    - These fields are incomplete \u2014 use with caution: `lean_messages`, `declaration_messages`; for theorems `local_syntactic_dependencies`, `external_syntactic_dependencies`, `wall_ms`, `heartbeats`.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from extraction tool\"\n    Messages from the extraction tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`documents` \u00b7 dict \u00b7 Declaration names mapped to self-contained documents\"\n    Dictionary mapping declaration names to self-contained Lean code documents. Each key is a declaration name, and the value is a self-contained breakdown of the declaration, including a content field containing that declaration plus all dependencies it needs (imports, definitions, etc.).\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Document Fields\n\nEach document in the `documents` dictionary contains:\n\n!!! note \"Field applicability\"\n    Not all fields are meaningful for all declaration kinds. For example, `proof_length` and `tactic_counts` are only relevant for theorems/lemmas with tactic proofs. For other declaration kinds (def, abbrev, structure, class, inductive, etc.), these fields may be empty or zero.\n\n??? \"`kind` \u00b7 str \u00b7 The kind of declaration\"\n    The kind of the declaration. For `extract_theorems`, this is always `\"theorem\"`. For `extract_decls`, possible values are: `theorem`, `def`, `abbrev`, `axiom`, `opaque`, `structure`, `class`, `class inductive`, `inductive`, `instance`, `example`, `unknown`.\n\n??? \"`declaration` \u00b7 str \u00b7 The declaration source code\"\n    The raw source code of this declaration.\n\n??? \"`content` \u00b7 str \u00b7 Standalone content including declaration and dependencies\"\n    Complete, self-contained Lean code that includes the declaration and all its local dependencies. Can be compiled independently.\n\n    **Empty** when the request specifies `names` or `indices`. In that mode only the selected declarations are returned and the unselected ones are *not* elaborated (a large speedup), so their transitive dependencies can no longer be computed. A `tool_messages` warning is emitted; all other fields (`type`, dependency lists, `is_sorry`, etc.) are still populated. Call the tool without `names`/`indices` to get the self-contained `content`.\n\n    Also empty when the request sets `elaborate_proofs=false`.\n\n??? \"`tokens` \u00b7 list[str] \u00b7 Raw tokens from the declaration\"\n    The declaration's source code split into tokens.\n\n??? \"`signature` \u00b7 str \u00b7 Declaration signature (everything before the body)\"\n    The declaration signature, e.g., `theorem foo (x : Nat) : x = x` or `def bar : Nat`.\n\n??? \"`type` \u00b7 str \u00b7 Pretty-printed type of the declaration\"\n    The type of the declaration as pretty-printed by Lean.\n\n??? \"`type_hash` \u00b7 int \u00b7 Hash of the canonical type expression\"\n    Hash of the canonical, alpha-invariant type expression. Useful for deduplication.\n\n??? \"`unfolded_type_hash` \u00b7 int \u00b7 Hash after unfolding local elaboration auxiliaries\"\n    Hash of the type after unfolding module-local elaboration auxiliaries; useful for deduplication.\n\n??? \"`type_depth` \u00b7 int \u00b7 Structural depth of the type expression\"\n    The nesting depth of the declaration's type as a Lean expression. This field maxes out at 255.\n\n??? \"`term_depth` \u00b7 int \u00b7 Structural depth of the value expression\"\n    The nesting depth of the declaration's value or proof as a Lean expression, or 0 when the declaration has no value. This field maxes out at 255.\n\n    Always `0` for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`is_sorry` \u00b7 bool \u00b7 Whether the declaration contains a sorry\"\n    True if the declaration contains a `sorry`.\n\n    Always `false` for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`index` \u00b7 int \u00b7 0-based index in original file\"\n    Position of this declaration in the original file. Note: indices may not be contiguous (mutual definitions share indices).\n\n??? \"`line_pos` \u00b7 int \u00b7 1-based line number where declaration starts\"\n    Line number where the declaration begins.\n\n??? \"`end_line_pos` \u00b7 int \u00b7 1-based line number where declaration ends\"\n    Line number where the declaration ends.\n\n??? \"`proof_length` \u00b7 int \u00b7 Approximate number of tactics in proof\"\n    Rough measure of proof complexity based on tactic count. Only meaningful for theorems/lemmas with tactic proofs.\n\n??? \"`tactic_counts` \u00b7 dict[str, int] \u00b7 Map of tactic names to occurrence counts\"\n    Breakdown of which tactics are used and how often. Only meaningful for theorems/lemmas with tactic proofs.\n\n??? \"`wall_ms` \u00b7 int \u00b7 Wall-clock milliseconds to elaborate the command\"\n    How long this command took to elaborate. This field reports wall-clock time, so it can vary from run to run.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`heartbeats` \u00b7 int \u00b7 Heartbeats consumed elaborating the command\"\n    Lean heartbeats consumed while elaborating this command.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`local_type_dependencies` \u00b7 list[str] \u00b7 Local dependencies of the type\"\n    Local declarations that the declaration's type depends on (non-transitive).\n\n??? \"`local_value_dependencies` \u00b7 list[str] \u00b7 Local dependencies of the body\"\n    Local declarations that the declaration's body/proof depends on (non-transitive).\n\n    Empty for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`external_type_dependencies` \u00b7 list[str] \u00b7 Immediate external dependencies of the type\"\n    External constants (builtins, imports) that appear in the type.\n\n??? \"`external_value_dependencies` \u00b7 list[str] \u00b7 Immediate external dependencies of the body\"\n    External constants (builtins, imports) that appear in the body/proof.\n\n    Empty for theorems when `elaborate_proofs=false` \u2014 do not use it in that mode.\n\n??? \"`local_syntactic_dependencies` \u00b7 list[str] \u00b7 Local constants explicitly written in source\"\n    Local constants that appear literally in source (not from notation/macro expansion).\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`external_syntactic_dependencies` \u00b7 list[str] \u00b7 External constants explicitly written in source\"\n    External constants that appear literally in source (not from notation/macro expansion).\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`declaration_messages` \u00b7 dict \u00b7 Messages specific to this declaration\"\n    Lean messages (`errors`, `warnings`, `infos`) specific to this declaration in the original document.\n\n    Incomplete for theorems when `elaborate_proofs=false` \u2014 use with caution.\n\n??? \"`theorem_messages` \u00b7 dict \u00b7 (Deprecated) Messages specific to this declaration\"\n    Lean messages (`errors`, `warnings`, `infos`) specific to this declaration. For `extract_theorems`, this contains the same data as `declaration_messages`. For `extract_decls`, this is always empty.\n\n    !!! warning \"Deprecated\"\n        This field is deprecated. Use `declaration_messages` instead for new code.\n\n## Python API\n\n```python\nresult = await axle.extract_decls(\n    content=\"import Mathlib\\ndef foo : Nat := 1\\ntheorem bar : foo = 1 := rfl\",\n    environment=\"lean-4.28.0\",\n    ignore_imports=True,  # Optional\n    timeout_seconds=120,   # Optional\n)\n\nprint(result.content)  # The processed Lean code\nfor name, doc in result.documents.items():\n    print(f\"{name}: {doc.declaration}\")\n```\n\n## CLI\n\n**Usage:** `axle extract-decls CONTENT [OPTIONS]`\n\n```bash\n# Extract to default directory\naxle extract-decls combined.lean --environment lean-4.31.0\n# Extract to custom directory\naxle extract-decls combined.lean -o my_decls/ --environment lean-4.31.0\n# Force overwrite\naxle extract-decls combined.lean -o my_decls/ -f --environment lean-4.31.0\n# Pipeline usage\ncat combined.lean | axle extract-decls - -o output/ --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/extract_decls \\\n    -d '{\"content\": \"import Mathlib\\ndef foo : Nat := 1\\ntheorem bar : foo = 1 := rfl\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"content\": \"import Mathlib\\ndef foo : Nat := 1\\ntheorem bar : foo = 1 := rfl\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"timings\": {\n    \"total_ms\": 92,\n    \"parse_ms\": 87\n  },\n  \"documents\": {\n    \"foo\": {\n      \"kind\": \"def\",\n      \"declaration\": \"def foo : Nat := 1\",\n      \"content\": \"import Mathlib\\n\\ndef foo : Nat := 1\",\n      \"tokens\": [\"def\", \"foo\", \":\", \"Nat\", \":=\", \"1\"],\n      \"signature\": \"def foo : Nat\",\n      \"type\": \"\u2115\",\n      \"type_hash\": 421340980,\n      \"type_depth\": 0,\n      \"term_depth\": 3,\n      \"is_sorry\": false,\n      \"index\": 0,\n      \"line_pos\": 2,\n      \"end_line_pos\": 2,\n      \"proof_length\": 1,\n      \"tactic_counts\": {},\n      \"wall_ms\": 1,\n      \"heartbeats\": 3,\n      \"local_value_dependencies\": [],\n      \"local_type_dependencies\": [],\n      \"external_value_dependencies\": [\"OfNat.ofNat\", \"Nat\", \"instOfNatNat\"],\n      \"external_type_dependencies\": [\"Nat\"],\n      \"local_syntactic_dependencies\": [],\n      \"external_syntactic_dependencies\": [\"Nat\"],\n      \"theorem_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []},\n      \"declaration_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []}\n    },\n    \"bar\": {\n      \"kind\": \"theorem\",\n      \"declaration\": \"theorem bar : foo = 1 := rfl\",\n      \"content\": \"import Mathlib\\n\\ndef foo : Nat := 1\\n\\ntheorem bar : foo = 1 := rfl\",\n      \"tokens\": [\"theorem\", \"bar\", \":\", \"foo\", \"=\", \"1\", \":=\", \"rfl\"],\n      \"signature\": \"theorem bar : foo = 1\",\n      \"type\": \"foo = 1\",\n      \"type_hash\": 254164366,\n      \"type_depth\": 4,\n      \"term_depth\": 4,\n      \"is_sorry\": false,\n      \"index\": 1,\n      \"line_pos\": 3,\n      \"end_line_pos\": 3,\n      \"proof_length\": 1,\n      \"tactic_counts\": {},\n      \"wall_ms\": 1,\n      \"heartbeats\": 5,\n      \"local_value_dependencies\": [\"foo\"],\n      \"local_type_dependencies\": [\"foo\"],\n      \"external_value_dependencies\": [\"rfl\", \"Nat\"],\n      \"external_type_dependencies\": [\"Eq\", \"Nat\", \"OfNat.ofNat\", \"instOfNatNat\"],\n      \"local_syntactic_dependencies\": [\"foo\"],\n      \"external_syntactic_dependencies\": [\"rfl\"],\n      \"theorem_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []},\n      \"declaration_messages\": {\"errors\": [], \"warnings\": [], \"infos\": []}\n    }\n  }\n}\n```\n"}, {"slug": "tools/extract_proof_states", "title": "extract_proof_states", "html_url": "https://axle.axiommath.ai/v1/docs/tools/extract_proof_states/", "markdown": "# extract_proof_states\n\nExtract the tactic proof state at the end of each line of the given Lean code, as shown in a Lean editor's goal panel. Lines with no tactic proof state are omitted.\n\n[Try this example in the web UI](https://axle.axiommath.ai/extract_proof_states#data=eyJjb250ZW50IjogImltcG9ydCBNYXRobGliXG50aGVvcmVtIGZvbyAobiA6IE5hdCkgOiBuICsgMCA9IG4gOj0gYnlcbiAgaW5kdWN0aW9uIG4gd2l0aFxuICB8IHplcm8gPT4gcmZsXG4gIHwgc3VjYyBrIGloID0%2BIHNpbXAiLCAiaWdub3JlX2ltcG9ydHMiOiB0cnVlLCAiZW52aXJvbm1lbnQiOiAibGVhbi00LjI4LjAiLCAidGltZW91dF9zZWNvbmRzIjogMTIwfQ%3D%3D)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`proof_states` \u00b7 list \u00b7 Per-line proof states\"\n    List of `{line, proof_state}` objects, one per line that has a tactic proof state, in file order. `line` is the 1-based line number in `content`. `proof_state` is the pretty-printed goal state at the end of that line \u2014 what an editor's goal panel shows with the cursor there.\n\n    The total size of this field is capped at 10,000,000 characters. When the cap is reached, proof states for the remaining lines are omitted, `truncated` is set to `true`, and a warning is added to `tool_messages`.\n\n??? \"`truncated` \u00b7 bool \u00b7 True if proof states were omitted due to the size cap\"\n    Returns `true` if `proof_states` hit the 10,000,000-character cap and states for the remaining lines were omitted. A warning in `tool_messages` reports the line where truncation occurred.\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from extract_proof_states tool\"\n    Messages from the extract_proof_states tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.extract_proof_states(\n    content=\"import Mathlib\\ntheorem foo (n : Nat) : n + 0 = n := by\\n  induction n with\\n  | zero => rfl\\n  | succ k ih => simp\",\n    environment=\"lean-4.28.0\",\n    ignore_imports=True,  # Optional\n    timeout_seconds=120,   # Optional\n)\n\nfor state in result.proof_states:\n    print(f\"line {state.line}:\\n{state.proof_state}\")\n```\n\n## CLI\n\n**Usage:** `axle extract-proof-states CONTENT [OPTIONS]`\n\n```bash\n# Basic usage\naxle extract-proof-states proof.lean --environment lean-4.31.0\n# Pipeline usage\ncat proof.lean | axle extract-proof-states - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/extract_proof_states \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo (n : Nat) : n + 0 = n := by\\n  induction n with\\n  | zero => rfl\\n  | succ k ih => simp\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"proof_states\": [\n    {\"line\": 2, \"proof_state\": \"n : \u2115\\n\u22a2 n + 0 = n\"},\n    {\"line\": 3, \"proof_state\": \"n : \u2115\\n\u22a2 n + 0 = n\"},\n    {\"line\": 4, \"proof_state\": \"no goals\"},\n    {\"line\": 5, \"proof_state\": \"no goals\"}\n  ],\n  \"truncated\": false,\n  \"content\": \"import Mathlib\\ntheorem foo (n : Nat) : n + 0 = n := by\\n  induction n with\\n  | zero => rfl\\n  | succ k ih => simp\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"timings\": {\n    \"total_ms\": 113,\n    \"parse_ms\": 112\n  }\n}\n```\n"}, {"slug": "tools/rename", "title": "rename", "html_url": "https://axle.axiommath.ai/v1/docs/tools/rename/", "markdown": "# rename\n\nRename declarations in Lean code.\n\n[Try this example in the web UI](https://axle.axiommath.ai/rename#data=eyJjb250ZW50IjoidGhlb3JlbSBoZWxwZXIgOiAxICsgMSA9IDIgOj0gYnkgc2ltcFxuZXhhbXBsZSA6IDIgPSAxICsgMSA6PSBoZWxwZXIuc3ltbVxuXG5uYW1lc3BhY2Ugbm1cblxudGhlb3JlbSBoZWxwZXIgOiAxICsgMSA9IDIgOj0gYnkgc2ltcFxudGhlb3JlbSB0aG0gOiAyID0gMSArIDEgOj0gaGVscGVyLnN5bW1cblxuZW5kIG5tIiwiZGVjbGFyYXRpb25zIjp7ImhlbHBlciI6Im91dHNpZGVfaGVscGVyIiwibm0uaGVscGVyIjoibm0uaW5zaWRlX2hlbHBlciIsIm5tLnRobSI6Im5tLmluc2lkZV90aGVvcmVtIn0sImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`declarations` \u00b7 dict \u00b7 required \u00b7 Map from old declaration names to new names\"\n    A dictionary mapping original declaration names to their new names (JSON format).\n    All references to renamed declarations are updated throughout the code.\n\n    CLI supports `key=val,key=val` format or `--declarations-file mapping.json`.\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from rename tool\"\n    Messages from the rename tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with renamed declarations\"\n    The Lean code with renamed declarations. The transformed code with all specified declarations renamed. References are updated throughout.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.rename(\n    content=\"import Mathlib\\ntheorem foo : 1 = 1 := rfl\\ntheorem baz : 1 = 1 := foo\",\n    declarations={\"foo\": \"bar\"},\n    environment=\"lean-4.28.0\",\n    timeout_seconds=120,  # Optional\n)\nprint(result.content)  # theorem bar : 1 = 1 := rfl\n```\n\n## CLI\n\n**Usage:** `axle rename CONTENT [OPTIONS]`\n\n```bash\n# Rename using command-line mapping\naxle rename theorem.lean --declarations foo=bar,helper=main_helper --environment lean-4.31.0\n# Rename using JSON file\naxle rename theorem.lean --declarations-file mapping.json --environment lean-4.31.0\n# Save to file\naxle rename theorem.lean --declarations foo=bar -o renamed.lean --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle rename - --declarations foo=bar --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/rename \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := rfl\\ntheorem baz : 1 = 1 := foo\", \"declarations\": {\"foo\": \"bar\"}, \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem bar : 1 = 1 := rfl\\n\\ntheorem baz : 1 = 1 := bar\",\n  \"timings\": {\n    \"total_ms\": 94,\n    \"parse_ms\": 89\n  }\n}\n```\n\n## Examples\n\n??? \"Basic rename with reference updates\"\n    Renaming `original` \u2192 `renamed` also updates all references:\n\n    **Before:**\n    ```lean\n    theorem original : 1 + 1 = 2 := by simp\n    example : 2 = 1 + 1 := original.symm\n    ```\n\n    **After:**\n    ```lean\n    theorem renamed : 1 + 1 = 2 := by simp\n    example : 2 = 1 + 1 := renamed.symm\n    ```\n\n??? \"Namespaced declarations\"\n    Use fully qualified names (`ns.original`) to rename declarations inside namespaces:\n\n    **Before:**\n    ```lean\n    namespace ns\n    theorem original : 1 + 1 = 2 := by simp\n    example : 2 = 1 + 1 := original.symm\n    end ns\n\n    example : 2 = 1 + 1 := ns.original.symm\n    ```\n\n    **After** (with `{\"ns.original\": \"ns.renamed\"}`):\n    ```lean\n    namespace ns\n    theorem renamed : 1 + 1 = 2 := by simp\n    example : 2 = 1 + 1 := renamed.symm\n    end ns\n\n    example : 2 = 1 + 1 := ns.renamed.symm\n    ```\n\n??? \"Renaming inductive types\"\n    Renaming an inductive type also updates constructor references:\n\n    **Before:**\n    ```lean\n    inductive enum\n    | caseA\n    | caseB\n\n    example : enum := enum.caseA\n    ```\n\n    **After** (with `{\"enum\": \"caseEnum\"}`):\n    ```lean\n    inductive caseEnum\n    | caseA\n    | caseB\n\n    example : caseEnum := caseEnum.caseA\n    ```\n"}, {"slug": "tools/theorem2lemma", "title": "theorem2lemma", "html_url": "https://axle.axiommath.ai/v1/docs/tools/theorem2lemma/", "markdown": "# theorem2lemma\n\nConvert between `theorem` and `lemma` declaration keywords.\n\n[Try this example in the web UI](https://axle.axiommath.ai/theorem2lemma#data=eyJjb250ZW50IjoidGhlb3JlbSBmb28gOiAxID0gMSA6PSBieSByZmxcbnRoZW9yZW0gbWFpbiA6IDIgPSAyIDo9IGJ5IHNvcnJ5IiwibmFtZXMiOlsiZm9vIl0sImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`target` \u00b7 str \u00b7 default: `lemma` \u00b7 Target keyword (lemma or theorem)\"\n    The keyword to convert to. Use `lemma` or `theorem`. Defaults to `lemma`.\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n    Note: on this tool, operations on non-theorem kinds are a no-op.\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from theorem2lemma tool\"\n    Messages from the theorem2lemma tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with updated declaration keywords\"\n    The code with `theorem` converted to `lemma` (or vice versa) for the specified declarations.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\n# Convert all theorems to lemmas\nresult = await axle.theorem2lemma(content=lean_code, environment=\"lean-4.28.0\")\n\n# Convert specific theorems by name\nresult = await axle.theorem2lemma(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"foo\", \"bar\"],\n)\n\n# Convert by index\nresult = await axle.theorem2lemma(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    indices=[0, -1],  # first and last\n)\n\n# Convert to theorem instead\nresult = await axle.theorem2lemma(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    target=\"theorem\",\n)\n```\n\n## CLI\n\n**Usage:** `axle theorem2lemma CONTENT [OPTIONS]`\n\n```bash\n# Convert all theorems to lemmas\naxle theorem2lemma theorems.lean --environment lean-4.31.0\n# Convert specific theorems by name\naxle theorem2lemma theorems.lean --names foo,bar --environment lean-4.31.0\n# Convert to theorem instead\naxle theorem2lemma lemmas.lean --target theorem --environment lean-4.31.0\n# Convert first and last theorems\naxle theorem2lemma theorems.lean --indices 0,-1 --environment lean-4.31.0\n# Pipeline usage\ncat theorems.lean | axle theorem2lemma - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\n# Convert all to lemmas\ncurl -s -X POST https://axle.axiommath.ai/api/v1/theorem2lemma \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := rfl\\ntheorem bar : 2 = 2 := rfl\", \"environment\": \"lean-4.28.0\"}' | jq\n\n# Convert specific theorems by index to theorems\ncurl -s -X POST https://axle.axiommath.ai/api/v1/theorem2lemma \\\n    -d '{\"content\": \"import Mathlib\\nlemma foo : 1 = 1 := rfl\\nlemma bar : 2 = 2 := rfl\", \"environment\": \"lean-4.28.0\", \"indices\": [0], \"target\": \"theorem\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\nlemma foo : 1 = 1 := rfl\\n\\nlemma bar : 2 = 2 := rfl\",\n  \"timings\": {\n    \"total_ms\": 106,\n    \"parse_ms\": 100\n  }\n}\n```\n"}, {"slug": "tools/theorem2sorry", "title": "theorem2sorry", "html_url": "https://axle.axiommath.ai/v1/docs/tools/theorem2sorry/", "markdown": "# theorem2sorry\n\nStrip proofs from theorems, replacing them with `sorry`.\n\n[Try this example in the web UI](https://axle.axiommath.ai/theorem2sorry#data=eyJjb250ZW50IjoidGhlb3JlbSBmb28gOiAxID0gMSA6PSBieSByZmxcbnRoZW9yZW0gbWFpbiA6IDIgPSAyIDo9IGJ5IHJmbCIsIm5hbWVzIjpbIm1haW4iXSwiaWdub3JlX2ltcG9ydHMiOnRydWUsImVudmlyb25tZW50IjoibGVhbi00LjI3LjAiLCJ0aW1lb3V0X3NlY29uZHMiOjEyMH0%3D)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from theorem2sorry tool\"\n    Messages from the theorem2sorry tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with proof bodies replaced by sorry\"\n    Useful for creating problem templates from solutions.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\n# Convert all theorems\nresult = await axle.theorem2sorry(content=lean_code, environment=\"lean-4.28.0\")\n\n# Convert specific theorems by name\nresult = await axle.theorem2sorry(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"foo\"],\n)\n\n# Convert by index (supports negative indices)\nresult = await axle.theorem2sorry(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    indices=[0, -1],  # first and last\n)\n```\n\n## CLI\n\n**Usage:** `axle theorem2sorry CONTENT [OPTIONS]`\n\n```bash\n# Convert all theorems to sorry\naxle theorem2sorry solution.lean -o problem.lean --environment lean-4.31.0\n# Convert specific theorems by name\naxle theorem2sorry solution.lean --names main_theorem,helper --environment lean-4.31.0\n# Pipeline usage\ncat solution.lean | axle theorem2sorry - --names main_theorem --environment lean-4.31.0 > problem.lean\n```\n\n## HTTP API\n\n```bash\n# Convert specific theorems by name\ncurl -s -X POST https://axle.axiommath.ai/api/v1/theorem2sorry \\\n    -d '{\"content\": \"import Mathlib\\ntheorem left_as_exercise : 1 = 1 := rfl\\ntheorem the_tricky_one : 2 = 2 := rfl\", \"environment\": \"lean-4.28.0\", \"names\": [\"left_as_exercise\"]}' | jq\n\n# Convert all theorems\ncurl -s -X POST https://axle.axiommath.ai/api/v1/theorem2sorry \\\n    -d '{\"content\": \"import Mathlib\\ntheorem left_as_exercise : 1 = 1 := rfl\\ntheorem the_tricky_one : 2 = 2 := rfl\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem left_as_exercise : 1 = 1 := sorry\\n\\ntheorem the_tricky_one : 2 = 2 := rfl\",\n  \"timings\": {\n    \"total_ms\": 97,\n    \"parse_ms\": 92\n  }\n}\n```\n"}, {"slug": "tools/merge", "title": "merge", "html_url": "https://axle.axiommath.ai/v1/docs/tools/merge/", "markdown": "# merge\n\nCombine multiple Lean files into a single file.\n\n[Try this example in the web UI](https://axle.axiommath.ai/merge#data=eyJkb2N1bWVudHMiOlsidGhlb3JlbSBEIDogKDEgPSAxIOKIpyAyID0gMikg4oinIFRydWUgOj0gc29ycnlcbnRoZW9yZW0gQiA6IDIgPSAyIDo9IHJmbFxudGhlb3JlbSBBIDogMSA9IDEgOj0gc29ycnlcbnRoZW9yZW0gQyA6IDEgPSAxIOKIpyAyID0gMiA6PSDin6hBLCBC4p%2BpIiwidGhlb3JlbSBBIDogMSA9IDEgOj0gcmZsXG50aGVvcmVtIEMgOiAxID0gMSDiiKcgMiA9IDIgOj0g4p%2BoQSwgQeKfqSAtLSBpbmNvcnJlY3RcbnRoZW9yZW0gRCA6ICgxID0gMSDiiKcgMiA9IDIpIOKIpyBUcnVlIDo9IOKfqEMsIHRyaXZpYWzin6lcbnRoZW9yZW0gQiA6IDIgPSAyIDo9IHNvcnJ5Il0sInVzZV9kZWZfZXEiOnRydWUsImluY2x1ZGVfYWx0c19hc19jb21tZW50cyI6ZmFsc2UsImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## Input Parameters\n\n??? \"`documents` \u00b7 list[str] \u00b7 required \u00b7 List of Lean code strings to merge\"\n    Multiple Lean files to combine into a single file. Duplicate declarations are merged intelligently.\n\n??? \"`use_def_eq` \u00b7 bool \u00b7 default: `True` \u00b7 Use definitional equality for deduplication\"\n    When `true`, types are compared using equality after kernel reduction.\n\n    When `false`, types are compared at face value, which is faster but may rarely fail to merge semantically identical proofs.\n\n    Defaults to true.\n\n??? \"`include_alts_as_comments` \u00b7 bool \u00b7 default: `False` \u00b7 Preserve alternate versions as comments\"\n    When deduplicating, preserves all versions of a merged declaration as comments for reference. Defaults to false.\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from merge tool\"\n    Messages from the merge tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 All input files merged into a single Lean file\"\n    Duplicates and dependencies are resolved.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.merge(\n    documents=[code1, code2, code3],\n    environment=\"lean-4.28.0\",\n    use_def_eq=True,                  # Optional\n    include_alts_as_comments=False,   # Optional\n    timeout_seconds=120,              # Optional\n)\nprint(result.content)\n```\n\n## CLI\n\n**Usage:** `axle merge FILE1 FILE2 ... [OPTIONS]`\n\n```bash\n# Merge multiple files to stdout\naxle merge theorem1.lean theorem2.lean theorem3.lean --environment lean-4.31.0\n# Merge all .lean files in directory\naxle merge *.lean -o combined.lean --environment lean-4.31.0\n# Merge and check\naxle merge *.lean --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/merge \\\n    -d '{\"documents\": [\"import Mathlib\\ntheorem foo : 1 = 1 := rfl\", \"import Mathlib\\ntheorem bar : 2 = 2 := rfl\"], \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := rfl\\n\\ntheorem bar : 2 = 2 := rfl\",\n  \"timings\": {\n    \"total_ms\": 105,\n    \"parse_ms\": 95\n  }\n}\n```\n\n## Demo\n\nThis merge function is intended to be a consolidation of multiple Lean files that performs best-effort deduplication and conflict resolution. As a demonstration, we'll merge the following two files, with descriptions of features along the way.\n\n### File 1\n```\nimport Mathlib\n\nopen Lean\n\ntheorem D : (1 = 1 \u2227 2 = 2) \u2227 True := rfl\ntheorem A : 2 = 2 := rfl\n\nvariable (x : Nat)\ntheorem E : x = 5 := trivial\n\ntheorem B : 1 = 1 := rfl\ntheorem C2 : 1 = 1 \u2227 2 = 2 := \u27e8B, A\u27e9\n\nset_option maxHeartbeats 0\n```\n\n### File 2\n```\nimport Mathlib\n\nopen Lean.Elab\nset_option maxHeartbeats 200000\n\ntheorem A : 4 = 4 := rfl\ntheorem B : 1 = 1 := rfl\ntheorem C1 : 1 = 1 \u2227 2 = 2 := \u27e8B, A\u27e9\ntheorem D : (1 = 1 \u2227 2 = 2) \u2227 True := \u27e8C1, trivial\u27e9\n\nvariable (x : Nat)\ntheorem E : x = 5 := sorry\n```\n\n### Non-declaration commands are extracted first\n\nAny non-declaration commands (variables, open scopes, options, notations, etc.) will be extracted _first_ from all files. These commands will be placed under a comment label like `----------------------`\n\nNote that this may break files, since many of these commands have global side effects that change how a proof is run, so it is a good idea to normalize your code first, whether manually or by calling [normalize](normalize.md).\n\nThis gives us:\n```\n----------------------\nopen Lean\nvariable (x : Nat)\nset_option maxHeartbeats 0\n\n----------------------\nopen Lean.Elab\nset_option maxHeartbeats 200000\nvariable (x : Nat)\n```\n\nPay attention to how we have conflicting commands here: at first, we set `maxHeartbeats` to 0, and then immediately reset it to 200000. Until we figure out a better way to handle this scenario, it is good to keep in mind.\n\n### Declarations are merged respecting dependencies\n\nAll remaining commands will be declarations, and will be merged in topological order.\n\n### Conflict resolution via renaming\n\nNotice that both files have a theorem `A`, which assert different things. The merge function will automatically rename one of them to a globally unique identifier. Note that our renaming function is fairly robust as seen in the [rename](rename.md) endpoint.\n```\ntheorem A : 2 = 2 := rfl\n\ntheorem A_1 : 4 = 4 := rfl\n```\n\n### Deduplication of identical theorems\n\nTheorem `B` exists in both files here, so we merge them into a single theorem.\n\n```\ntheorem B : 1 = 1 := rfl\n```\n\nNote that we also merge non-theorems (e.g., definitions and structures), but these must have the same *value* in addition to having the same type, because they *are* implementation-specific.\n\n### Deduplication merges theorems with different names\n\nTheorem `C` exists in both files, but with different names (`C1` vs. `C2`). Our merge function will automatically detect this equivalence and generate a unique name to use in the merged file.\n```\ntheorem C2_1 : 1 = 1 \u2227 2 = 2 := \u27e8B, A\u27e9\n```\n\n### Preference for error-free and sorry-free declarations\n\nTheorem `D` exists in both files, but in the first file, the proof `rfl` completely fails, so we'll prefer the implementation in the second file.\n\n```\ntheorem D : (1 = 1 \u2227 2 = 2) \u2227 True := \u27e8C2_1, trivial\u27e9\n```\n\nNotice something interesting here: in the first file, `D` was declared *before* `A, B, C` existed, so there couldn't possibly be a proof of `D` that uses `A, B, C`. However, our dependency tracking figures out that since we should use the implementation in the second file, we need the dependencies from that file, where `A, B, C` *are* defined.\n\n### Unsuccessful attempts are preserved as comments\n\nIf no successful proofs exist, we select one arbitrarily, but keep the others as reference. We retain the remaining unsuccessful proofs as comments following the chosen proof, with the signposting `unsuccessful attempt`.\n\n```\ntheorem E : x = 5 := trivial\n\n/-\n-- unsuccessful attempt\ntheorem E : x = 5 := sorry\n-/\n```\n\n### Final File\n```\nimport Mathlib\n\n----------------------\nopen Lean\nvariable (x : Nat)\nset_option maxHeartbeats 0\n\n----------------------\nopen Lean.Elab\nset_option maxHeartbeats 200000\nvariable (x : Nat)\n\ntheorem A : 2 = 2 := rfl\n\ntheorem B : 1 = 1 := rfl\n\ntheorem C2_1 : 1 = 1 \u2227 2 = 2 := \u27e8B, A\u27e9\n\ntheorem A_1 : 4 = 4 := rfl\n\ntheorem D : (1 = 1 \u2227 2 = 2) \u2227 True := \u27e8C2_1, trivial\u27e9\n\ntheorem E : x = 5 := trivial\n\n/-\n-- unsuccessful attempt\ntheorem E : x = 5 := sorry\n-/\n```\nNote that you may get slightly different results due to the possibility of multiple topological orderings of the declarations.\n"}, {"slug": "tools/simplify_theorems", "title": "simplify_theorems", "html_url": "https://axle.axiommath.ai/v1/docs/tools/simplify_theorems/", "markdown": "# simplify_theorems\n\nSimplify theorem proofs by removing unnecessary tactics and cleaning up code.\n\n[Try this example in the web UI](https://axle.axiommath.ai/simplify_theorems#data=eyJjb250ZW50IjoiaW1wb3J0IE1hdGhsaWJcblxudGhlb3JlbSBmb28gKGEgYiA6IE5hdCkgOlxuICAgIGEg4omkIGEgKyBiIDo9IGJ5XG4gIGhhdmUgaCA6IGEgKyAwIOKJpCBhICsgYiA6PSBieVxuICAgIGFwcGx5IE5hdC5hZGRfbGVfYWRkX2xlZnQgO1xuICAgIGV4YWN0IE5hdC56ZXJvX2xlIF9cbiAgc2ltcCIsImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`simplifications` \u00b7 list[str] \u00b7 List of simplifications to apply\"\n    If not specified, all simplifications are applied. See below for available simplifications.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from simplify_theorems tool\"\n    Messages from the simplify_theorems tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with simplified theorem proofs\"\n    May be shorter and cleaner than input.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n??? \"`simplification_stats` \u00b7 dict \u00b7 Count of each simplification type applied\"\n    Maps simplification names to counts (e.g., `{\"remove_unused_tactics\": 3}`).\n\n\n## Available Simplifications\n\n??? \"`remove_unused_tactics`\"\n    Removes tactics that don't contribute to the proof.\n\n    In `theorem foo : 1 = 1 := by rfl <;> rfl`, the second `rfl` is useless and should be removed.\n\n??? \"`remove_unused_haves`\"\n    Removes unused `have` statements.\n\n    ```lean\n    theorem foo (a b : Nat) :\n        a \u2264 a + b := by\n      have h : a + 0 \u2264 a + b := by\n        apply Nat.add_le_add_left ;\n        exact Nat.zero_le _\n      simp\n    ```\n\n    In the above theorem, `h` is useless and should be removed.\n\n??? \"`rename_unused_vars`\"\n    Cleans up unused variable names.\n\n    In `theorem triv (arg : \u2115) : True := trivial`, the variable `arg` is useless. We do *not* remove it, because that would change the signature of the theorem, but we can clean things up a bit by replacing it with an underscore, as in: `theorem triv (_ : \u2115) : True := trivial`.\n\n<!-- Not functional\n#### `simplify_have_exact`\n```\ntheorem h\u2081 : (5 : \u211d) \u2264 Real.sqrt 26 := by\n  have h : 5 \u2264 Real.sqrt 26 := by apply Real.le_sqrt_of_sq_le ; norm_num\n  exact h\n```\nIn `h\u2081`, the `have` statement, followed by `exact` is redundant. The goal can just be proved directly:\n```\ntheorem h\u2081 : (5 : \u211d) \u2264 Real.sqrt 26 := by\n  apply Real.le_sqrt_of_sq_le ; norm_num\n```\nHowever, this causes problems with indentation and formatting that cannot be easily fixed, so this has been disabled for now.\n\n\n#### `remove_unnecessary_seq_focus`\n```\ntheorem h\u2081 : (5 : \u211d) \u2264 Real.sqrt 26 := by\n  apply Real.le_sqrt_of_sq_le <;>\n  norm_num\n```\nIn `h\u2081`, the `<;>` sequence is bad style, and should be removed or replaced with `;`.\nHowever, in the following example, even though the linter generates the same warning, it is in fact unsound to replace `<;>` with `;`.\n```\ntheorem ref : 1 = 1 \u2228 False := by\n  (try left <;>\n    try rfl)\n```\n-->\n\n\n<!--\n### Unsupported Features\n\n#### `remove_unnecessary_rw_simp_arg`\nIn `theorem triv : 1 = 1 := by simp [Nat.add_assoc]`, the `Nat.add_assoc` argument is unnecessary and can be removed.\n\nHowever, the linter is not always correct, which can sometimes result in the simplification being unsound.\n\n#### `replace_unnecessary_simpa`\nIt's generally seen as bad style to use `simpa` when `simp` would suffice. This generates the linter warning \"try 'simp' instead of 'simpa'\". However, this doesn't always work, and also I don't really see the benefit in this simplification.\n\n#### `remove_redundant_have`\n```\ntheorem duh (h : 1 + 4 = 5) : 1 = 1 := by\n  have h' : 1 + 4 = 5 := h\n  have h'' : 1 + 4 = 5 \u2228 False := by left; exact h'\n  rfl\n```\nIn this theorem, `h'` is obvious -- it's the exact same as `h`, so we should remove it. However, this has not been implemented because it also requires renaming any occurrences of `h'`. This gets a little messy because we are now dealing with local variables, which are not unique (unlike global constants). Punting for now.\n\n-->\n\n## Python API\n\n```python\n# Simplify all theorems with all simplifications\nresult = await axle.simplify_theorems(content=lean_code, environment=\"lean-4.28.0\")\n\n# Simplify specific theorems\nresult = await axle.simplify_theorems(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"complex_theorem\"],\n)\n\n# Apply only specific simplifications\nresult = await axle.simplify_theorems(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    simplifications=[\"remove_unused_tactics\"],\n)\n\nprint(result.content)\nprint(result.simplification_stats)\n```\n\n## CLI\n\n**Usage:** `axle simplify-theorems CONTENT [OPTIONS]`\n\n```bash\n# Simplify all theorems\naxle simplify-theorems complex.lean --environment lean-4.31.0\n# Simplify specific theorems\naxle simplify-theorems complex.lean --names main_theorem,helper --environment lean-4.31.0\n# Apply only specific simplifications\naxle simplify-theorems complex.lean --simplifications remove_unused_tactics --environment lean-4.31.0\n# Pipeline usage\ncat complex.lean | axle simplify-theorems - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/simplify_theorems \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := by rfl <;> rfl\", \"environment\": \"lean-4.28.0\", \"names\": [\"foo\"]}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": [\"simplify_theorems completed in 1 iterations\"]\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := by rfl\",\n  \"timings\": {\n    \"total_ms\": 97,\n    \"parse_ms\": 92\n  },\n  \"simplification_stats\": {\n    \"remove_unused_tactics\": 1,\n    \"rename_unused_vars\": 0,\n    \"remove_unused_haves\": 0\n  }\n}\n```\n"}, {"slug": "tools/repair_proofs", "title": "repair_proofs", "html_url": "https://axle.axiommath.ai/v1/docs/tools/repair_proofs/", "markdown": "# repair_proofs\n\nAttempt to repair broken theorem proofs. Available repairs:\n\n- `remove_extraneous_tactics` \u2014 truncate trailing tactics after the proof closes\n- `apply_terminal_tactics` \u2014 try terminal tactics in place of `sorry`\n- `replace_unsafe_tactics` \u2014 replace `native_decide` with `decide +kernel`\n- `remove_unknown_options` \u2014 strip `set_option` commands referencing an unknown option\n- `enable_autoImplicit` \u2014 set `autoImplicit true` when a command needs auto-implicit binders\n- `relax_defeq_transparency` \u2014 set `backward.isDefEq.respectTransparency false` when a command fails due to improper reducibility/transparency settings for implicit arguments (Lean \u2265 4.29 only)\n\nIf `repairs` is omitted, all of the above run. Pass an explicit list to limit which apply. See \"Available Repairs\" below for details on each pass.\n\n**Note on malformed commands:** Lean's parser silently discards source it cannot parse as a command (e.g., a stray `#fake_command`). Such text is dropped during the initial parse and never reaches `repair_proofs`. The reprinted output will not contain it. This is a property of Lean's parser, not `repair_proofs`.\n\n[Try this example in the web UI](https://axle.axiommath.ai/repair_proofs#data=eyJjb250ZW50IjoiaW1wb3J0IE1hdGhsaWJcblxudGhlb3JlbSBwYXJhbGxlbF9nb2Fsc19leHRyYW5lb3VzXG4gICh5IDog4oSCKSAoeCA6IOKEnSkgKGggOiB4IOKJpSAyKSA6XG4gIDcgKiAoMyAqIHkgKyAyKSA9IDIxICogeSArIDE0XG4gIOKIpyB4XjIg4omlIDFcbiAgOj0gYnlcbiAgY29uc3RydWN0b3JcbiAgYWxsX2dvYWxzIHNvcnJ5XG4gIGdyaW5kXG4gIHJmbFxuICBzb3JyeSIsImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n??? \"Known Limitations\"\n    - The repair tool does not guarantee that repaired proofs will be semantically correct or complete\n    - Some repairs may introduce new errors or conflicts\n    - Complex proofs with multiple goals may require manual intervention\n    - The tool works best on simple, localized proof issues\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`repairs` \u00b7 list[str] \u00b7 default: `['remove_unknown_options', 'enable_autoImplicit', 'relax_defeq_transparency', 'remove_extraneous_tactics', 'apply_terminal_tactics', 'replace_unsafe_tactics']` \u00b7 List of repairs to apply\"\n    If not specified, all repairs are applied. See below for available repairs.\n\n??? \"`terminal_tactics` \u00b7 list[str] \u00b7 default: `['grind']` \u00b7 Tactics to try for closing goals\"\n    Used when `apply_terminal_tactics` repair is applied. Tactics tried in order; stops on first success. Defaults to `grind`.\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from repair_proofs tool\"\n    Messages from the repair_proofs tool with `errors`, `warnings`, and `infos` lists.\n\n    Errors here are failed repairs: a repair was detected as necessary, but no successful change could fix it.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with repair attempts applied\"\n    Check `okay` to see if repairs succeeded and the repaired code compiles.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n??? \"`repair_stats` \u00b7 dict \u00b7 Count of each repair type applied\"\n    Maps repair names to counts (e.g., `{\"apply_terminal_tactics\": 2}`).\n\n??? \"`okay` \u00b7 bool \u00b7 True if all repairs succeed and the repaired code compiles\"\n    `True` when all repairs succeed and the repaired code compiles; `False` otherwise. A failed repair is when a repair is detected as necessary but no successful change could fix it \u2014 e.g. a `sorry` that no terminal tactic could prove, or a `native_decide` that can't be safely replaced. Failed repairs are reported in `tool_messages.errors`.\n\n\n## Available Repairs\n\n??? \"`remove_unknown_options`\"\n    Strips `set_option` references with an option name Lean doesn't recognize. Bare `set_option` commands are dropped entirely; `set_option ... in <inner>` gets unwrapped to just `<inner>` so the inner declaration / tactic / term is preserved.\n\n    **Bare command \u2014 dropped:**\n    ```lean\n    import Mathlib\n\n    set_option fake_option true\n\n    theorem foo : 1 = 1 := by rfl\n    ```\n    becomes\n    ```lean\n    import Mathlib\n\n    theorem foo : 1 = 1 := by rfl\n    ```\n\n    **`set_option ... in <decl>` \u2014 unwrapped:**\n    ```lean\n    import Mathlib\n\n    set_option fake_option true in\n    theorem foo : 1 = 1 := by rfl\n    ```\n    becomes\n    ```lean\n    import Mathlib\n\n    theorem foo : 1 = 1 := by rfl\n    ```\n\n??? \"`enable_autoImplicit`\"\n    When a command fails because it relies on auto-implicit binders but `autoImplicit` is disabled in the current scope, this repair prepends `set_option autoImplicit true in` to the command so it elaborates. Note that `autoImplicit` is already on by default, so this only affects code that explicitly turns it off.\n\n??? \"`relax_defeq_transparency`\"\n    Lean 4.29's `backward.isDefEq.respectTransparency` (default `true`) keeps `isDefEq` from unfolding reducible/instance definitions when unifying implicit arguments, breaking proofs that relied on it. Mathlib turns it off per-theorem. Similarly, this repair prepends `set_option backward.isDefEq.respectTransparency false in` when the fix gets the proof further (all errors resolved, or the first error appears later in the source). On environments without the option, the repair is a no-op.\n\n    **Before:**\n    ```lean\n    import Mathlib\n\n    open Finset in\n    theorem pnat_card_Icc (a b : \u2115+) : #(Icc a b) = b + 1 - a := by\n      rw [\u2190 Nat.card_Icc, \u2190 PNat.map_subtype_embedding_Icc, card_map]\n    ```\n\n    **After (Lean \u2265 4.29):**\n    ```lean\n    import Mathlib\n\n    set_option backward.isDefEq.respectTransparency false in\n    open Finset in\n    theorem pnat_card_Icc (a b : \u2115+) : #(Icc a b) = b + 1 - a := by\n      rw [\u2190 Nat.card_Icc, \u2190 PNat.map_subtype_embedding_Icc, card_map]\n    ```\n\n??? \"`remove_extraneous_tactics`\"\n    When a proof is already complete but has extra tactics afterward, this repair removes the extraneous tactics.\n\n    **Before:**\n    ```lean\n    theorem extra_tactics : 1 = 1 := by\n      rfl\n      simp  -- This tactic is never reached\n      omega\n    ```\n\n    **After:**\n    ```lean\n    theorem extra_tactics : 1 = 1 := by\n      rfl\n    ```\n\n??? \"`apply_terminal_tactics`\"\n    Tries terminal tactics in place of sorries.\n\n    In `theorem foo : 1 = 1 := by sorry`, the proof is incomplete. This repair attempts to apply terminal tactics to complete the proof. The tactics to try can be customized via the `terminal_tactics` parameter (default: `[\"grind\"]`).\n\n    **Before:**\n    ```lean\n    theorem simple_eq : 1 + 1 = 2 := by\n      sorry\n    ```\n\n    **After:**\n    ```lean\n    theorem simple_eq : 1 + 1 = 2 := by\n      grind\n    ```\n\n\n\n??? \"`replace_unsafe_tactics`\"\n    Replaces unsafe tactics with safer alternatives.\n\n    Some tactics like `native_decide` use native code execution which can be unsafe. This repair replaces them with safer alternatives.\n\n    **Before:**\n    ```lean\n    theorem check_prime : Nat.Prime 7 := by\n      native_decide\n    ```\n\n    **After:**\n    ```lean\n    theorem check_prime : Nat.Prime 7 := by\n      decide +kernel\n    ```\n\n## Python API\n\n```python\n# Repair all theorems with all repairs\nresult = await axle.repair_proofs(content=broken_code, environment=\"lean-4.28.0\")\n\n# Repair specific theorems\nresult = await axle.repair_proofs(\n    content=broken_code,\n    environment=\"lean-4.28.0\",\n    names=[\"broken_theorem\"],\n)\n\n# Apply only specific repairs\nresult = await axle.repair_proofs(\n    content=broken_code,\n    environment=\"lean-4.28.0\",\n    repairs=[\"remove_extraneous_tactics\"],\n)\n\n# Use custom terminal tactics\nresult = await axle.repair_proofs(\n    content=broken_code,\n    environment=\"lean-4.28.0\",\n    repairs=[\"apply_terminal_tactics\"],\n    terminal_tactics=[\"aesop\", \"simp\", \"rfl\"],\n)\n\nprint(result.content)\nprint(result.repair_stats)\n```\n\n## CLI\n\n**Usage:** `axle repair-proofs CONTENT [OPTIONS]`\n\n```bash\n# Repair all theorems\naxle repair-proofs broken.lean --environment lean-4.31.0\n# Repair specific theorems\naxle repair-proofs broken.lean --names main_theorem,helper --environment lean-4.31.0\n# Apply only specific repairs\naxle repair-proofs broken.lean --repairs remove_extraneous_tactics --environment lean-4.31.0\n# Pipeline usage\ncat broken.lean | axle repair-proofs - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/repair_proofs \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 := by\\n  rfl\\n  simp\\n  omega\", \"environment\": \"lean-4.28.0\", \"names\": [\"foo\"]}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := by\\n  rfl\",\n  \"timings\": {\n    \"total_ms\": 102,\n    \"parse_ms\": 95\n  },\n  \"repair_stats\": {\n    \"remove_unknown_options\": 0,\n    \"enable_autoImplicit\": 0,\n    \"remove_extraneous_tactics\": 2,\n    \"apply_terminal_tactics\": 0,\n    \"replace_unsafe_tactics\": 0\n  },\n  \"okay\": true\n}\n```\n"}, {"slug": "tools/have2lemma", "title": "have2lemma", "html_url": "https://axle.axiommath.ai/v1/docs/tools/have2lemma/", "markdown": "# have2lemma\n\nExtract `have` statements from proofs and convert them into standalone lemmas.\n\n[Try this example in the web UI](https://axle.axiommath.ai/have2lemma#data=eyJjb250ZW50IjoidGhlb3JlbSBvdXRlciA6IDEgPSAxIDo9IGJ5XG4gIGhhdmUgaW5uZXIgOiAyID0gMiA6PSBieVxuICAgIGhhdmUgbmVzdGVkIDogMyA9IDMgOj0gYnkgcmZsXG4gICAgcmZsXG4gIHJmbCIsImluY2x1ZGVfaGF2ZV9ib2R5Ijp0cnVlLCJpbmNsdWRlX3dob2xlX2NvbnRleHQiOnRydWUsInJlY29uc3RydWN0X2NhbGxzaXRlIjp0cnVlLCJ2ZXJib3NpdHkiOjAsImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## See Also\n\nThis tool is partially powered by [`extract_goal`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Tactic/ExtractGoal.html), a Mathlib tactic for extracting goals into standalone declarations.\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`include_have_body` \u00b7 bool \u00b7 default: `False` \u00b7 Include proof bodies in extracted lemmas\"\n    If `true`, extracted lemmas include the original proof. If `false`, they use `sorry` as placeholder. Defaults to false.\n\n??? \"`include_whole_context` \u00b7 bool \u00b7 default: `True` \u00b7 Include whole context when extracting\"\n    If `true`, lemmas include all context variables. If `false`, attempts to minimize the context. Defaults to true.\n\n??? \"`reconstruct_callsite` \u00b7 bool \u00b7 default: `False` \u00b7 Replace have statement with lemma call\"\n    If `true`, the original `have` is replaced with a call to the extracted lemma. Defaults to false.\n\n??? \"`verbosity` \u00b7 float \u00b7 default: `0` \u00b7 Pretty-printer verbosity level (0-2)\"\n    Preset buckets of pretty-printer options, addressing the ambiguity problem described under `delab_options`:\n\n    - `verbosity=0` (default): Standard pretty-printing options\n    - `verbosity=1`: Robust options with additional explicitness\n    - `verbosity=2`: Extra robust options with maximum explicitness (e.g. `pp.explicit=true`)\n\n    **Rule of thumb:** If you encounter type inference errors in the output\u2014especially involving coercions, casts, or polymorphic functions\u2014try increasing the verbosity level. Do note that at `verbosity=2`, type signatures may become incredibly complex and unreadable, so it should be used sparingly.\n\n    For finer-grained control over individual pretty-printer options, see `delab_options`, whose overrides apply on top of this preset.\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from have2lemma tool\"\n    Messages from the have2lemma tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with have statements extracted as lemmas\"\n    The code with `have` statements lifted to top-level lemmas. Original theorems may reference these new lemmas.\n\n??? \"`lemma_names` \u00b7 list \u00b7 Names of newly created lemmas\"\n    Names are auto-generated based on the parent theorem.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.have2lemma(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"main_theorem\"],         # Optional\n    include_have_body=False,        # Optional: use sorry instead\n    include_whole_context=True,     # Optional\n    reconstruct_callsite=False,     # Optional\n    verbosity=0,                    # Optional: 0-2\n)\nprint(result.content)\nprint(result.lemma_names)  # [\"main_theorem.h1\", \"main_theorem.h2\"]\n```\n\n## CLI\n\n**Usage:** `axle have2lemma CONTENT [OPTIONS]`\n\n```bash\n# Extract all have statements\naxle have2lemma theorem.lean --environment lean-4.31.0\n# Extract from specific theorems\naxle have2lemma theorem.lean --names main_proof,helper --environment lean-4.31.0\n# Include proof bodies in extracted lemmas\naxle have2lemma theorem.lean --include-have-body --environment lean-4.31.0\n# Reconstruct callsites (replace have with lemma call)\naxle have2lemma theorem.lean --reconstruct-callsite --environment lean-4.31.0\n# Skip context cleanup\naxle have2lemma theorem.lean --no-include-whole-context --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle have2lemma - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/have2lemma \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 \u2227 2 = 2 := by\\n  have h1 : 1 = 1 := by rfl\\n  have h2 : 2 = 2 := by rfl\\n  exact \u27e8h1, h2\u27e9\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\nlemma foo.h1 : 1 = 1 := sorry\\n\\nlemma foo.h2 (h1 : 1 = 1) : 2 = 2 := sorry\\n\\ntheorem foo : 1 = 1 \u2227 2 = 2 := by\\n  have h1 : 1 = 1 := by rfl\\n  have h2 : 2 = 2 := by rfl\\n  exact \u27e8h1, h2\u27e9\",\n  \"lemma_names\": [\"foo.h1\", \"foo.h2\"],\n  \"timings\": {\n    \"total_ms\": 95,\n    \"parse_ms\": 88\n  }\n}\n```\n\n## Demo\n\nThere are a lot of configurable options for `have2lemma`. Let's go through them and discuss why they exist.\n\n### Options\n\nThere are three main options to discuss:\n\n- `include_have_body`: Whether to include have bodies in extracted lemmas. If false, lemmas will use `sorry` instead. Defaults to false.\n- `include_whole_context`: Whether to include the whole context (skip cleanup) when extracting have statements. Defaults to true.\n- `reconstruct_callsite`: Whether to reconstruct the callsite (replace have statement with lemma call). Defaults to false.\n\n#### Default behavior\n\nLet's look at the simple following example.\n```\ntheorem example_theorem (p q r : Prop) : p \u2227 r \u2192 p \u2228 q := by\n  intro hpr\n  have h1 : p := by simp_all\n  have h2 : r := by simp_all\n  left\n  assumption\n```\nThe default behavior sets `include_have_body=false`, `include_whole_context=true`, and `reconstruct_callsite=false`, giving us\n```\nlemma example_theorem.h1 (p q r : Prop) (hpr : p \u2227 r) : p := sorry\n\nlemma example_theorem.h2 (p q r : Prop) (hpr : p \u2227 r) (h1 : p) : r := sorry\n\ntheorem example_theorem (p q r : Prop) : p \u2227 r \u2192 p \u2228 q := by\n  intro hpr\n  have h1 : p := by simp_all\n  have h2 : r := by simp_all\n  left\n  assumption\n```\nHere,\n\n- both generated lemmas are sorried out -- this is the result of `include_have_body=false`.\n- in both lemmas, the entire local context is provided, which is the result of `include_whole_context=true`. This might include redundant variables -- in this case, `q` isn't relevant to the goal.\n- the main theorem is left unchanged -- this is the result of `reconstruct_callsite=false`.\n\n#### `include_have_body`\n\nLet's see what happens if we set this value to true:\n```\nlemma example_theorem.h1 (p q r : Prop) (hpr : p \u2227 r) : p := by simp_all\n\nlemma example_theorem.h2 (p q r : Prop) (hpr : p \u2227 r) (h1 : p) : r := by simp_all\n```\nThe output now includes the proof body!\n\n**Why bother making this configurable?**\n\nThis option is NOT guaranteed to be robust, and might introduce errors into the file. In this example:\n```\ntheorem complex_types : \u2200 (n : Nat), n + 0 = n := by\n  intro n\n  have base : 0 + 0 = 0 := by rfl\n  have step : \u2200 m, m + 0 = m \u2192 (m + 1) + 0 = m + 1 := by\n    intro m ih\n    rfl\n  sorry\n```\nthe second generated lemma is\n```\nlemma complex_types.step : \u2200 (n : \u2115), 0 + 0 = 0 \u2192 \u2200 (m : \u2115), m + 0 = m \u2192 m + 1 + 0 = m + 1 := by\n    intro m ih\n    rfl\n```\n**This does not compile!!!** Notice that Lean has decided to revert `n` in the type. This means the proof will fail, because there is a missing `intro n ...`.\n\n#### `include_whole_context`\n\nNow let's set this option to false. In our original example, this gives us:\n```\nlemma example_theorem.h1 (p r : Prop) (hpr : p \u2227 r) : p := sorry\nlemma example_theorem.h2 (p r : Prop) (hpr : p \u2227 r) (h1 : p) : r := sorry\n```\n\nNotice that the tool has now removed the `q` variable from both lemmas, as it is irrelevant to the goal and hypotheses.\n\n**Why make this configurable?**\n\nIn general, Lean's dependency analysis is purely based on heuristics. See the source:\n\n> A variable is *relevant* if (1) it occurs in the target type, (2) there is a relevant variable that depends on it, or (3) the type of the variable is a proposition that depends on a relevant variable.\n\nTherefore, it's possible that a hypothesis in the context is useful even though Lean judges it to be irrelevant. In rare cases, it can break the proof when used in conjunction with `include_have_body=true`. For example:\n```\ntheorem foo : Odd 5 \u2228 Even 5 := by\n  have odd : Odd 5 := by exists 2\n  have sol : Odd 5 \u2228 Even 5 := by\n    left\n    assumption\n  exact sol\n```\nWhen running with `include_have_body=true` and `include_whole_context=false`, the tool will output the lemmas\n```\nlemma foo.odd : Odd 5 := by exists 2\n\nlemma foo.sol : Odd 5 \u2228 Even 5 := by\n    left\n    assumption\n```\nNotably, in the second lemma, Lean judged the hypothesis `odd` as irrelevant -- no good! The proof body now breaks on `assumption`.\n\n#### `reconstruct_callsite`\n\nOur final option is the most intricate. Let's try enabling this option:\n```\n...\n\ntheorem example_theorem (p q r : Prop) : p \u2227 r \u2192 p \u2228 q := by\n  intro hpr\n  have h1 : p := example_theorem.h1 p q r hpr\n  have h2 : r := example_theorem.h2 p q r hpr h1\n  left\n  assumption\n```\nHere, in the main theorem, we removed the body of the have statement, replacing it with an application of the lemmas we just generated!\n\n**Why make this configurable?**\n\nLet's make a very small change to our original proof. Instead of running `intro hpr`, we'll have Lean generate the name for us, and just run `intros`.\n```\ntheorem example_theorem (p q r : Prop) : p \u2227 r \u2192 p \u2228 q := by\n  intros\n  have h1 : p := by simp_all\n  ...\n```\nNow we'll run `have2lemma` again.\n```\nlemma example_theorem.h1 (p q r : Prop) (a : p \u2227 r) : p := sorry\n\nlemma example_theorem.h2 (p q r : Prop) (a : p \u2227 r) (h1 : p) : r := sorry\n\ntheorem example_theorem (p q r : Prop) : p \u2227 r \u2192 p \u2228 q := by\n  intros\n  have h1 : p := sorry /- try using example_theorem.h1 here -/\n  have h2 : r := sorry /- try using example_theorem.h2 here -/\n  left\n  assumption\n```\nUh oh. What happened? Notice that when we run `intros`, we introduce a new hypothesis with type `p \u2227 r` -- but we haven't given it a name! This means we can't ever refer to it explicitly (i.e., it is *inaccessible*). (This is a Lean quirk which can be disabled, but hygienic names are generally a good thing.) `have2lemma` automatically generated the name `a` in the lemmas, but we can't assign anything to it -- so our tool complains that we've encountered an inaccessible variable, and gives up.\n\n### Summary\n\nThese configuration options provide some flexibility around usage, at the cost of correctness in some cases. Try to keep this in mind when generating bug reports -- some of these errors aren't fixable without significant effort.\n"}, {"slug": "tools/have2sorry", "title": "have2sorry", "html_url": "https://axle.axiommath.ai/v1/docs/tools/have2sorry/", "markdown": "# have2sorry\n\nReplace `have` statements in proofs with `sorry`. Useful for creating problem templates from solutions while keeping the overall proof structure intact.\n\n[Try this example in the web UI](https://axle.axiommath.ai/have2sorry#data=eyJjb250ZW50IjoidGhlb3JlbSBmb28gOiBUcnVlIDo9IGJ5XG4gIGhhdmUgOiAxID0gMiA6PSByZmxcbiAgdHJpdmlhbCIsImlnbm9yZV9pbXBvcnRzIjp0cnVlLCJlbnZpcm9ubWVudCI6ImxlYW4tNC4yNy4wIiwidGltZW91dF9zZWNvbmRzIjoxMjB9)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from have2sorry tool\"\n    Messages from the have2sorry tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with have proof bodies replaced by sorry\"\n    The `have` structure is preserved.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.have2sorry(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"main_theorem\"],  # Optional\n)\nprint(result.content)\n```\n\n## CLI\n\n**Usage:** `axle have2sorry CONTENT [OPTIONS]`\n\n```bash\n# Replace all have statements\naxle have2sorry theorem.lean --environment lean-4.31.0\n# Replace from specific theorems\naxle have2sorry theorem.lean --names main_proof,helper --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle have2sorry - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/have2sorry \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo : 1 = 1 \u2227 2 = 2 := by\\n  have h1 : 1 = 1 := by rfl\\n  have h2 : 2 = 2 := by rfl\\n  exact \u27e8h1, h2\u27e9\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 \u2227 2 = 2 := by\\n  have h1 : 1 = 1 := sorry\\n  have h2 : 2 = 2 := sorry\\n  exact \u27e8h1, h2\u27e9\",\n  \"timings\": {\n    \"total_ms\": 95,\n    \"parse_ms\": 88\n  }\n}\n```\n"}, {"slug": "tools/sorry2lemma", "title": "sorry2lemma", "html_url": "https://axle.axiommath.ai/v1/docs/tools/sorry2lemma/", "markdown": "# sorry2lemma\n\nExtract `sorry` placeholders and unsolved goals at error locations from Lean code and lift them into standalone top-level lemmas.\n\n[Try this example in the web UI](https://axle.axiommath.ai/sorry2lemma#data=eyJjb250ZW50IjoidGhlb3JlbSBtdWx0aXBsZSAobiA6IE5hdCkgOiAxID0gMSDiiKcgMiA9IDIgOj0gYnkgY29uc3RydWN0b3IgPDs%2BIHNvcnJ5IiwiZXh0cmFjdF9zb3JyaWVzIjp0cnVlLCJleHRyYWN0X2Vycm9ycyI6dHJ1ZSwiaW5jbHVkZV93aG9sZV9jb250ZXh0Ijp0cnVlLCJyZWNvbnN0cnVjdF9jYWxsc2l0ZSI6dHJ1ZSwidmVyYm9zaXR5IjowLCJpZ25vcmVfaW1wb3J0cyI6dHJ1ZSwiZW52aXJvbm1lbnQiOiJsZWFuLTQuMjcuMCIsInRpbWVvdXRfc2Vjb25kcyI6MTIwfQ%3D%3D)\n\n## See Also\n\nThis tool is partially powered by [`extract_goal`](https://leanprover-community.github.io/mathlib4_docs/Mathlib/Tactic/ExtractGoal.html), a Mathlib tactic for extracting goals into standalone declarations.\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`extract_sorries` \u00b7 bool \u00b7 default: `True` \u00b7 Lift sorries into standalone lemmas\"\n    If `true`, `sorry` placeholders are extracted into standalone lemmas. Defaults to true.\n\n??? \"`extract_errors` \u00b7 bool \u00b7 default: `True` \u00b7 Lift errors into standalone lemmas\"\n    If `true`, error positions (type mismatches, etc.) are extracted into standalone lemmas. Defaults to true.\n\n??? \"`include_whole_context` \u00b7 bool \u00b7 default: `True` \u00b7 Include whole context when extracting\"\n    If `true`, lemmas include all context variables. If `false`, attempts to minimize the context. Defaults to true.\n\n??? \"`reconstruct_callsite` \u00b7 bool \u00b7 default: `False` \u00b7 Replace sorry with lemma call\"\n    If `true`, the original `sorry` is replaced with a call to the extracted lemma. Defaults to false.\n\n??? \"`merge_duplicates` \u00b7 bool \u00b7 default: `False` \u00b7 Merge duplicate extracted lemmas (by definitional equality)\"\n    If `true`, extracted lemmas within the same parent that are definitionally equal \u2014 to each other, or to the `theorem`/`lemma` they were extracted from \u2014 are merged: duplicates collapse into a single lemma that all callsites reference, and a sorry whose goal is definitionally equal to its parent theorem/lemma is dropped rather than lifted into a restatement (e.g. a top-level `:= sorry` / `:= by sorry`). The parent-restatement check applies only to `theorem`/`lemma` parents, not `def`/`instance`/etc. Defaults to false.\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n??? \"`verbosity` \u00b7 float \u00b7 default: `0` \u00b7 Pretty-printer verbosity level (0-2)\"\n    Preset buckets of pretty-printer options, addressing the ambiguity problem described under `delab_options`:\n\n    - `verbosity=0` (default): Standard pretty-printing options\n    - `verbosity=1`: Robust options with additional explicitness\n    - `verbosity=2`: Extra robust options with maximum explicitness (e.g. `pp.explicit=true`)\n\n    **Rule of thumb:** If you encounter type inference errors in the output\u2014especially involving coercions, casts, or polymorphic functions\u2014try increasing the verbosity level. Do note that at `verbosity=2`, type signatures may become incredibly complex and unreadable, so it should be used sparingly.\n\n    For finer-grained control over individual pretty-printer options, see `delab_options`, whose overrides apply on top of this preset.\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`reparse` \u00b7 bool \u00b7 default: `True` \u00b7 Re-elaborate the transformed output\"\n    If `true` (default), the transformed content is re-elaborated. If `false`, re-elaboration is skipped. The resulting `lean_messages` is then returned empty.\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n    If the request sets `reparse=false`, the transformed output is not re-elaborated and this field is returned empty.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from sorry2lemma tool\"\n    Messages from the sorry2lemma tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 Lean code with sorries/errors extracted as lemmas\"\n    The code with `sorry` and error positions lifted to top-level lemmas with their goals as types.\n\n??? \"`lemma_names` \u00b7 list \u00b7 Names of newly created lemmas\"\n    Names are auto-generated based on the parent theorem and position.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.sorry2lemma(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"main_theorem\"],         # Optional\n    extract_sorries=True,           # Optional\n    extract_errors=True,            # Optional\n    include_whole_context=True,     # Optional\n    reconstruct_callsite=False,     # Optional\n    merge_duplicates=False,         # Optional\n    theorems_only=True,             # Optional\n    verbosity=0,                    # Optional: 0-2\n)\nprint(result.content)\nprint(result.lemma_names)  # [\"main_theorem.sorried\", \"main_theorem.unsolved\"]\n```\n\n## CLI\n\n**Usage:** `axle sorry2lemma CONTENT [OPTIONS]`\n\n```bash\n# Extract all sorries and errors\naxle sorry2lemma theorem.lean --environment lean-4.31.0\n# Extract from specific theorems\naxle sorry2lemma theorem.lean --names main_proof,helper --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle sorry2lemma - --environment lean-4.31.0 | axle check - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/sorry2lemma \\\n    -d '{\"content\": \"import Mathlib\\ntheorem foo (p q : Prop) : p \u2192 q := by\\n  intro hp\\n  sorry\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [\"-:3:6-3:11: warning: declaration uses 'sorry'\\n\", \"-:5:8-5:13: warning: declaration uses 'sorry'\\n\"],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\nlemma foo.sorried (p q : Prop) (hp : p) : q := sorry\\n\\ntheorem foo (p q : Prop) : p \u2192 q := by\\n  intro hp\\n  sorry\",\n  \"lemma_names\": [\"foo.sorried\"],\n  \"timings\": {\n    \"total_ms\": 95,\n    \"parse_ms\": 88\n  }\n}\n```\n\n## Demo\n\nThe `sorry2lemma` tool extracts `sorry` placeholders and unsolved goals at error locations into standalone lemmas. This is useful for breaking down incomplete proofs into subgoals that can be tackled independently.\n\n### `extract_sorries` and `extract_errors`\n\nYou can control which types of goals are extracted:\n\n```python\n# Only extract sorries\nresult = await axle.sorry2lemma(content, environment=\"lean-4.28.0\", extract_errors=False)\n\n# Only extract errors\nresult = await axle.sorry2lemma(content, environment=\"lean-4.28.0\", extract_sorries=False)\n\n# Extract neither (effectively a no-op)\nresult = await axle.sorry2lemma(content, environment=\"lean-4.28.0\", extract_sorries=False, extract_errors=False)\n```\n\n### `include_whole_context`, `reconstruct_callsite`\nRefer to the [have2lemma documentation](have2lemma.md#demo) for a detailed description and examples of these fields. `sorry2lemma` handles them in mostly the same way.\n\n**Multiple goals:** When a single sorry applies to multiple goals (e.g., after `<;>`), the tool generates multiple lemmas and combines them with `first`:\n```lean\n-- Input\ntheorem multiple (n : Nat) : 1 = 1 \u2227 2 = 2 := by constructor <;> sorry\n\n-- Output with reconstruct_callsite=true\ntheorem multiple (n : Nat) : 1 = 1 \u2227 2 = 2 := by constructor <;> (first | exact multiple.sorried n | exact multiple.sorried_1 n)\n```\n"}, {"slug": "tools/disprove", "title": "disprove", "html_url": "https://axle.axiommath.ai/v1/docs/tools/disprove/", "markdown": "# disprove\n\nAttempt to disprove theorems by proving the negation.\n\n[Try this example in the web UI](https://axle.axiommath.ai/disprove#data=eyJjb250ZW50IjoidGhlb3JlbSBmaXJzdCA6IOKIgCBuIDog4oSVLCBuIDwgMTBeMTAwIDo9IHNvcnJ5XG50aGVvcmVtIHNlY29uZCA6IDIgPSAxIDo9IGJ5IHNvcnJ5IiwidGVybWluYWxfdGFjdGljcyI6WyJhZXNvcCJdLCJpZ25vcmVfaW1wb3J0cyI6dHJ1ZSwiZW52aXJvbm1lbnQiOiJsZWFuLTQuMjcuMCIsInRpbWVvdXRfc2Vjb25kcyI6MTIwfQ%3D%3D)\n\n## See Also\n\nThis tool is partially powered by [Plausible](https://github.com/leanprover-community/plausible), a Lean 4 library for property-based testing and counterexample generation.\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`names` \u00b7 list[str] \u00b7 Theorem names to process\"\n    Optional list of theorem names to process. If not specified, all theorems are processed.\n    Requesting a name not found in the code returns an error.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`indices` \u00b7 list[int] \u00b7 Theorem indices to process\"\n    Optional list of theorem indices to process (0-based). Supports negative indices:\n    `-1` is the last theorem, `-2` is second-to-last, etc.\n    If not specified, all theorems are processed.\n    When `theorems_only` is `false`, these select over all declarations (not just theorems).\n\n??? \"`terminal_tactics` \u00b7 list[str] \u00b7 default: `['grind']` \u00b7 Tactics to try when attempting to disprove\"\n    Tactics tried in order to prove the negation. `grind` often works for false statements. Defaults to 'grind'.\n\n??? \"`theorems_only` \u00b7 bool \u00b7 default: `True` \u00b7 Process theorems/lemmas only\"\n    If `true` (default), only `theorem`/`lemma` declarations are processed. Set to `false` to process all declaration kinds (`def`/`instance`/`abbrev`/`opaque`/etc). When `false`, `names` and `indices` select over all declarations rather than just theorems.\n\n    Note: on this tool, operations on non-theorem kinds are a no-op.\n\n??? \"`verbosity` \u00b7 float \u00b7 default: `0` \u00b7 Pretty-printer verbosity level (0-2)\"\n    Preset buckets of pretty-printer options, addressing the ambiguity problem described under `delab_options`:\n\n    - `verbosity=0` (default): Standard pretty-printing options\n    - `verbosity=1`: Robust options with additional explicitness\n    - `verbosity=2`: Extra robust options with maximum explicitness (e.g. `pp.explicit=true`)\n\n    **Rule of thumb:** If you encounter type inference errors in the output\u2014especially involving coercions, casts, or polymorphic functions\u2014try increasing the verbosity level. Do note that at `verbosity=2`, type signatures may become incredibly complex and unreadable, so it should be used sparingly.\n\n    For finer-grained control over individual pretty-printer options, see `delab_options`, whose overrides apply on top of this preset.\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`content` \u00b7 string \u00b7 Processed Lean code\"\n    The Lean code that was actually processed. May differ from input if `ignore_imports=true` caused header injection.\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from disprove tool\"\n    Messages from the disprove tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`results` \u00b7 dict \u00b7 Map from theorem name to disprove result\"\n    Each theorem maps to a string indicating the outcome of the disprove attempt.\n\n??? \"`negated` \u00b7 dict \u00b7 Map from theorem name to negated goal\"\n    Each theorem maps to the negated goal type that was attempted (the statement whose proof would disprove the theorem).\n\n??? \"`disproved_theorems` \u00b7 list \u00b7 List of theorems that were disproved\"\n    List of theorems that were disproved\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n\n## Python API\n\n```python\nresult = await axle.disprove(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    names=[\"conjecture1\", \"conjecture2\"],  # Optional\n    ignore_imports=True,                   # Optional\n)\nprint(result.disproved_theorems)  # [\"conjecture2\"]\nprint(result.results)  # Per-theorem results\nprint(result.negated)  # Per-theorem negated goals\nprint(result.content)  # The processed Lean code\n```\n\n## CLI\n\n**Usage:** `axle disprove CONTENT [OPTIONS]`\n\n```bash\n# Disprove all theorems\naxle disprove theorems.lean --environment lean-4.31.0\n# Disprove specific theorems by name\naxle disprove theorems.lean --names main_theorem,helper --environment lean-4.31.0\n# Disprove specific theorems by index\naxle disprove theorems.lean --indices 0,-1 --environment lean-4.31.0\n# Pipeline usage\ncat theorems.lean | axle disprove - --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/disprove \\\n    -d '{\"content\": \"import Mathlib\\ntheorem solid_fact : 1 = 1 := rfl\\ntheorem bold_claim : 2 = 3 := rfl\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"content\": \"import Mathlib\\n\\ntheorem solid_fact : 1 = 1 := rfl\\ntheorem bold_claim : 2 = 3 := rfl\\n\",\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"results\": {\n    \"solid_fact\": \"Disprove: failed to prove negation.\",\n    \"bold_claim\": \"Disprove: goal is false! Proof of negation by plausible.\\n\\n===================\\nFound a counter-example!\\nissue: 2 = 3 does not hold\\n(0 shrinks)\\n-------------------\\n\"\n  },\n  \"negated\": {\n    \"solid_fact\": \"\u00ac1 = 1\",\n    \"bold_claim\": \"\u00ac2 = 3\"\n  },\n  \"disproved_theorems\": [\"bold_claim\"],\n  \"timings\": {\n    \"total_ms\": 97,\n    \"parse_ms\": 92\n  }\n}\n```\n"}, {"slug": "tools/normalize", "title": "normalize", "html_url": "https://axle.axiommath.ai/v1/docs/tools/normalize/", "markdown": "# normalize\n\nStandardize Lean file formatting to prepare for other operations, especially `merge` operations. Use this tool to detect when a file is unusually structured, in which case other Axle operations may behave unexpectedly.\n\n[Try this example in the web UI](https://axle.axiommath.ai/normalize#data=eyJjb250ZW50IjoiaW1wb3J0IE1hdGhsaWJcbm9wZW4gT3B0aW9uXG5cbm5hbWVzcGFjZSB0ZXN0XG5vcGVuIE9wdGlvblxuXG5sZW1tYSBzb21lX2xlbW1hICjOsSA6IFR5cGUpICh4IDogzrEpIDpcbiAgICBPcHRpb24uZ2V0RCAoc29tZSB4KSB4ID0geCA6PSBieVxuICBzaW1wIFtnZXREXVxuXG5lbmQgdGVzdCIsImZhaWxzYWZlIjp0cnVlLCJpZ25vcmVfaW1wb3J0cyI6dHJ1ZSwiZW52aXJvbm1lbnQiOiJsZWFuLTQuMjcuMCIsInRpbWVvdXRfc2Vjb25kcyI6MTIwfQ%3D%3D)\n\n## Input Parameters\n\n??? \"`content` \u00b7 str \u00b7 required \u00b7 Lean source code\"\n    The Lean source code to be processed by this tool.\n\n??? \"`normalizations` \u00b7 list[str] \u00b7 List of normalizations to apply\"\n    Options: remove_sections, remove_opens, expand_decl_names, expand_scoped_notations, remove_duplicates, split_open_in_commands, normalize_module_comments, normalize_doc_comments. Default: remove_sections, remove_duplicates, split_open_in_commands.\n\n??? \"`failsafe` \u00b7 bool \u00b7 default: `True` \u00b7 Return original if normalization fails\"\n    If true, returns the original content unchanged if normalization introduces errors. Defaults to true.\n\n??? \"`delab_options` \u00b7 dict \u00b7 Pretty-printer option overrides\"\n    A dictionary of Lean pretty-printer options (JSON format), applied on top of the options this tool pretty-prints with. Only `pp.*` options are accepted.\n\n    **Why override pretty-printer options?** Pretty-printed output can be ambiguous: the printed form loses information and fails to re-elaborate. Consider this example involving coercions:\n    ```\n    theorem explicit_coercion_test (n : \u2115) (hn : n > 0) : True := by\n      have h : (\u2211 i : Fin n, (1 : \u211d) / (i.val + 1)) \u2264 (harmonic n : \u211d) + 1 := by\n        sorry\n      trivial\n    ```\n\n    With default options, the coercion `(harmonic n : \u211d)` may be pretty-printed as `Rat.cast (harmonic n)`, losing the target type `\u211d`. This causes Lean to fail with errors like \"failed to synthesize RatCast \u2115\" because it can't infer the correct target type for the coercion. Setting `{\"pp.explicit\": true}` preserves the target type information and produces valid output.\n\n    This is a known limitation of the Lean pretty-printer (for more details, see [this Zulip thread](https://leanprover.zulipchat.com/#narrow/stream/113488-general/topic/RFC.3A.20printing.20coercions.20with.20type.20ascriptions)).\n\n    For preset buckets of suggested options, see the `verbosity` field (available on some tools).\n\n??? \"`mathlib_options` \u00b7 bool \u00b7 default: `False` \u00b7 Enable Mathlib options\"\n    If true, enables conventional Mathlib options. This toggle sets `linter.mathlibStandardSet` to true, `autoImplicit` to false, `relaxedAutoImplicit` to false, and `pp.unicode.fun` to true. It also runs the `#lint` environment linters and reports their findings in `lean_messages`.\n\n??? \"`global_options` \u00b7 dict \u00b7 Lean option overrides\"\n    A dictionary of Lean options (JSON format), applied to everything the request parses and elaborates, on top of the defaults and the `mathlib_options` preset. For example, `{\"maxHeartbeats\": 400000}` raises the elaboration heartbeats budget.\n\n    Each name must be a registered Lean option, and its value must match the type the option was declared with: a boolean, an integer, or a string.\n\n    For pretty-printer overrides on the tools that pretty-print output, see `delab_options`.\n\n??? \"`ignore_imports` \u00b7 bool \u00b7 default: `True` \u00b7 Ignore import mismatches\"\n    Controls import statement handling:\n\n    - `true` (default): Ignore the imports in `content` and substitute the environment's default header. This uses the pre-built cached environment, so it is fast. The substituted code is returned in the `content` field.\n    - `false`: Process the imports in `content` exactly as written. This is significantly slower (the cached environment cannot be reused) and may produce inconsistent or incorrect results if a required dependency such as `Mathlib.Tactic` is missing. A warning is returned in these cases. See the troubleshooting page for more details.\n\n??? \"`environment` \u00b7 str \u00b7 required \u00b7 Lean environment or version\"\n    The Lean environment to use for evaluation. Each environment includes a specific\n    Lean version and pre-built dependencies (typically Mathlib).\n\n    Available environments: `lean-4.28.0`, `lean-4.27.0`, `lean-4.26.0`, etc.\n\n??? \"`timeout_seconds` \u00b7 float \u00b7 default: `120` \u00b7 Max execution time in seconds\"\n    Maximum execution time in seconds. Requests exceeding this limit return a timeout error. Note that end-to-end request latency may exceed this timeout due to queue time and other overhead. Additionally, all non-admin requests are subject to an absolute maximum timeout of 900 seconds (15 minutes).\n\n\n## Output Fields\n\n??? \"`lean_messages` \u00b7 dict \u00b7 Messages from Lean compiler\"\n    Messages from the Lean compiler with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate invalid Lean code (syntax errors, type errors, etc.); an empty `errors` list means the code compiles.\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`tool_messages` \u00b7 dict \u00b7 Messages from normalize tool\"\n    Messages from the normalize tool with `errors`, `warnings`, and `infos` lists.\n    Errors here indicate tool-specific issues (not Lean compilation errors).\n\n    If the tool allows declaration selection and a `names`/`indices` selection is given, elaboration is skipped for the proofs of unselected declarations, so this field reflects only the selected declarations and is otherwise incomplete.\n\n??? \"`content` \u00b7 string \u00b7 The normalized Lean code\"\n    The standardized code. May be identical to input if `failsafe` triggered.\n\n??? \"`timings` \u00b7 dict \u00b7 Execution timing breakdown\"\n    Timing information in milliseconds for various stages of processing.\n\n??? \"`normalize_stats` \u00b7 dict \u00b7 Count of each normalization applied\"\n    Maps normalization names to counts (e.g., `{\"remove_sections\": 2}`).\n\n\n## Available Normalizations\n\n??? \"`remove_sections`\"\n    Removes `section`, `namespace`, and `end` commands. Declaration names are fully qualified to preserve semantics. If a `noncomputable section` is removed, `noncomputable section` is re-inserted at the top of the file to preserve semantics.\n\n    **Before:**\n    ```lean\n    namespace MyNamespace\n    noncomputable section MySection\n\n    theorem foo : 1 = 1 := rfl\n\n    end MySection\n    end MyNamespace\n    ```\n\n    **After:**\n    ```lean\n    noncomputable section\n    theorem MyNamespace.foo : 1 = 1 := rfl\n    ```\n\n??? \"`remove_opens`\"\n    Removes `open` commands, both standalone commands and `open ... in` prefixes (including nested chains like `open A in open B in ...`). An `open` that sits behind a non-`open` command (e.g. `def foo := 1 in open A`) is kept unchanged, with a tool warning.\n\n    Removing `open` commands changes how names and notations resolve, so combine this with `expand_decl_names` and `expand_scoped_notations`; a tool warning is emitted when either is missing.\n\n    **Before:**\n    ```lean\n    open Nat\n    theorem foo : Nat.succ 0 = 1 := rfl\n\n    open List in\n    theorem bar : List.isEmpty ([] : List Nat) = Bool.true := rfl\n    ```\n\n    **After:**\n    ```lean\n    theorem foo : Nat.succ 0 = 1 := rfl\n\n    theorem bar : List.isEmpty ([] : List Nat) = Bool.true := rfl\n    ```\n\n??? \"`expand_decl_names`\"\n    Fully qualifies declaration names by prepending all enclosing namespaces. Useful for making declarations unambiguous without relying on namespace context.\n\n    **Before:**\n    ```lean\n    open Option\n    example (\u03b1 : Type) (x : \u03b1) :\n        Option.getD (some x) x = x := by\n      simp [getD]\n    ```\n\n    **After:**\n    ```lean\n    open Option\n    example (\u03b1 : Type) (x : \u03b1) :\n        Option.getD (Option.some x) x = x := by\n      simp [Option.getD]\n    ```\n\n??? \"`expand_scoped_notations`\"\n    Expands scoped notations (those brought in by `open`) into their underlying applications. This runs Lean's delaborator with notations disabled, so the expanded form uses function application. Combined with `expand_decl_names`, constant names in the output are fully qualified.\n\n    Note: inside an expanded notation, all nested notations are stripped \u2014 including globals like +. This expander can be over-aggressive for notations whose body contains other, non-scoped notations.\n\n    Note: the delaborator isn't guaranteed to round-trip cleanly \u2014 coercions, universe annotations, and a few other constructs are known trouble spots and may produce output that doesn't re-elaborate. Uncommon in practice, but keep `failsafe` on if correctness matters.\n\n    **Before:**\n    ```lean\n    namespace MyNS\n    scoped infix:65 \" \u22b9 \" => HAdd.hAdd\n    end MyNS\n\n    open MyNS\n\n    def x : Nat := (1 \u22b9 2) + 3\n    ```\n\n    **After:**\n    ```lean\n    namespace MyNS\n    scoped infix:65 \" \u22b9 \" => HAdd.hAdd\n    end MyNS\n\n    open MyNS\n\n    def x : Nat := ( HAdd.hAdd  1  2 ) + 3\n    ```\n\n??? \"`remove_duplicates`\"\n    Removes duplicate commands, such as repeated `open` statements for the same module.\n\n    **Before:**\n    ```lean\n    open Nat\n    open Nat\n    open List\n    ```\n\n    **After:**\n    ```lean\n    open Nat\n    open List\n    ```\n\n??? \"`split_open_in_commands`\"\n    Splits `open [modules] in [decl]` syntax into separate `open` and declaration commands. This makes the structure more explicit and easier to process.\n\n    **Before:**\n    ```lean\n    open Nat in\n    theorem foo : succ 0 = 1 := rfl\n    ```\n\n    **After:**\n    ```lean\n    open Nat\n    theorem foo : succ 0 = 1 := rfl\n    ```\n\n??? \"`normalize_module_comments`\"\n    Converts module documentation comments (`/-! ... -/`) into regular block comments (`/- ... -/`). Module comments are typically used for file-level documentation.\n\n??? \"`normalize_doc_comments`\"\n    Converts documentation comments (`/-- ... -/`) into regular block comments (`/- ... -/`). Doc comments are typically attached to declarations to provide API documentation.\n\n## Python API\n\n```python\nresult = await axle.normalize(\n    content=lean_code,\n    environment=\"lean-4.28.0\",\n    normalizations=[\"remove_sections\", \"expand_decl_names\"],  # Optional: specify which normalizations\n    failsafe=True,  # Optional: return original if normalization fails\n)\nprint(result.content)\nprint(result.normalize_stats)\n```\n\n## CLI\n\n**Usage:** `axle normalize CONTENT [OPTIONS]`\n\n```bash\n# Normalize a file\naxle normalize theorem.lean --environment lean-4.31.0\n# Normalize and save to file\naxle normalize theorem.lean -o normalized.lean --environment lean-4.31.0\n# Apply only specific normalizations\naxle normalize theorem.lean --normalizations remove_sections,expand_decl_names --environment lean-4.31.0\n# Pipeline usage\ncat theorem.lean | axle normalize - --environment lean-4.31.0 | axle merge - other.lean --environment lean-4.31.0\n# Disable failsafe to always return normalized output\naxle normalize theorem.lean --no-failsafe --environment lean-4.31.0\n```\n\n## HTTP API\n\n```bash\ncurl -s -X POST https://axle.axiommath.ai/api/v1/normalize \\\n    -d '{\"content\": \"import Mathlib\\nsection\\ntheorem foo : 1 = 1 := rfl\\nend\", \"environment\": \"lean-4.28.0\"}' | jq\n```\n\n## Example Response\n\n```json\n{\n  \"lean_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"tool_messages\": {\n    \"errors\": [],\n    \"warnings\": [],\n    \"infos\": []\n  },\n  \"content\": \"import Mathlib\\n\\ntheorem foo : 1 = 1 := rfl\\n\",\n  \"timings\": {\n    \"total_ms\": 92,\n    \"parse_ms\": 87\n  },\n  \"normalize_stats\": {\n    \"remove_sections\": 2\n  }\n}\n```\n"}]}