Skip to content

Commit f95f9a3

Browse files
Merge pull request #145 from open-coder-ai/claude/armed-probe-verb
Promote the experiment probe into the package behind `agentseam probe`
2 parents ea76dd8 + 0c510e1 commit f95f9a3

31 files changed

Lines changed: 1768 additions & 1057 deletions

.github/workflows/probe.yml

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
name: Probe (recorded)
2+
3+
# Replays every committed witness recording through the real probe classifier and fails the
4+
# job if any trial disagrees with what src/agentseam/data/matrix.json claims. No agent CLI,
5+
# no network, no credentials -- the recorded driver only -- so this is the one probe check
6+
# every push and PR can run for free (armed initiative, wave 1, T4).
7+
#
8+
# This does not re-witness anything -- that needs a real, installed agent (see
9+
# docs/coverage-gaps.md and tools/watch_versions.py, which notices when a re-witness is due).
10+
# It answers a narrower question: does the matrix still agree with what was already
11+
# witnessed, on this change, right now? A `DISAGREES` row means a change made a recorded
12+
# claim untrue, and this job fails loudly rather than silently passing (contract invariant 4:
13+
# silence is a measurement, not a pass -- see tools/probe_ci.py's own summary line).
14+
15+
on:
16+
push:
17+
branches: [main]
18+
pull_request:
19+
paths:
20+
- "src/agentseam/probe/**"
21+
- "src/agentseam/data/recordings/*.json"
22+
- "src/agentseam/data/matrix.json"
23+
- "src/agentseam/matrix.py"
24+
- "src/agentseam/matrix_data.py"
25+
- "src/agentseam/matrix_evidence.py"
26+
- "src/agentseam/matrix_terms.py"
27+
- "src/agentseam/evidence_report.py"
28+
- "src/agentseam/recordings.py"
29+
- "tools/probe_ci.py"
30+
workflow_dispatch:
31+
32+
permissions:
33+
contents: read
34+
35+
jobs:
36+
check:
37+
runs-on: ubuntu-latest
38+
steps:
39+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
40+
with:
41+
persist-credentials: false
42+
43+
- uses: actions/setup-python@e9d6f990972a57673cdb72ec29e19d42ba28880f # v6
44+
with:
45+
python-version: "3.13"
46+
47+
- name: Replay every recording against the matrix
48+
# pipefail so a disagreement's nonzero exit fails the step even through `tee` --
49+
# the whole point of this job (see the module docstring for why exit 0 is unsafe).
50+
run: |
51+
set -o pipefail
52+
python3 tools/probe_ci.py | tee probe.txt
53+
54+
- name: Summarise
55+
if: always()
56+
run: |
57+
{
58+
echo '## Probe (recorded driver)'
59+
echo
60+
echo '```'
61+
cat probe.txt
62+
echo '```'
63+
} >> "$GITHUB_STEP_SUMMARY"

