# LLMLL — v0.28.0 **An experiment in letting AI write the code: the compiler proves each function against its contract, and refutes a type-correct but wrong body before it merges.** LLMLL (Large Language Model Logical Language) is a programming language and verification pipeline built for experiments in which AI agents write code under formal contracts. Its primary author is an LLM agent, not a human: contracts state what a function must do, agents fill typed holes, and the compiler proves each body against its contract with Z3 before the patch is applied. Agents coordinate through those contracts, not through conversation. An agent can *hallucinate* an implementation and that's fine, as long as it satisfies the contract: verification turns hallucination from a failure mode into a search strategy (generate a candidate, check it against the spec, accept or reject). > **Current version:** see [`CHANGELOG.md § Latest`](CHANGELOG.md#Latest). Full release notes per version live in CHANGELOG; this README does not duplicate them. > **Learn more:** [`docs/README.md`](docs/README.md) is the reading guide to the documentation · [`ROADMAP.md`](ROADMAP.md) says what has shipped and what is next · [`experiments/README.md`](experiments/README.md) indexes the experiments and their results. --- ## See it: money that can't be created, proven `conserve(from, to, amount)` returns **both** post-transfer balances, and its contract ties them together: `(first result) + (second result) = from + to`: the total is conserved, full stop. A deliberately wrong body that credits the destination one unit extra is **type-correct** and looks harmless on inspection, but it breaks conservation, and the SMT solver refutes it: ```text # body: (pair (- from amount) (+ to (+ amount 1))) ← type-correct, creates money $ llmll verify conserve-bad.llmll error: body verification of 'conserve-bad' failed — implementation does not satisfy postcondition (constraint #0) # body: (pair (- from amount) (+ to amount)) ← correct, conserves the sum $ llmll verify conserve.llmll ✅ conserve.llmll — SAFE (liquid-fixpoint) ``` The proof is over **both** return values at once: a relational invariant, not a bound on one number. The wrong body above is scripted to show the check firing; no agent produced it. Dafny, Liquid Haskell or F\* would refute it too; what LLMLL adds is the loop around the proof, [below](#why-not-have-an-agent-write-dafny-liquid-haskell-or-f).

LLMLL refutes money creation before merge

The wrong body in this recording is scripted to show the check firing; no agent produced it. Regenerate with make demo-gifs. If an animation on this page shows a still frame, your browser or GitHub's Accessibility setting for animated images may be pausing it; click the image to open the GIF directly.

Full copy-pasteable walkthrough: [`payments-core/DEMO-RUNBOOK.md`](examples/payments-core/DEMO-RUNBOOK.md) — the composed `transfer`/`debit` call-chain beat and the single-constructor `settle` beat live there too. For the interactive **repair-loop protocol** — an agent checks out a typed `?hole`, submits a patch, and the compiler rejects or accepts it before anything merges — see [`withdraw-demo/DEMO-RUNBOOK.md`](examples/withdraw-demo/DEMO-RUNBOOK.md) (narrated: [`DemoPost.md`](examples/withdraw-demo/DemoPost.md)). --- ## Why not have an agent write Dafny, Liquid Haskell or F\*? Those tools prove the same kind of property, and LLMLL's proof path (liquid-fixpoint over Z3) is the one Liquid Haskell uses. LLMLL does not claim a stronger verifier. It builds the loop around the verifier for the case where an agent writes the code: - **A hole is a contract.** `llmll checkout` gives an agent a typed `?hole` with its precondition, the postcondition it must meet and the names in scope, and not the answer. `llmll patch` applies the fill only if the program still type-checks and the solver does not refute it. - **Weak contracts are flagged.** `--weakness-check` reports a contract so weak that a trivial body satisfies it; `--cdp` scores how sharply a contract rules out wrong bodies. - **Every function carries a trust level.** The trust report marks each function `verified`, `asserted` and so on, and a `verified` claim never silently rests on an unproven callee. - **Agents edit structure, not text.** Every program also has a JSON-AST form, and patches are RFC 6902 JSON-Patch against it, so there are no text merge conflicts. - **Decomposition is checked.** `llmll refine` fills a hole and spawns contracted sub-holes in one step, and rejects a sub-contract that no body can meet or that says nothing. **What the experiments show.** In [`experiments/minimal-agent/`](experiments/minimal-agent/SUMMARY.md), three frontier models wrote verified-correct bodies 30 of 30 times on fixtures built to trip them, with 0 wrong fills in 54 attempts. The evidence is for assurance: agent-written code, proved against a contract the agent did not write. The refutation demos in this README and on the blog use scripted wrong versions to show that the check works; the agents in these experiments did not produce them. Every experiment, with its result: [`experiments/README.md`](experiments/README.md). --- ## Prove what the solver can't — kernel-checked Not every property is decidable by SMT. `square(n) = n*n` claims `result ≥ 0` — but `n*n` is **nonlinear**, outside the linear-arithmetic fragment LLMLL sends to the solver, so the SMT verifier can only mark the postcondition `asserted` (an explicit "not proven"). With **`--leanstral`**, LLMLL states the obligation as a Lean theorem, has Leanstral prove it, and **checks that proof with the Lean kernel + Mathlib** — recording a `verified-lean` tier with an independently re-checkable `.lean` certificate.

