Quick start
Get a task proven end to end. The commands and the transcript below are checked
mechanically: tests/test_readme_quickstart.py executes the equivalent
walkthrough from the README, tests/test_doc_transcripts.py compares the failing
run shown here against real output, and
tests/test_docs_metadata.py checks that every plan-auditor command on this
page is a real subcommand. None of them run this page top to bottom, so treat the
walkthrough as verified in pieces rather than as one executed script.
Install
pipx install plan-auditor
Prefer an isolated environment. If you must install into the current one:
pip install plan-auditor
Python 3.10 or newer. No third-party runtime dependencies.
The commands and transcripts on this page were written against a checkout of
main and re-checked against the published 2.4.2 release. See the
release-status section of the README for what each published version contains.
The examples invoke the interpreter as python. On a system that only provides
python3, activate a virtualenv that has python or change the cmd in the
plan to python3.
plan-auditor --help
If you would rather not install anything, the deterministic core runs straight from a checkout:
python scripts/audit_check.py --help
Your first verified task
The walkthrough uses a throwaway directory so nothing else on your machine is touched.
1. Create the project and the plan.
mkdir plan-auditor-demo && cd plan-auditor-demo
mkdir .plan-auditor
README.md:
# Demo project
.plan-auditor/plan.json:
{
"task": "Prove that README.md exists in this scratch project",
"created": "2026-07-24T00:00:00",
"requirements": [
{
"id": "REQ-001",
"description": "README.md must exist in the project root and print its first line.",
"priority": "must"
}
],
"steps": [
{
"id": 1,
"title": "README.md exists and is readable",
"covers": ["REQ-001"],
"verify": [
{ "type": "file_exists", "path": "README.md" },
{
"type": "run",
"cmd": "python -c \"print(open('README.md', encoding='utf-8').read().strip())\"",
"expect_exit": 0
}
],
"status": "pending"
}
]
}
Two rules are already visible here and they are not optional:
requirementsmust exist, and everymust/shouldrequirement must be named in at least one step'scovers.- Every step needs at least one behavioral check.
file_existsandregexcan supplement, but a step made only of those is rejected with "at least one BEHAVIORAL control (run/pytest/exec) is required". (execis an accepted alias forrun: both execute an argv list — or acmdstring split without a shell — and both reject"shell": true.)
2. Declare what you asked for.
request-source.json:
{
"format_version": 1,
"task": "Prove that README.md exists in this scratch project",
"requirements": [
{
"id": "REQ-001",
"description": "README.md must exist in the project root and print its first line.",
"priority": "must",
"acceptance_checks": [
{ "type": "file_exists", "path": "README.md" },
{
"type": "run",
"cmd": "python -c \"print(open('README.md', encoding='utf-8').read().strip())\"",
"expect_exit": 0
}
]
}
]
}
This is the host-owned half of the contract. You write it; the agent does not. Once activated it is immutable, and the supervisor rejects a plan whose requirements no longer match it.
3. Activate, seal, prove, gate.
plan-auditor request init . --file request-source.json
plan-auditor plan verify .
plan-auditor run . 1
plan-auditor audit .
Real output:
$ plan-auditor request init . --file request-source.json
{
"activated": true,
"valid": true,
"reason": "request contract active",
"request_sha256": "d7d47e409fc1c53b9830346740bac2a20e716aefc71e7396ad3db0ebbdd837f2",
"authenticated": false
}
$ plan-auditor plan verify .
{
"plans": {
"default": {
"verdict": "PASS",
"schema_errors": [],
...
"seal": {
"status": "sealed",
"format_version": 4,
"criteria_count": 2,
"plan_hash": "27617f6233effbc0f15c77eed6951a5d656d606f48adee840fd04cf43deb02fa",
...
}
}
},
"outcome": "PASS"
}
$ plan-auditor run . 1
[OK ] step 1: README.md exists and is readable (attempt 1/3)
- passed | README.md EXISTS
- passed | exit=0 (expected 0)
$ plan-auditor audit .
FULL AUDIT: all steps are being retested in fresh subprocesses...
[OK ] step 1: README.md exists and is readable
- passed | README.md EXISTS
- passed | exit=0 (expected 0)
ID ADIM DURUM KONTROL
----------------------------------------------------------------------
1 README.md exists and is readable VERIFIED 2 kontrol
RESULT: audit PASSED — every step is proven.
{
"outcome": "PASS",
"plans": {
"default": "PASS"
},
"deterministic_core": "fresh audit PASS for every active plan under workspace-wide freeze",
"gate": {
"outcome": "PASS",
"deterministic_passed": true,
"pending_steps": [],
"seal_ok": true,
"seal_violations": [],
"adversarial_findings": [],
"notes": []
}
}
A lone ... marks fields omitted here for length. The progress lines above come
from the tool's own UI strings; the JSON verdict is the stable machine interface,
so scripts should read "outcome", not scrape the prose.
Afterwards, .plan-auditor/ holds request.json, activation.json, seal.json,
evidence.jsonl and the updated plan.json. Nothing else needs to be trusted,
because plan-auditor audit . re-derives the verdict from scratch.
What "verified" actually means
| It does prove | It does not prove |
|---|---|
The named command exited 0 in a fresh subprocess |
That the command tested the right thing |
| The file or regex matched at audit time | That your product code is correct |
| The plan still matches the sealed contract | That your requirements were complete |
| The evidence chain is unbroken | That a same-user hostile process could not interfere |
| Every active plan passed | That natural-language meaning was formalized perfectly |
The right column is not a disclaimer to ignore; it is why the acceptance checks are yours to write. See Threat model.
Adding a real project
For a repository you already have:
- Write
.plan-auditor/plan.jsonwith one requirement per thing you asked for, and a step per deliverable. - Write
request-source.jsonwith the same requirement IDs, descriptions and priorities, plus the deterministic acceptance checks. - Activate the contract yourself, before handing the task to the agent.
- Seal with
plan-auditor plan verify .. - Let the agent implement and run
plan-auditor run . <step-id>after each step. - Require
plan-auditor audit .to exit0before merge.
Useful conventions:
- Prefer
{"type": "pytest", "args": "tests/ -q"}over a hand-written shell command; it is portable and its output is captured automatically. - Set
"timeout"on aruncheck that can hang. Output is bounded; overflow fails the check rather than truncating silently. - Add a named
outputto a step when a later step must consume its artifact, and bind it withrequires_outputs. See Dependency graph. - For non-trivial multi-step plans, run
plan-auditor-formalize compile .before sealing. The compiler refuses to mutate a sealed plan.
Troubleshooting
Start here, always:
plan-auditor doctor .
It emits JSON with a top-level assessment.outcome and an integrity block. A
workspace with no plan reports NO_PLAN; that is healthy.
audit reports "request contract is not activated"
You skipped the host-side approval. The agent must not be able to invent your requirements, so this gate has no bypass:
plan-auditor request init . --file request-source.json
Check plan-auditor request status . for the current activation state.
request init reports "... is not a safe regular file"
--file is resolved against your current working directory, not against the
workspace argument. From the workspace root use a bare filename, or pass an
absolute path:
plan-auditor request init . --file request-source.json
plan-auditor request init . --file "$PWD/request-source.json"
request init reports "invalid request contract: ... at least one BEHAVIORAL control"
Your acceptance_checks are all file_exists/regex. Add at least one run,
pytest, or exec check per requirement. (exec is an accepted alias for
run.)
request init reports "requirement REQ-001 is missing from every active plan"
The request contract is authoritative. Every requirement in it must also appear
in a plan's requirements with the same description and priority, and must be
named by at least one step's covers. If the text drifts you get
"was modified in plan requirements" instead — keep the two strings identical.
plan verify reports "seal.json already exists" / refuses to change
Once a plan is sealed, criteria may only be strengthened. To genuinely change
the scope you need a new host-approved request generation. See
Threat model and the plan-auditor-migrate-seal command for
the representation-only migration path.
A step fails three times and then refuses to run
$ plan-auditor run . 1
[FAIL] step 1: the sentinel script exits nonzero (attempt 1/3)
- FAILED | exit=3 (expected 0)
$ plan-auditor run . 1
[FAIL] step 1: the sentinel script exits nonzero (attempt 2/3)
- FAILED | exit=3 (expected 0)
$ plan-auditor run . 1
[FAIL] step 1: the sentinel script exits nonzero (attempt 3/3)
- FAILED | exit=3 (expected 0)
$ plan-auditor run . 1
[SKIPPED] step 1: the sentinel script exits nonzero previous genuine failed attempts — 3 limit exceeded.
Nothing is elided and every invocation exits 1. The check here is
python -c "import sys; sys.exit(3)" against expect_exit: 0; a failing command
that writes to stderr additionally gets a output: line, joined with " | ".
Fix the underlying problem. Re-arm deliberately with plan-auditor run . 1 --force
only when you are certain the check is correct and the implementation is wrong.
audit fails with "workspace content/type/mode changed during the full final-audit session"
The final audit is observational: it fingerprints the workspace, runs the checks, and fails if anything changed underneath it. The usual causes are yours, not the agent's:
- A background process, formatter, watcher, or build writing into the workspace during the audit.
- Redirecting the audit's own output into a file inside the workspace.
- Another agent or terminal running a generator concurrently.
Quiesce those writers and re-run. If a verification command itself creates the artifact it asserts, move creation into an earlier step and have the check only read it.
audit fails with "no fresh deterministic full-audit proof for the current plan"
The workspace fingerprint changed since the last successful audit. Re-run
plan-auditor audit . with nothing else touching the tree.
evidence verify reports a broken chain
Records in .plan-auditor/evidence.jsonl (or an archive) were edited or reordered.
Every record carries the SHA-256 of the previous one, so tampering is detected.
doctor explains where the chain breaks; you will have to re-establish the plan
rather than repair the log by hand.
The npm launcher always exits 0
It should not. The published bin (bin/plan-auditor.js, which npx and the
package's bin field resolve to) exits with the child verifier's code, so a
nonzero result reaches your shell:
npx plan-auditor audit .
echo $?
If that prints 0 while the JSON verdict says FAIL, you are running npm
2.4.0, which shipped a syntax error in the launcher so the bin entry point
could not run at all. Update the package:
npm i plan-auditor@latest
or use the console script, which does not go through the launcher:
pipx install --force plan-auditor
plan-auditor audit .
The npm launcher audits the wrong directory
npm 2.4.1 is affected by this and must not be used: it ran the CLI with the
installed package directory as its working directory, so a relative workspace
path such as . resolved against the package instead of the directory you ran
the command from. The output looks authoritative and refers to a tree you did not
ask about.
2.4.2 is fixed and is the release to use. If you are pinned to an older build,
pass an absolute path or use the console script instead:
npx plan-auditor audit "$(pwd)"
For comparison, plan-auditor audit . from the same shell reports the workspace
you are standing in, because the console script does not change directory.
The agent keeps "fixing" the test instead of the code
That is a policy problem, not a bug. Add a check that asserts the product
behavior independently — for example a run check that imports your package and
calls the API — so editing the test cannot produce a green result.
Next steps
- CLI reference for every command and exit code.
- Integrations for skill hosts, blocking hooks, and CI.
- Architecture for the layer model.
- Formal planning for LLM-free STRIPS reachability.
- What is measurable for claims this project does and does not make.