CONTRIBUTING.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,9 @@ python3 tools/experiment.py run --agent <agent> --report \
9696
--agent-version <version> --reporter @yourhandle > report.json
9797
```
9898

99+
(`agentseam probe run` from a `pip install` — same code, promoted so it ships in the wheel;
100+
`agentseam probe list` shows every trial.)
101+
99102
Open an issue with the **Evidence report** template and paste the result.
100103

101104
A few things worth knowing before you run either:
@@ -117,6 +120,12 @@ A few things worth knowing before you run either:
117120
`reference` driver is the vendor's documentation made executable, not a measurement, and
118121
the schema refuses to let it claim `live-run`. `python3 tools/verify_report.py
119122
report.json` shows you what a maintainer will see.
123+
- **"witnessed", "tested" and "recorded" are not interchangeable.** *Witnessed* is a real
124+
vendor client, watched, right now (`driver: real-agent`). *Tested* is an automated check
125+
that exercised the code without one (`driver: reference`) — real and useful, never the
126+
same claim. *Recorded* is a witnessed run frozen to a file and replayed deterministically
127+
(`driver: recorded`) — the data is witnessed, the replay is not a new witness. See
128+
[docs/coverage-gaps.md](docs/coverage-gaps.md) for the gates nobody has witnessed yet.
120129

121130
Evidence carries the reporter's handle. Age and version drift are displayed rather than
122131
hidden — see `agentseam matrix --evidence`. A row that says "verified against 3.17.8, 87

README.md

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,9 +200,20 @@ python3 tools/capture.py uninstall --agent cursor
200200

201201
The probe always allows, so it cannot interfere with real work, and payloads are reduced to
202202
shape before anything touches disk — keys and types survive, values do not. A different
203-
probe, `tools/experiment.py`, does the opposite on purpose: it denies, crashes and stalls,
204-
so it only ever runs in a throwaway directory a harness creates and removes — never point it
205-
at a config you work in. See [tools/VERIFY.md](tools/VERIFY.md).
203+
probe, `agentseam probe` (`tools/experiment.py` from a checkout — same code, promoted so it
204+
ships in the wheel), does the opposite on purpose: it denies, crashes and stalls, so it only
205+
ever runs in a throwaway directory a harness creates and removes — never point it at a
206+
config you work in. See [tools/VERIFY.md](tools/VERIFY.md).
207+
208+
A report's own `driver` and `basis` fields say how it was obtained, from a closed
209+
vocabulary: **witnessed** means a real vendor client, watched, right now (`driver:
210+
real-agent`, `basis: live-run` or `live-run-partial`); **tested** means an automated check
211+
exercised the code path with no vendor client at all (`driver: reference`, `basis:
212+
vendor-docs`) — real, useful, and never the same claim; **recorded** means a witnessed run
213+
frozen to a file and replayed deterministically (`driver: recorded`) — the data is
214+
witnessed, the replay is not a new witness. `evidence_report.validate()` refuses a report
215+
that claims more than its driver earned. See [docs/coverage-gaps.md](docs/coverage-gaps.md)
216+
for the three gates nobody has witnessed yet.
206217

207218
## Contributing
208219

docs/coverage-gaps.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Coverage gaps only a real agent can close
2+
3+
The probe (`agentseam probe`, promoted from `tools/experiment.py` in wave 1 of the `armed`
4+
initiative) covers 8 trials × 3 gateable events × 16 agents. The witnessed evidence covers a
5+
narrow slice of that: `claude_code@2.1.263`, `pre_tool` only, and every trial except
6+
`escalate` (the one `PreToolUse` reading no vendor doc settles). This closes the *tooling*
7+
gap, not the *evidence* gap -- **a cloud session holds no vendor credentials and cannot
8+
produce a witnessed row.** These three gaps are yours to close, on a machine with Claude
9+
Code installed and authenticated.
10+
11+
## What "witnessed", "tested" and "recorded" mean
12+
13+
A reader must be able to tell these apart without opening the source:
14+
15+
| Word | What it means | How you get one |
16+
| :--- | :--- | :--- |
17+
| **witnessed** | Somebody watched the real vendor client do this, on this machine, right now. | `--driver "<your headless CLI, containing {prompt}>"` |
18+
| **tested** | An automated check exercised the code path -- the dialect, the classifier, the schema -- without a real vendor client. Real, useful, **not** a vendor observation. | `--driver reference` (the protocol made executable) or the `pytest` suite itself |
19+
| **recorded** | A previously *witnessed* run, frozen to a file and replayed deterministically. The data is witnessed; the replay is not a new witness. | `--driver recorded` (the default once a recording exists) |
20+
21+
`agentseam probe run`'s own output never blurs these: a report's `driver` field is one of
22+
`real-agent`, `reference` or `recorded`, and its `basis` field is one of `live-run`,
23+
`live-run-partial`, `vendor-docs`, `vendor-source`, `third-party-install` or `inherited`
24+
(`src/agentseam/matrix_terms.py`). `evidence_report.validate()` refuses a report from the
25+
`reference` driver that claims a live basis -- that is the one invariant this whole
26+
initiative exists to enforce (contract invariant 1).
27+
28+
## The three gaps
29+
30+
1. **`escalate` at `pre_tool`.** The recording covers seven of eight trials at this gate.
31+
2. **Every trial at `prompt_submit`.** No agent has ever been witnessed at this gate.
32+
3. **Every trial at `stop`.** No agent has ever been witnessed at this gate, including the
33+
sentinel re-fire asymmetry that gate's own docs describe
34+
(`src/agentseam/probe/experiment.py`'s `_blocked()`).
35+
36+
## Close one gap
37+
38+
Pick a row from [`docs/witness-skeleton.json`](witness-skeleton.json) and run its command,
39+
replacing the placeholder driver with your own headless invocation:
40+
41+
```bash
42+
agentseam probe run --agent claude_code --event pre_tool --trial escalate \
43+
--driver "<your headless claude_code invocation, containing {prompt}>" \
44+
--agent-version <version> --record --report --reporter @yourhandle \
45+
> escalate-pre_tool-report.json
46+
47+
agentseam probe run --agent claude_code --event prompt_submit \
48+
--driver "<your headless claude_code invocation, containing {prompt}>" \
49+
--agent-version <version> --record --report --reporter @yourhandle \
50+
> prompt_submit-report.json
51+
52+
agentseam probe run --agent claude_code --event stop \
53+
--driver "<your headless claude_code invocation, containing {prompt}>" \
54+
--agent-version <version> --record --report --reporter @yourhandle \
55+
> stop-report.json
56+
```
57+
58+
`--record` appends to (never replaces) `data/recordings/claude_code@<version>.json`, so
59+
running all three builds one recording covering every gate. `--report` prints the
60+
evidence-report shape alongside it -- the two are independent flags and combine safely; the
61+
`.json` files above are what you paste into the **Evidence report** issue template, or add
62+
directly to a PR (`data/recordings/*.json` plus the `data/matrix.json` per-claim `test`
63+
pointers the recording backs).
64+
65+
`docs/witness-skeleton.json`'s `report` blocks are placeholders on purpose: replace the
66+
blank fields with your actual `--report` output, never hand-type a `basis`, `date` or
67+
`version`. `tests/test_coverage_skeleton.py` asserts the committed skeleton fails
68+
`evidence_report.validate()` exactly as it stands, and a genuinely filled-in block
69+
validates -- so the file can never be mistaken for evidence, only for the shape evidence
70+
takes.
71+
72+
## What this is not
73+
74+
A run against `reference` or `recorded` never counts as closing one of these gaps -- only a
75+
run against the real, installed `claude_code` does. See
76+
[CONTRIBUTING.md](../CONTRIBUTING.md#contributing-evidence-you-do-not-need-to-write-code)
77+
for the full walk-through and the two ways to submit what you find.

docs/witness-skeleton.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"_note": "Generated by the armed initiative, wave 1 (W1, 2026-09-10). Each entry in `gaps` names one coverage gap the committed witness recording (src/agentseam/data/recordings/claude_code@2.1.263.json) does not close, the exact command that closes it, and the evidence-report shape (src/agentseam/evidence_report.py) that command's --report output takes. Copy one `report` object, run its `command`, and replace the blank fields with the real --report output verbatim -- do not hand-type a basis, date or version. tests/test_coverage_skeleton.py asserts every `report` below, exactly as committed, is REJECTED by evidence_report.validate() -- an unfilled block must never be mistaken for a witnessed one.",
3+
"gaps": [
4+
{
5+
"gate": "pre_tool",
6+
"trial": "escalate",
7+
"why": "claude_code@2.1.263 covers every pre_tool trial except escalate -- the one PreToolUse reading (permissionDecision: \"ask\") no vendor doc settles, so the reference driver refuses to guess it (src/agentseam/probe/reference_agent.py raises Undocumented there).",
8+
"command": "agentseam probe run --agent claude_code --event pre_tool --trial escalate --driver \"<your headless claude_code invocation, containing {prompt}>\" --agent-version <version> --record --report --reporter @yourhandle > escalate-pre_tool-report.json",
9+
"report": {
10+
"report_version": 1,
11+
"agent": "claude_code",
12+
"basis": "",
13+
"date": "",
14+
"driver": "real-agent",
15+
"event": "pre_tool",
16+
"version": "",
17+
"experiments": {}
18+
}
19+
},
20+
{
21+
"gate": "prompt_submit",
22+
"trial": null,
23+
"why": "No trial has ever been witnessed at prompt_submit for any agent -- claude_code's block/fail_mode/etc. claims for this gate rest on vendor-docs only.",
24+
"command": "agentseam probe run --agent claude_code --event prompt_submit --driver \"<your headless claude_code invocation, containing {prompt}>\" --agent-version <version> --record --report --reporter @yourhandle > prompt_submit-report.json",
25+
"report": {
26+
"report_version": 1,
27+
"agent": "claude_code",
28+
"basis": "",
29+
"date": "",
30+
"driver": "real-agent",
31+
"event": "prompt_submit",
32+
"version": "",
33+
"experiments": {}
34+
}
35+
},
36+
{
37+
"gate": "stop",
38+
"trial": null,
39+
"why": "No trial has ever been witnessed at stop for any agent -- including the sentinel re-fire asymmetry (experiment.py's own _blocked()) that has so far only been exercised through the reference driver, never a real one.",
40+
"command": "agentseam probe run --agent claude_code --event stop --driver \"<your headless claude_code invocation, containing {prompt}>\" --agent-version <version> --record --report --reporter @yourhandle > stop-report.json",
41+
"report": {
42+
"report_version": 1,
43+
"agent": "claude_code",
44+
"basis": "",
45+
"date": "",
46+
"driver": "real-agent",
47+
"event": "stop",
48+
"version": "",
49+
"experiments": {}
50+
}
51+
}
52+
]
53+
}

pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,21 @@ exclude = ["*.py.tmpl"]
117117
"src/agentseam/contract.py" = ["PLR0913"]
118118
"src/agentseam/install.py" = ["PLR0913", "PLR0917"]
119119
"src/agentseam/install_identity.py" = ["PLR0913", "PLR0917"]
120+
# The probe (promoted from tools/, T1 wave 1) prints the same human table and CLI output
121+
# `agentseam probe` (cli.py) is a thin wrapper over -- cli.py's own T201 exemption above
122+
# applies equally to the functions that actually do the printing (probe/cli.py is `agentseam
123+
# probe`'s own dispatch; experiment_cli.py is tools/experiment.py's own). `run_trial`,
124+
# `record` and `run_turn` are the harness's own config-knobs functions (same shape as
125+
# contract.py above: one or two positional args, the rest optional and keyword-only).
126+
# `NoRecording` and `Undocumented` are pre-existing, widely-tested public names
127+
# (tools/experiment_kit tests import and `pytest.raises` on them by name); renaming for
128+
# N818's Error suffix churns every caller of a promoted module for no behavioural gain.
129+
"src/agentseam/probe/cli.py" = ["T201"]
130+
"src/agentseam/probe/experiment.py" = ["PLR0913"]
131+
"src/agentseam/probe/experiment_cli.py" = ["T201"]
132+
"src/agentseam/probe/experiment_report.py" = ["T201"]
133+
"src/agentseam/probe/recorded_driver.py" = ["T201", "PLR0913", "N818"]
134+
"src/agentseam/probe/reference_agent.py" = ["PLR0913", "N818"]
120135
# TODO(lint-adoption): this wave's Sonar/Checkstyle/FindBugs baseline (coding-standards.md
121136
# §3) was measured and fixed for src/ only -- 100 findings at 2026-09-02, enumerated in
122137
# plan/spine-a/reports/w51.md. tests/, tools/, examples/ and docs/'s two asset scripts were

src/agentseam/cli.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@
1010
import textwrap
1111
from datetime import date
1212

13-
from . import __version__, adapters, recordings
13+
from . import __version__, adapters, probe, recordings
1414
from . import install as install_mod
1515
from . import instructions as instructions_mod
1616
from . import packaging as packaging_mod
1717
from . import permissions as permissions_mod
1818
from . import staleness as staleness_mod
19-
from .contract import EVENTS
19+
from .contract import EVENTS, PRE_TOOL
2020
from .matrix import MATRIX, enforcement_level
2121

2222

@@ -261,6 +261,7 @@ def _main(argv=None):
261261
d = sub.add_parser("doctor", help="what is wired here; flag stale capability claims")
262262
d.add_argument("--repo", default=".")
263263
d.set_defaults(fn=_cmd_doctor)
264+
probe.cli.add_subparser(sub, default_event=PRE_TOOL) # armed: probe/cli.py owns its own wiring (review budget)
264265

265266
i = sub.add_parser("install", help="wire a handler command into an agent's config")
266267
i.add_argument("agent", help="agent name, or 'all'")

src/agentseam/probe/__init__.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""Armed: measures what an agent's hooks actually enforce, instead of asserting it.
2+
3+
Promoted from tools/ (wave 1, T1) so it ships in the wheel behind `agentseam probe`
4+
(cli.py) instead of being a developer-only script. tools/*.py at the old paths are now
5+
thin shims over these modules -- see tools/experiment.py's own docstring.
6+
"""
7+
8+
from __future__ import annotations
9+
10+
from . import (
11+
cli,
12+
experiment,
13+
experiment_cli,
14+
experiment_driver,
15+
experiment_escalate,
16+
experiment_probe,
17+
experiment_report,
18+
recorded_driver,
19+
reference_agent,
20+
)
21+
22+
__all__ = [
23+
"cli",
24+
"experiment",
25+
"experiment_cli",
26+
"experiment_driver",
27+
"experiment_escalate",
28+
"experiment_probe",
29+
"experiment_report",
30+
"recorded_driver",
31+
"reference_agent",
32+
]

src/agentseam/probe/_shell.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
"""The one place this package hands a command string to a shell.
2+
3+
`agentseam-no-shell-true` bans `shell=True` across `src/`, and it is right to: the runtime
4+
path sits inline in a developer's agent loop, on payloads an attacker can influence. The
5+
probe is not that path. It is a deliberate command-runner, and the shell is the thing under
6+
measurement rather than an implementation detail:
7+
8+
- `reference_agent` is Claude Code's protocol made executable, and Claude Code runs a hook by
9+
handing a command string to a shell. An argv list would emulate a vendor that does not
10+
exist, and every `tested`-basis row is measured against this driver.
11+
- The `transform` trial runs the command *as the hook rewrote it*. Measuring the rewrite means
12+
running the rewrite.
13+
- The driver template is a command line the operator typed on their own machine.
14+
15+
So the exception is real, and it is confined to this function rather than scattered across four
16+
call sites: one place to audit, and the rule keeps biting everywhere else in `src/`.
17+
"""
18+
19+
from __future__ import annotations
20+
21+
import subprocess
22+
23+
24+
def run_shell(command, *, check=False, capture_output=True, **kwargs):
25+
"""`subprocess.run(command, shell=True)`, defaulted to capture and never to raise."""
26+
# nosemgrep: agentseam-no-shell-true -- the audited exception; see this module's docstring.
27+
return subprocess.run( # noqa: S602
28+
command, shell=True, check=check, capture_output=capture_output, **kwargs
29+
)

0 commit comments

Comments
 (0)