LLMLL experimental verified-lean demo

Experimental. Recorded live on v0.26.3 against the Leanstral API; regenerate with examples/leanstral-demo/demo.sh (needs an API key and a Lean 4 + Mathlib project).

```text $ llmll verify examples/leanstral-demo/square.llmll --trust-report square: post: asserted # nonlinear: outside the SMT fragment, not proven $ LLMLL_LEANSTRAL_API_KEY=… llmll verify examples/leanstral-demo/square.llmll \ --leanstral --leanstral-lean-project ~/proofcheck --trust-report square: Leanstral proof found, Lean kernel + Mathlib CHECKED square: post: verified-lean (certificate: square.verified.lean) ``` The certificate is a Lean proof term the kernel accepted, checkable by anyone with Lean without trusting Leanstral. What you still trust is LLMLL's translation of the contract into the Lean theorem statement. **An AI proved what the SMT solver couldn't, and you don't have to take its word for it.** > **Experimental.** Opt-in demo; needs a Leanstral API key (`LLMLL_LEANSTRAL_API_KEY`) and a local Lean 4 + Mathlib project. Production Lean verification across all obligation classes is the deferred `LEAN-GA` rebuild. Reproduce: [`examples/leanstral-demo/`](examples/leanstral-demo/) (`demo.sh`) · design: [`docs/archive/shipped-design-specs/leanstral-demo-spec.md`](docs/archive/shipped-design-specs/leanstral-demo-spec.md). --- ## Try it The full repair loop (hole → rejected bad fills → accepted fix → verified) is the copy-pasteable [`DEMO-RUNBOOK.md`](examples/withdraw-demo/DEMO-RUNBOOK.md).

LLMLL agent protocol: holes, checkout, a rejected patch, an accepted patch, verified

The two fills are scripted stand-ins for agents: fixed patch files committed to the repo, not produced by an agent run. Script: examples/withdraw-demo/demo.sh.

