Grace17 for AI agents

This guide is for people building agents that prove theorems in Grace17, and for the agents themselves. It describes the interfaces an agent can call, what each returns, and a working loop that uses them well. If your agent changes the Grace17 code base itself, read AGENTS.md instead.

The contract is the same for an agent as for a person. A proof is admitted only if the kernel checks it. Tactics, search, and suggestions are untrusted conveniences, so nothing an agent produces can admit a false statement. What an agent can do is explore quickly, read goal states, search the library, and hand a human a checked result with its assumptions listed.

Working with Claude Code#

Grace17 ships a Claude Code skill: the write–check loop, the module header, the proof style Cumulus expects, how theorem application infers its parameters, and the elaborator pitfalls that cost the most time. There are three ways to have it.

python3 scripts/all-errors.py <file> lists the first error of every theorem in a module in one run, so an agent can plan its repairs instead of discovering them one check at a time.

The machine interface#

Everything the Workbench shows is available as structured data with the same semantics through the machine interface: g17 serve speaks newline-delimited JSON over stdio, and each method is also a subcommand. The methods are check (per-theorem status, the goal state after every step, diagnostics with positions, the partial trace on failure, the axiom audit on request), goal (the state at a position), suggest (exact?, apply?, or rw? at a position, each suggestion as the command that pastes plus its theorem, instantiation, and evidence), search, declaration, audit, library, and notations. An agent that loops over check, goal, suggest, and paste should use it rather than read printed text.

sh
node bin/g17.ts check --json --module-root lib Probe.g17
node bin/g17.ts goal Probe.g17 --line 11 --column 12
node bin/g17.ts suggest Probe.g17 --line 12 --tactic 'exact?'
node bin/g17.ts search 'name:transitive module:Set.Core'

Interfaces at a glance#

