diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json
index 6cbb4a2..ebb80e2 100644
--- a/.claude-plugin/plugin.json
+++ b/.claude-plugin/plugin.json
@@ -1,6 +1,6 @@
{
"name": "agent-polygraph",
- "version": "0.4.0",
+ "version": "0.4.1",
"description": "Sends Claude back once when its final message says tests pass but the session shows no passing run after the last edit. Adds /agent-polygraph:check for a report on your past sessions. Runs locally, nothing is uploaded.",
"author": {
"name": "Eleftherios Chondros",
diff --git a/README.md b/README.md
index 0357103..770ff92 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,41 @@
[](https://github.com/EleChon-pixel/agent-polygraph/actions/workflows/test.yml)
-Your coding agent said "all tests pass". Did a test pass after its last edit?
+Check whether your coding agent's test claims have evidence—and optionally run the tests before it stops.
-agent-polygraph is an audit of your past sessions. It reads the Claude Code and Codex session logs already on your machine and checks every "tests pass" claim against what ran: was there a passing test run after the agent's last code edit? It tells you how often your agents claimed something they hadn't verified, and shows the evidence for each case. No setup, no API key, nothing uploaded.
+agent-polygraph reads Claude Code and Codex session logs on your machine. Its audit checks detected passing-test claims against the available runs and edits, then shows the evidence behind each verdict. No API key or setup is needed for the default audit, and it uploads nothing.
-The audit and guard **read evidence; they do not execute tests**. A flagged claim is not proof the code is wrong: it means the available evidence does not support the claim at that point. If you want automatic test execution, use a separate test-running hook or CI.
+```sh
+npx --yes agent-polygraph # audit your own sessions
+npx --yes agent-polygraph --demo # generated sample sessions
+```
+
+The audit does not execute tests. The optional guard offers three policies:
+
+| Policy | Executes tests? | What happens at Stop |
+|---|---|---|
+| `claims` (default) | No | Challenges definitely unbacked passing-test claims once; allows ambiguous evidence. |
+| `strict` | No | Also challenges inconclusive passing-test claims; surfaces unresolved repeated claims. |
+| `verify` | Yes | Runs your configured project command regardless of claims, retests repairs, and reports unresolved verification after a bounded repair budget. |
+
+A flagged claim means the available evidence does not support it at that point. It does not establish that the code is broken or the agent intended to mislead you. A passing verification establishes only that your chosen command passed on the observed inputs.
+
+To enable executable verification, use a persistent installation and run this in the project you want checked:
```sh
-npx agent-polygraph # your own sessions
-npx agent-polygraph --demo # generated sample sessions
+npm install -g agent-polygraph
+agent-polygraph guard --install --mode verify --test-command "npm test"
+agent-polygraph guard --status
```
+Replace `npm test` with the command that checks your project. Verification requires a Git checkout for complete fingerprints. Existing installations and the Claude plugin keep the default claim-only policy until explicitly reconfigured. See [guard policies](#guard-policies-optional) for timeouts, repair budgets and caching.
+
> **Beta / evidence limitations:** The parser has been evaluated on the author's own sessions and public Claude Code traces ([evaluation](docs/evaluation.md)). Controlled replays and real executed tests exist, but **independent, blinded human-validated accuracy has not been established**. If you report a wrong verdict, [open an issue](https://github.com/EleChon-pixel/agent-polygraph/issues/new/choose) with a *manually redacted* example—never upload an unreviewed raw session log.
-Historical example from the author's sessions, July–October 2026 (not a representative benchmark or a current-user measurement):
+Historical example: the author's sessions, July–October 2026
+
+This is not a representative benchmark or a current-user measurement.
```
agent-polygraph 32 working sessions · Jul 30 – Oct 31, 2026 · 11.0s
@@ -45,7 +66,17 @@ Where the time went
Under the tables it prints the latest examples: what the agent claimed, the last test command and its result, what was edited afterwards, and the session file, so you can check it yourself.
-**What the benchmarks actually show:** agent-polygraph detects stale test claims more reliably than a *simple any-prior-pass rule* in [25 authored replay scenarios](docs/benchmark.md) (14/14 unbacked claims versus 7/14). In [12 instrumented projects using real test executions](docs/live-evaluation.md), its selective guard requested **5** additional tests and detected **3 of 7** failing final suites; rerunning the full suite every time required **12** additional tests and detected **all 7**. That is an efficiency-versus-coverage trade-off, **not** evidence that the guard improves overall defect detection. [Public-session workload projections](docs/benchmark.md) are separate from actual executed tests. The independently labeled human study is [still open](https://github.com/EleChon-pixel/agent-polygraph/issues/3).
+
-`guard` is a Stop hook that runs the same checks on the live session. When the agent's final message says tests pass and the session doesn't back it up, the agent is sent back once:
+The default `claims` policy is a Stop hook that runs the audit checks on the live session. When the agent's final message says tests pass and the session doesn't back it up, the agent is sent back once:
> agent-polygraph: your final message says "All tests pass.", but you edited calc.js after the last test run. Run the relevant tests now and report the actual result. If they can't be run, say so instead of claiming they pass.
-It blocks at most once per stop, looks only at the final message (so a claim the agent already corrected doesn't count), stays quiet when it can't tell, and never fails the turn because of its own errors. Install it globally (`npm i -g agent-polygraph`) so the hook doesn't start `npx` on every stop.
+The `claims` policy requests one corrective continuation, looks only at the final message (so a claim the agent already corrected doesn't count), stays quiet when it can't tell, and never fails the turn because of its own errors. Install it globally (`npm i -g agent-polygraph`) so the hook doesn't start `npx` on every stop.
-I tested it with both agents in a staged setup: they were told to edit code after a test run and then claim the tests pass (the GIF above is one of these runs). Both were stopped, reran `npm test` and reported the failure. Codex needs one extra step: it only runs hooks you have approved, and only reads a project's `.codex/` folder if you trust it. After installing, open `codex` in the project once, trust the folder and approve the hook.
+I tested the claim-only policy with both agents in a staged setup: they were told to edit code after a test run and then claim the tests pass (the GIF above is one of these runs). Both were stopped, reran `npm test` and reported the failure. Codex needs one extra step: it only runs hooks you have approved, and only reads a project's `.codex/` folder if you trust it. After installing, open `codex` in the project once, trust the folder and approve the hook.
### Verify the project even when the agent makes no test claim
@@ -112,16 +143,14 @@ The selected command is the coverage contract: choose the full suite if that is
For transcript-only checking with visible ambiguity, use `guard --install --mode strict`. It also challenges inconclusive passing-test claims and surfaces unresolved repeated claims. It does not run tests or discover unclaimed failures. The default `--mode claims` retains the original one-cycle, definite-claim-only behavior; the plugin uses that default unless a project install overrides it.
-### Do you need it?
-
-Maybe not. Two simpler setups also work, and they are more thorough:
-
-- A Stop hook that runs your test suite after every turn and gives the agent the result. This also catches breakage when the agent doesn't claim anything.
-- A Stop hook that always asks the agent to double-check its results. When I tried this on small tasks where the agent had edited code after its last run, it reran the tests every time (6 of 6, Claude Haiku).
+### Which policy should you use?
-The trade-off is cost. Both fire on every stop, so every turn ends with a test run, and the second also adds a model turn, even when nothing changed or nothing was claimed. A test-running hook also has to know each project's test command and setup (a virtualenv, environment variables, a subfolder, a database). The default `claims` policy runs nothing: it reads the session in well under a second and only sends the agent back when the final message claims a pass that no run after the last edit supports (about 6% of claims in my logs), naming the edit that made the run stale.
+- Use the **audit** to investigate past sessions without running tests or changing agent behavior.
+- Use **`claims`** when you want occasional verification requests and accept that unclaimed failures and ambiguous evidence can pass through.
+- Use **`strict`** when you want ambiguous passing-test claims challenged, while retaining transcript-only operation. It still cannot discover unclaimed failures.
+- Use **`verify`** when the chosen suite should run before the agent stops, including after repairs. With caching off it incurs test execution on each Stop within the repair budget. It adds local receipts, fingerprint checks and bounded recovery to a test-running hook.
-If your suite runs in a few seconds and needs no setup, running it on every stop provides stronger failure coverage. The `verify` policy does that with bounded repairs and local status receipts. The `claims` policy is for when you want fewer rerun requests and accept narrower coverage. `verify` with caching can reduce repeat executions, but reusing a pass assumes unchanged ignored inputs and external dependencies.
+A plain Stop hook that runs your suite is also a reasonable choice for a simple project. Neither policy is a better defect detector than the tests it executes. Optional caching can reduce repeated executions of deterministic local suites, but it adds assumptions about unchanged ignored inputs and external services.
### As a Claude Code plugin
@@ -133,11 +162,11 @@ In Claude Code you can install the guard as a plugin instead:
On Claude Code older than 2.1.275, run `/plugin marketplace add EleChon-pixel/agent-polygraph` first, then `/plugin install agent-polygraph@agent-polygraph`.
-The plugin runs the same Stop hook from its own copy, so nothing is downloaded when the agent stops, and it adds `/agent-polygraph:check` for the report (it takes the same options, for example `/agent-polygraph:check --since 30d`). If you also installed the hook with `guard --install`, the plugin's copy stays quiet so the agent isn't sent back twice. The plugin doesn't include `record`; turn that on separately if you want it.
+The plugin runs the `claims` Stop hook from its own copy, so nothing is downloaded when the agent stops, and it adds `/agent-polygraph:check` for the report (it takes the same options, for example `/agent-polygraph:check --since 30d`). If you also installed the hook with `guard --install`, the plugin's copy stays quiet so the agent isn't sent back twice. The plugin doesn't include `record`; turn that on separately if you want it.
## Evidence the logs don't have
-Some things never show up in the logs: tests you ran yourself in another terminal, CI results, and files changed by a script that builds their names at run time. Two optional extras cover them.
+Some things never show up in the logs: tests you ran yourself in another terminal, CI results, and files changed by a script that builds their names at run time. Optional recording and GitHub CI evidence can reduce those gaps for retrospective analysis. Executable verification provides a separate current test run; its status receipts are not imported into the retrospective claim report.
```sh
npx agent-polygraph record --install # this project; add --global for all of them
@@ -153,7 +182,7 @@ npx agent-polygraph --ci # GitHub Actions (online)
The report shows how many claims each of these settled.
-## What it checks
+## What the retrospective audit checks
| Finding | How it's decided |
|---|---|
@@ -173,22 +202,22 @@ Background runs are followed: Claude Code's `run_in_background` results (the not
## How it decides
-Everything is deterministic pattern matching over the logs. There is no LLM involved, and the same logs always give the same numbers. When a rule is unsure, it says "couldn't tell" instead of flagging the agent.
+Retrospective verdicts use deterministic pattern matching, without an LLM. The same transcripts and supplemental inputs produce the same analysis; persisted output files, recorder evidence and online CI results can change those inputs. Ambiguous audit evidence is reported as "couldn't tell". Guard handling of uncertainty depends on its selected policy.
- Each command's start and its result are separate events, in the order they were observed. A claim can only be backed by a result the agent had already seen. A run covers the code as it was when the run started, so an edit during the run makes it stale.
- In a Codex cell that runs several commands, each command gets its own output. If outputs can't be matched to commands, the result stays unknown.
- An edit counts once it happened: a Claude Code edit after its tool result confirms it, a Codex patch if it applied. Shell writes count too (`sed -i`, redirects, `tee`, `cp`/`mv`, scripts that write named files), placed by the `cd` in effect where they appear.
- A script's writes are the files it clearly writes, such as `open('x.py', 'w')`, `Path('x.py').write_text(...)`, `writeFileSync('x.js', ...)`, or a variable holding such a path that is written later. Files it only reads don't count. A write to a file it can't name (a loop over paths, `extractall`, `shutil.copy` to a variable) makes the claim "couldn't tell".
-- An edit in one checkout or worktree doesn't make a run in another one stale. Only edits to source files count; docs, config, data, generated output and hidden tool folders don't.
+- In the retrospective audit, an edit in one checkout or worktree does not make a run in another stale. Only recognized source-file edits count; docs, config, data, generated output and hidden tool folders are excluded. Executable verification separately fingerprints all enumerated non-ignored working-tree files, including configuration and data.
- `tail pytest.log` is evidence about the run that wrote `pytest.log`, not a new run.
- Results come from the runner's summary line first (`3 failed, 10 passed in 1.2s`, `Tests: 1 failed`, `test result: FAILED`). An exit code is trusted only when the test ran last (`cd app && pytest`, `ruff check .; pytest`) or a plain `&&` chain exited 0. `npm test; true` exits 0 because of `true`.
- Commands refused by a permission prompt, the auto-mode classifier or a hook never ran, so they aren't test runs.
- Every detected claim sentence in a message is checked separately. The guard quotes a claim that actually lacks evidence, even when another claim in the same message is backed.
- Runs in an unrelated checkout do not back the current project. Subagent runs retain their own working directory; ambiguous project scope is left unknown.
- A claim is a sentence like "all tests pass" or "62 passed". Plans ("I'll make sure the tests pass"), conditions ("once tests pass"), history ("the latest recorded run passed"), questions, partial results ("58 of 59 pass") and quoted runner output are ignored. English is supported, plus simple forms in Greek, Spanish, Portuguese, German, French and Italian.
-- Subagent transcripts are merged into the parent session, so a subagent's passing run backs the parent's claim.
+- Subagent transcripts are merged into the parent session. A subagent's passing run can supply evidence when its directory and scope are relevant; an unrelated worktree is not proof of a pass in the parent checkout.
- Tests the agent created in the same session can't be "weakened". A Claude Code `Write` over an existing test is an overwrite, so it can.
-- `guard` judges only the final message. "Correction: I did not run the tests" after an earlier claim doesn't trigger a block.
+- Transcript-only guard policies judge the final message. "Correction: I did not run the tests" after an earlier claim does not trigger a claim-based block. Executable verification runs its configured command independently of the final wording.
The rules are in [`src/classify.js`](src/classify.js) and the tests in [`test/`](test), including a labelled set of claim and non-claim sentences ([`test/claims.corpus.test.js`](test/claims.corpus.test.js)). A false positive or a missed claim makes a good [issue](https://github.com/EleChon-pixel/agent-polygraph/issues/new/choose); include the redacted sentence and it goes into that set.
@@ -201,20 +230,20 @@ The public Claude Code evaluation reviewed 26 parsed sessions and identified 24
Two reproducible controlled experiments test narrower propositions:
- [Stop-hook replay](docs/benchmark.md): 25 authored Claude Code/Codex scenarios. Agent-polygraph correctly blocked 14/14 labeled unbacked claims versus 7/14 for a naive “any test passed earlier” hook, with zero false blocks in these selected cases.
-- [Executed-test policy comparison](docs/live-evaluation.md): 12 authored scenarios with actual Node.js test executions. The selective guard required five extra test executions and detected three of seven failing final suites; running the full suite after every response required 12 executions and detected all seven.
+- [Executed-test policy comparison](docs/live-evaluation.md): 12 authored scenarios with actual Node.js test executions. The claim-only guard required five extra test executions and detected three of seven failing final suites. Both executable verification with caching off and always-rerun required 12 executions and detected all seven.
-The replay labels and scenarios were constructed by the project authors. The guard cannot discover failures the agent never claimed away, or infer whether a narrow passing test establishes a full-suite pass. **An independent, blinded human evaluation is still needed.** The [review issue](https://github.com/EleChon-pixel/agent-polygraph/issues/3) describes the protocol; `scripts/blind-review.mjs` prepares prediction-free review packets. No raw private session logs should be shared with reviewers or issues without permission and redaction.
+The replay labels and scenarios were constructed by the project authors. Transcript-only guard policies cannot discover failures without a relevant passing-test claim. Executable verification runs the chosen command regardless of claims, but its coverage depends on that command and the tests themselves. **An independent, blinded human evaluation is still needed.** The [review issue](https://github.com/EleChon-pixel/agent-polygraph/issues/3) describes the protocol; `scripts/blind-review.mjs` prepares prediction-free review packets. No raw private session logs should be shared with reviewers or issues without permission and redaction.
## Limits
-These are the gaps that remain. `record` and `--ci` can reduce some of them, but neither replaces running tests or provides independent proof of correctness.
+The retrospective audit depends on recorded evidence; `record` and `--ci` can reduce gaps. Executable verification instead runs a current command, with the fingerprint, timeout and caching limits described above. None of these establishes overall code correctness.
- Tests outside the logs. Tests the agent ran, test runs Claude Code recorded from hooks, commands you typed with `!` in Claude Code, and test output you pasted into the chat all count. So do your own bash and zsh runs with `record --shell`, and GitHub Actions runs on pushed commits with `--ci`. Other shells (fish, PowerShell, an IDE's test button), other CI services, and CI runs on commits the session never printed are still invisible.
- "The full suite passes" prefers an explicitly recognized full-suite command in the same folder, even when no count was printed. If the only observed run is targeted and there is no full-suite evidence, the result is **inconclusive**, not backed. Count-based heuristics still help when the runner cannot reliably identify the suite's scope. A passing test run is evidence of that run, never proof that all code works.
- Output with no summary. Codex keeps each command's full output. Claude Code may save long output to a separate file that the analyzer reads if it still exists. New `record` entries retain **parsed test outcomes and pass counts, not raw test output**. A run abandoned before printing a result leaves nothing to read.
- Files a script names at run time. Without `record` these make a claim "couldn't tell", never "backed". `record` settles them, but only in git checkouts and only for sessions after it was installed.
- New agent versions can change the log format. Supported: Claude Code 2.x (Bash and PowerShell tools; Linux, macOS and Windows paths) and Codex CLI. The report lists the versions it read, warns about newer ones, and counts files it couldn't parse instead of dropping them silently.
-- Unbacked is not the same as false. "Code edited after the last run" means the claim wasn't verified after the edit; the edit may have been harmless, such as a comment or a test file. The report can't tell whether the tests would have passed, because nobody ran them at that point.
+- Unbacked is not the same as false. "Code edited after the last run" means the claim wasn't verified after the edit; the edit may have been harmless, such as a comment or a test file. The retrospective report cannot establish whether the tests would have passed at that point. A later successful verification does not retroactively establish what evidence the agent had when it made the original claim.
- The numbers describe your sessions, projects and prompts. They are not a benchmark of the models.
## Options
@@ -230,6 +259,7 @@ These are the gaps that remain. `record` and `--ci` can reduce some of them, but
--json full results as JSON: findings, unclear reasons, unreadable files (keep it local)
explain investigate flagged/uncertain verdicts (--limit, --since, --session, --project)
doctor check local hooks, transcript availability and recorder permissions
+guard --status inspect local executable-verification receipts
--ci also use GitHub Actions results (goes online, through your gh login)
--claude-dir