**Zero-install (Docker).** No Haskell toolchain — the image bundles `llmll`, `z3`, and `liquid-fixpoint`: ```bash # see the SMT refutation of a conservation-breaking fill (no local files needed): docker run --rm ghcr.io/machunter/llmll verify /opt/llmll/examples/payments-core/conserve-bad.llmll # verify your own file (mounts the current directory at /work): docker run --rm -v "$PWD":/work ghcr.io/machunter/llmll verify myfile.llmll ``` **From source.** Build first: ```bash cd compiler && stack build stack exec llmll -- --help ``` Requires GHC ≥ 9.4 + Stack ≥ 2.9. The proof step also needs `z3` + `liquid-fixpoint`. > **Nothing passes without the solver.** On the from-source path, with `z3`/`liquid-fixpoint` absent, `verify` prints a `SOLVER NOT FOUND -- NOTHING WAS PROVEN` banner and exits `3`, and `patch` / `refine` refuse to apply a contracted patch (`PatchVerifyUnavailable`, exit `3`). Install both to see the refutation. (The Docker image bundles both, so it never hits this.) See [`docs/getting-started.md`](docs/getting-started.md). --- ## What it is LLMLL treats **verification as the coordination protocol**. A lead agent defines types and contracts (the *what*); specialist agents fill typed holes with the *how*; the compiler verifies each fill against its contract before merging. Agents trust each other's *contracts*, not each other's *code*. Merges are structured JSON-AST patches, not text diffs — so there are no structural merge conflicts, and every patch is re-verified before it lands. **It does not claim program correctness.** It guarantees that all code is *consistent with its declared specifications*, and it tracks how strong each guarantee is: a `verified` contract was proven by the SMT solver; an `asserted` one was not. Trust propagates — no `verified` claim silently rests on an unproven dependency. The weakness checker (`--weakness-check`) even flags a contract so weak that a trivial implementation satisfies it. Its discriminative-power sibling (`--cdp`) scores how sharply a contract rules out wrong bodies. Both checks measure *non-vacuity, not spec fidelity*: a contract that is discriminative yet captures the wrong behavior still passes, and the code still verifies against it. --- ## What's proven vs. not — read this before believing the headline The **shipped** proof path is SMT (Z3 via liquid-fixpoint) over a non-recursive **QF-LIA core** — integer linear arithmetic, let-bindings, conditionals, calls to contracted functions (assume-guarantee), and n-arm matches on admissible (non-recursive) sums (`Result` and user ADTs, nested and sequential) — **extended with three decidable theories**: the array class (`bytes[n]` memory safety, and `map[{int,string},{int,bool,string}]` get-after-put / key-presence / construction / read-modify-write), admissible datatype construction, and string **literals** (equality, distinctness, and code-point length). That covers numeric bounds, conservation invariants, length preservation, array/map bounds-and-presence safety, and string-tag discrimination. Everything else — string **structure** (concatenation, substring, regex), recursive-payload ADTs, non-linear arithmetic (`* / mod`), IO — **falls back** to contract-only checking, property tests, or runtime assertions, each carrying an explicit trust label (full matrix in [`LLMLL.md §5.3.5`](LLMLL.md)). Recursion is inside the fragment: with a `(decreases e)` measure the solver discharges, the proof is total; without one, it holds only if the recursion terminates, and the `verify` headline drops the `✅` and names the function. Nonlinear obligations have an **experimental** Lean 4 path: the opt-in `--leanstral` flag shown above, which needs a Leanstral API key and a local Lean 4 + Mathlib project. Production Lean verification across all obligation classes is deferred. `--leanstral-mock` runs the same pipeline against a mock prover, for testing. [`docs/one-pager.md`](docs/one-pager.md) carries the full **Claim-to-Evidence map** — every claim mapped to a shipped command or an explicit "Planned"/"Not shipped" label. The "Planned"/"Not shipped" labels are deliberate; read it before sharing. --- ## The repository runs on LLMLL Six of this repository's CI gates are LLMLL programs, in `tools/`: `version-gate` (version banners and schema versions agree), `doc-archive` (each archived design document sits where its status says), `doc-claims` (what the docs say the compiler rejects, checked against the compiler), `doc-path-lint` (path citations in prose; advisory), `refute-crux` (96 frozen `verify` verdicts, so a lost refutation fails CI) and `build-smoke` (builds and runs the other five gates end to end). The [CI workflow](.github/workflows/version-gate.yml) builds and runs them on every push to `main` and every pull request against it. In the same run, each gate's decision core (`adjudicate.llmll`) must pass `llmll verify`, and a deliberately broken copy of that core (`crux-*.llmll`) must be refuted, or the run fails. Only the decision core is proved; the file and process handling around it is built and run, not proved. The largest LLMLL program in the tree is not a CI gate. [`tools/llmll-driver/`](tools/llmll-driver/README.md) is the RFC-SWARM pipeline driver: 8522 lines across 39 modules, with 55 proved functions and 581 effectful `def-shell` functions that carry no proof by construction ([`docs/design/driver-ll-campaign-close.md`](docs/design/driver-ll-campaign-close.md)). Its README separates what is proved from what is only asserted. --- ## Compiler The active compiler is a **Haskell stack project** in `compiler/`. It is the only supported backend. | Command | What it does | |---------|--------------| | `llmll check [--strict]` | Parse + type-check; emit structured diagnostics. With `--strict`: unbound variables, unknown functions, unknown operators, and branch type mismatches are hard errors instead of warnings. Without `--strict`: text mode renders accumulated warnings on success. | | `llmll holes [--deps] [--deps-out FILE]` | List all `?hole` expressions. With `--deps`: include dependency graph in `--json` output. With `--deps-out`: persist graph to file. | | `llmll test ` | Run property-based tests (`check`/`for-all` blocks via QuickCheck) | | `llmll build [-o ]` | Generate a Haskell package (`src/Lib.hs` + `package.yaml` + `stack.yaml`) and compile it with `stack build`, or `ghc --make` when Stack is absent. Accepts both `.llmll` S-expression and `.ast.json` JSON-AST sources. With neither `stack` nor `ghc` on PATH it fails (exit 1); `--emit-only` writes the package without compiling it. | | `llmll build-json [-o ]` | Compile a JSON-AST source to a Haskell package: the `build` pipeline reading `.ast.json` input. `--emit-only` writes the Haskell sources but skips the internal stack build; `--contracts MODE` sets the runtime assertion mode (`full` default, `unproven`, `none`). | | `llmll run [args...]` | Compile the program and run it immediately; requires a `def-main`. The program inherits this process's stdin, stdout and stderr, and `llmll run` exits with the program's own status. Trailing arguments are passed through to the running program, flags included; `--` is needed only before an argument that starts with `-` and must not be read as a flag. | | `llmll verify [--fq-out FILE] [--leanstral] [--leanstral-lean-project DIR] [--leanstral-mock] [--trust-report] [--weakness-check] [--obligations] [--obligation-report] [--spec-coverage] [--strict-verified-core] [--cdp] [--strict-verify] [--proof-artifact FILE]` | Emit `.fq` constraint file and run `liquid-fixpoint` (if installed). With `--proof-artifact FILE`, also writes a unified, replayable verification record consolidating the trust/obligation/`.fq`/sidecar surfaces plus the determinism pins. With `--leanstral` (experimental), sends nonlinear obligations to a live Leanstral prover and checks the returned proof with the Lean kernel (see above); `--leanstral-mock` runs the same pipeline against a mock prover. With `--trust-report`, prints per-function trust summary with transitive closure, epistemic drift warnings, and `weakness-ok` suppressions (note: `--trust-report` reloads persisted evidence **instead of running fixpoint**, so a solver-refutable function renders as `asserted`, not `refuted`; use the default `verify` or `--strict-verified-core` to surface `refuted`). With `--weakness-check`, detects specs that admit trivial implementations. With `--obligations`, suggests postcondition strengthening when UNSAFE at cross-function boundaries. With `--obligation-report`, emits structured JSON obligation report for every hole, unproven contract, and failed call-site precondition. With `--spec-coverage`, classifies every function and computes effective specification coverage ratio. With `--strict-verified-core`, hard-errors if any function falls back from body-faithful verification, carries overflow-tainted verified evidence, or is refuted (body-faithful but disproved by the solver), transitively over the call graph, or reaches an imported contract that is not proved (fell back, never verified, or changed since). With `--cdp`, computes contract discriminative power per function: emits a paired `discriminative_axis` block in the trust-report JSON alongside the existing evidence axis. With `--strict-verify`, runs `--trust-report --weakness-check --spec-coverage --cdp` together: the recommended serious-verify path. | | `llmll replay-artifact ` | Re-derive and check a recorded proof artifact: recompute the source hash, re-run the stored VC under the pinned solver, and **fail closed** on any source/solver-determinism mismatch or `unknown`/timeout. | | `llmll typecheck --sketch ` | Partial-program type inference. Returns inferred type for every `?hole` plus `holeSensitive`-annotated errors and `invariant_suggestions` from the pattern registry. | | `llmll serve [--host H] [--port P] [--token T]` | Expose `--sketch` as `POST /sketch` HTTP endpoint for agent swarms. Default: `127.0.0.1:7777`. | | `llmll checkout [--multi N]` | Lock a `?hole` for exclusive agent editing. Returns a checkout token with the hole's contract context (`contract_pre`, `postcondition_goal`, `path_condition`) and typing context (`in_scope`, `type_definitions`). Use `--release` to abandon, `--status` to query TTL. With `--multi N`, opens or joins an R5 divergence session: N concurrent scratch-isolated tokens on one pointer. | | `llmll diverge-report ` | R5: collect a divergence session's fills and emit the `divergence_witness` record. The session id is the one returned by `checkout --multi`. A fill the solver could not grade (no solver, or no verdict) is listed under `status_partition.unavailable`, the record carries `solver_available`, and the command exits 3. A fill whose body is outside the decidable fragment is listed under `status_partition.outside_fragment`, not `refuted`, and does not change the exit code. | | `llmll patch ` | Apply an RFC 6902 JSON-Patch to a checked-out hole. Re-type-checks and re-verifies (SMT) the patched program before writing it. Exits 1 on a rejection, and 3 (`PatchVerifyUnavailable`, nothing written) when the solver is missing or returns no verdict. A success reports, per patched function, whether its body was proved (`verification[].body_faithful`); `--require-proof` refuses a fill whose postcondition passed only as an assumption (`PatchNotProved`). | | `llmll refine ` | Fill a checked-out hole **and** spawn new contracted sub-holes its body calls, atomically (cascading decomposition). Spawned sub-contracts pass a feasibility (no-miracle) gate (a sub-contract no body can discharge is rejected with a witnessing input) and a CDP vacuity gate; in-scope defs whose contracts subsume a spawned sub-contract are surfaced as advisory `reuse_suggestions` (non-blocking `W-REUSE` on an exact contract-equivalent). | | `llmll hub fetch --from-file ` | Install a local `.tar.gz` package into the hub cache (`~/.llmll/modules/`). Local tarballs only; there is no registry-by-name fetch. | | `llmll hub scaffold