Interface Use it for Returns
g17 serve, g17 check --json, g17 goal, g17 suggest A tool or agent driving the checker The Workbench's states, suggestions, search results, declarations, and audits as JSON; see machine interface
g17 check --artifacts build/modules Checking files from a shell Pass or fail; on failure the message, line, and the remaining goals with named hypotheses (--json adds the full tactic trace and every theorem's step states); with --artifacts, library imports replay from compiled artifacts in about a second
Workbench HTTP API, POST /api/check Structured proof states Every tactic's before and after goals, named hypotheses, search suggestions, sorry goals, statements, dependencies
TypeScript API, checkWorkbenchFiles The same data in-process The same structure, without a server
g17 axiom-audit --json What a proof depends on Named axioms, schema instances, Choice usage, dependency paths
Library search Finding existing results Qualified names, statements, source locations
g17 export, independent checkers Handing off a certificate A proof tape replayable by the Python, OCaml, and Rust checkers

The structured checking API#

Start the Workbench server once; it also builds the browser bundle:

sh
npm run workbench

Then post a workspace. Each file is a name and its full source. activeFile selects which file's theorems are reported in detail; the others are context.

sh
curl -s http://127.0.0.1:1717/api/check \
  -H 'content-type: application/json' \
  -d '{"files":[{"name":"Probe.g17","source":"module Probe\nimport Grace.Cumulus.Set.Core\n\ntheorem subset_chain (A B C : Set)\n  requires first : A ⊆ B\n  requires second : B ⊆ C\n  shows A ⊆ C\nproof\n  unfold Subset\n  intro x\n  intro hx\n"}],"activeFile":"Probe.g17"}'

The proof above stops one step short. The response says so and includes the whole trace. Abridged to its last step:

json
{
  "ok": false,
  "kind": "surface",
  "message": "1 goal remains; next is x ∈ C",
  "line": 11,
  "trace": {
    "steps": [
      {
        "line": 11,
        "command": "intro hx",
        "after": {
          "goals": [
            {
              "goal": "x ∈ C",
              "assumptions": ["A ⊆ B", "B ⊆ C", "x ∈ A"],
              "locals": [
                { "name": "first",  "formula": "A ⊆ B" },
                { "name": "second", "formula": "B ⊆ C" },
                { "name": "hx",     "formula": "x ∈ A" }
              ]
            }
          ]
        }
      }
    ]
  }
}

Three fields matter most. goal is the formula to prove. locals are the hypotheses by name, which is what you cite in exact ... with first, hx. assumptions are the same formulas without names, in context order.

When a file checks, the response has ok: true and a theorems array. Each theorem carries its statement, its steps with the same before and after states, its sorries, and its dependencies (axioms, theorems, definitions).

The same function is available in-process, which is faster for many calls:

ts
import { checkWorkbenchFiles, workbenchFailure } from "./src/index.ts";

let result;
try {
  result = checkWorkbenchFiles(files, "ZFG", ["Probe.g17"], { includeKernelDetails: false });
} catch (error) {
  result = workbenchFailure(error);   // same shape as the HTTP failure response
}

The HTTP server fixes the foundation to ZFG. The in-process call and the command line accept ZF, ZFC, or ZFG.

Use sorry as a probe#

A proof that ends in sorry checks successfully, so you get the complete step trace and a sorries entry recording what remains:

json
"sorries": [
  { "theorem": "Probe.subset_chain", "line": 12, "goal": "x ∈ C",
    "axiom": "sorry.Probe.subset_chain.12.1" }
]

This is the most useful pattern for an agent: write the steps you are sure of, end with sorry, read the remaining goal with its hypotheses, and continue. A sorry introduces a named axiom, so it also appears in the axiom audit and in dependencies.axioms. Cumulus accepts nothing that contains a sorry.

Query tactics return commands you can paste#

exact?, apply?, and rw? run bounded, deterministic search over the imported environment. Their results appear in the step's info field as fully qualified commands:

json
{
  "command": "apply?",
  "info": {
    "title": "apply?",
    "items": [
      "apply Grace.Cumulus.Set.Core.powerset.membership_iff.forward at A, C",
      "apply Grace.Cumulus.Set.Basic.Set.Subset.equation.backward at A, C"
    ]
  }
}

Replace the query tactic with the suggested line. A finished proof should not contain ? tactics. The written form is faster to replay, stable when the library grows, and readable by the person who reviews it. When search finds nothing, the message says so; search the library yourself or prove the missing lemma.

Finding theorems#

Three complementary routes:

Prefer applying an existing theorem to re-proving it. If a lemma is missing, add it to the module where it belongs. Do not inline it in your proof.

A working loop#

  1. State the theorem first. Write requires hypotheses with names and a shows clause. Check the file with the proof body sorry. If the statement does not elaborate, the message names the line and the problem, usually a missing import or an unresolved name.
  2. Read the goal. From the sorries entry or the trace, take the goal and the named locals.
  3. Try automation on the goal. ring for polynomial identities, decide for closed statements about finite sets (membership, inclusion, equality, cardinality over set literals), linarith for linear inequalities over the reals and rationals (products are atoms, so state nonlinear facts such as 0 ≤ x * y as hypotheses first), arith for linear arithmetic over naturals, simp [rules] for rewriting, definitional and logical closing, and membership in compound sets (the membership lemmas and Subset are automatic), typecheck for carrier obligations. See tactics. A tactic that fails costs nothing but time.
  4. Decompose. unfold a definition, intro binders and premises, obtain ⟨x, hx⟩ := h for existentials, cases for disjunctions, have h : P for an intermediate fact. Re-check after each step and read the new goal.
  5. Apply library results. Use apply? or search to find a candidate, then write it out: exact Subset.transitive with first, second, or have h := theorem at args with premises.
  6. Finish and audit. Remove every sorry and ?. Run the axiom audit and check that Choice appears only if it is needed. Check under ZF if the subject permits.

Checking the probe file above from the command line takes about half a second. Files that import large subjects such as real analysis take longer, because their imports are elaborated too; the in-process API keeps a loaded workspace between calls.

Foundations and the axiom audit#

The command line accepts --foundation ZF, ZFC, or ZFG. A theorem checked under ZFG may still have a Choice-free proof; the audit tells you:

sh
node bin/g17.ts axiom-audit --json --foundation ZF --module-root lib Probe.g17 --theorem Probe.subset_chain

The report lists direct and hereditary named axioms, schema instances (Separation, Replacement, datatypes, recursion), classical double-negation use, and a dependency path to each axiom. An incomplete audit exits with status 1 and cannot certify the absence of Choice. See axiom audits.

Handing off a result#

Before presenting a theorem as done:

sh
node bin/g17.ts compile --module-root lib Probe.g17 --out probe.g17m --environment probe.g17env
python3 checkers/python/g17check.py probe.g17m --environment probe.g17env

Report which of these you ran. A human takes ownership of anything submitted to Cumulus; see CONTRIBUTING.md.

Known limitations#

These are the gaps most likely to matter to an agent builder today:

Pitfalls that cost an agent iterations#

The checker's message is quoted for each so it can be matched.