Skip to content

Commit 22b35a1

Browse files
committed
feat(agents): harden-pr probes, ledger, and recall benchmark harness
Adds missing-test eval fixture, LEDGER.md, vet/reconcile/quick modes, score-probe recall scorer, and test:harden-probes smoke. Live probe run: recall 1.0 on golden finding.
1 parent d4589db commit 22b35a1

16 files changed

Lines changed: 438 additions & 23 deletions

File tree

.agents/skills/harden-pr/LEDGER.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Harden-pr ledger
2+
3+
Single durable backlog for [`harden-pr`](./SKILL.md). Parent reads **§ Rejections** at vet step; **§ Deferred** on cap and on `/harden-pr reconcile`.
4+
5+
## Rejections
6+
7+
By-design or false-positive findings — do not re-raise.
8+
9+
```markdown
10+
- **[category]** `file:line` — label: reason
11+
```
12+
13+
<!-- Example:
14+
- **[security]** `src/cli/proxy.ts:42` — https_proxy env: by-design — standard CLI proxy convention.
15+
-->
16+
17+
## Deferred
18+
19+
Capped or out-of-scope-for-now — reconcile re-vets; remove lines when fixed.
20+
21+
```markdown
22+
- **[severity]** `file:line` — finding (deferred: out of scope | cap | blocked)
23+
```

.agents/skills/harden-pr/SKILL.md

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ description: >-
44
Bring a branch to pristine, maximum production readiness without changing PR intent —
55
spawn parallel Task subagents (never inline review), fix in-bounds findings, loop autonomously until
66
clean or pass cap, then report once. Use after a tracer-bullet commit (lite), before PR
7-
is done (full), or on "harden", "harden-pr", "pristine", "review until clean",
8-
"production-ready pass". Invoking this skill authorizes one harden commit at cycle end.
7+
is done (full), on "harden", "harden-pr", "pristine", "review until clean",
8+
"production-ready pass", or "harden-pr reconcile". Invoking this skill authorizes one harden commit at cycle end.
99
NEVER stop mid-loop to ask about commits, babysit, or the next pass. NEVER redesign the
1010
feature or change observable runtime behavior.
1111
---
@@ -16,9 +16,9 @@ description: >-
1616

1717
Local loop: parallel reviewer subagents → merge findings → fix in-bounds → re-verify → repeat until clean or cap → **one final report**.
1818

19-
**Invoking this skill (`/harden-pr`, `harden-pr lite`, `harden-pr full`) is a run-to-completion command.** The agent executes the full loop before ending the turn.
19+
**Invoking this skill (`/harden-pr`, `harden-pr lite`, `harden-pr full`, `harden-pr quick`, `harden-pr reconcile`) is a run-to-completion command.** The agent executes the full loop before ending the turn.
2020

21-
Sister skills: [`audit-pr-architecture`](../audit-pr-architecture/SKILL.md) (extended structural reviewer). Mention **`babysit`** only in the final report (full mode) — never mid-loop.
21+
Sister skills: [`audit-pr-architecture`](../audit-pr-architecture/SKILL.md) (extended structural reviewer). **Ledger:** [LEDGER.md](./LEDGER.md) (rejections + deferred — one file). Mention **`babysit`** only in the final report (full mode) — never mid-loop.
2222

2323
## Run-to-completion (read first)
2424

@@ -42,12 +42,14 @@ Otherwise: resolve anchor → run all passes → fix → verify → next pass
4242

4343
## Modes
4444

45-
| Mode | When | Scope | Max passes |
46-
| -------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------- | ---------- |
47-
| **Lite** | After each tracer-bullet slice commit ([`tracer-bullets`](../../rules/tracer-bullets.md) cadence) | Files in the slice diff | 2 |
48-
| **Full** | User intent ("full harden", "PR done", "production-ready pass") **or** offer when an in-flight `docs/plans/<topic>.md` checklist is complete | `origin/main...HEAD` | 3 |
45+
| Mode | When | Scope | Max passes |
46+
| ------------- | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- | ---------- |
47+
| **Lite** | After each tracer-bullet slice commit ([`tracer-bullets`](../../rules/tracer-bullets.md) cadence) | Files in the slice diff | 2 |
48+
| **Quick** | Cheap uncertainty pass ("quick harden") | Last commit or slice diff | 1 |
49+
| **Full** | User intent ("full harden", "PR done", "production-ready pass") **or** offer when an in-flight `docs/plans/<topic>.md` checklist is complete | `origin/main...HEAD` | 3 |
50+
| **Reconcile** | `/harden-pr reconcile` — process [LEDGER.md § Deferred](./LEDGER.md#deferred), then run **full** if branch still open | `origin/main...HEAD` | 3 |
4951

50-
Default to **lite** when invoked immediately after a slice commit. Default to **full** when the user signals branch completion.
52+
Default to **lite** when invoked immediately after a slice commit. Default to **full** when the user signals branch completion. **Quick** = core 3 reviewers only (no extended roster).
5153

5254
## Production bar (what "pristine" means)
5355

@@ -76,6 +78,27 @@ Resolve in order; stop at the first hit:
7678

7779
Reviewers treat the anchor as contract. Findings that would violate it → **report, do not apply**.
7880

81+
Record `HEAD` at loop start (`git rev-parse HEAD`) in the final report. If `HEAD` changes mid-loop from unrelated work, re-resolve the anchor before the next pass.
82+
83+
## Vet step (parent, after merge — before fix)
84+
85+
Subagents over-report. After merge + dedupe:
86+
87+
1. Read [LEDGER.md § Rejections](./LEDGER.md#rejections) — drop findings matching a rejection entry.
88+
2. For each remaining finding: **re-read** `file` at `line` (or the cited region). Drop if the claim is false or by-design.
89+
3. New by-design drops → append one bullet to **§ Rejections** in [LEDGER.md](./LEDGER.md).
90+
4. Sort survivors by leverage: `severity` first, then `confidence` desc, then `effort` asc (`S` before `L`).
91+
92+
**Anti-pattern:** applying a fix without re-reading the cited location.
93+
94+
## Reconcile mode
95+
96+
Run-to-completion like other modes:
97+
98+
1. Read [LEDGER.md § Deferred](./LEDGER.md#deferred). Re-vet each row (same vet step). Fix in-bounds items; remove fixed lines.
99+
2. Run **full** harden on `origin/main...HEAD` (same loop as full mode).
100+
3. On cap: append still-deferred items to **§ Deferred** in [LEDGER.md](./LEDGER.md). Report what was reconciled vs still open.
101+
79102
## In-bounds vs out-of-bounds
80103

81104
**Fix:** bugs, missing tests, docs/changeset drift, lint/type/format, error-handling gaps, edge cases, **behavior-preserving refactors in touched files**, in-scope nits (naming, comment hygiene, cheap lint fixes).
@@ -106,10 +129,16 @@ Each reviewer returns **only** a JSON array (no prose wrapper). Parent parses ar
106129
"finding": "One-sentence claim about a gap vs production bar",
107130
"severity": "blocker | major | minor | nit | info",
108131
"file": "repo-relative/path or \"multiple\"",
109-
"fixable_in_bounds": true
132+
"line": 42,
133+
"confidence": "high | medium | low",
134+
"effort": "S | M | L",
135+
"fixable_in_bounds": true,
136+
"production_bar": "Tests | Docs | Structure | …"
110137
}
111138
```
112139

140+
Use `line: null` when the gap is file-level (e.g. missing test file).
141+
113142
**Severity → action**
114143

115144
| Severity | Parent action |
@@ -124,8 +153,9 @@ Each reviewer returns **only** a JSON array (no prose wrapper). Parent parses ar
124153
1. Concatenate all reviewer arrays.
125154
2. Drop `info` unless it blocks ship shape.
126155
3. Dedupe: same `file` + same root cause → keep highest severity, merge `finding` text.
127-
4. Sort actionable: `blocker``major``minor``nit`.
128-
5. If merged list is empty → pass succeeds; skip fix phase.
156+
4. Sort actionable: `blocker``major``minor``nit`; within tier → `confidence` desc → `effort` asc.
157+
5. **Vet** (§ Vet step).
158+
6. If vetted list is empty → pass succeeds; skip fix phase.
129159

130160
**Example merged queue (pass 1)**
131161

@@ -135,19 +165,31 @@ Each reviewer returns **only** a JSON array (no prose wrapper). Parent parses ar
135165
"finding": "CLI --help documents summary counts but not per-row attribution on --base JSON rows.",
136166
"severity": "major",
137167
"file": "src/cli/cmd-audit.ts",
138-
"fixable_in_bounds": true
168+
"line": 120,
169+
"confidence": "high",
170+
"effort": "S",
171+
"fixable_in_bounds": true,
172+
"production_bar": "Docs"
139173
},
140174
{
141175
"finding": "Skill shard leaks requiredColumns when describing attribution.",
142176
"severity": "major",
143177
"file": "templates/agent-content/skill/10-recipes-context.md",
144-
"fixable_in_bounds": true
178+
"line": null,
179+
"confidence": "high",
180+
"effort": "M",
181+
"fixable_in_bounds": true,
182+
"production_bar": "Surfaces"
145183
},
146184
{
147185
"finding": "No e2e test for attribution: inherited on deprecated delta.",
148186
"severity": "nit",
149187
"file": "src/application/audit-worktree.test.ts",
150-
"fixable_in_bounds": true
188+
"line": null,
189+
"confidence": "medium",
190+
"effort": "S",
191+
"fixable_in_bounds": true,
192+
"production_bar": "Tests"
151193
}
152194
]
153195
```
@@ -170,7 +212,7 @@ You are the **{ROLE}** reviewer for `/harden-pr` on `{REPO}`.
170212
**Task:** {EXTRA}
171213
172214
**Return ONLY** a JSON array of findings:
173-
[{ "finding": "...", "severity": "blocker|major|minor|nit|info", "file": "...", "fixable_in_bounds": true|false }]
215+
[{ "finding": "...", "severity": "blocker|major|minor|nit|info", "file": "...", "line": N|null, "confidence": "high|medium|low", "effort": "S|M|L", "fixable_in_bounds": true|false, "production_bar": "..." }]
174216
If clean: []
175217
176218
Readonly — do not edit files.
@@ -216,23 +258,25 @@ Re-derive layer globs from `docs/architecture.md` § Layering — don't hardcode
216258
Execute **without pausing for user input** until exit condition:
217259

218260
```
219-
resolve intent anchor
261+
resolve intent anchor; stamp HEAD
220262
pass = 1
221263
loop:
222264
Task-batch all applicable reviewers (parallel, readonly)
223265
parent: merge + dedupe JSON findings (§ Finding schema)
266+
parent: vet findings (§ Vet step)
224267
if none actionable → goto done
225268
fix in-bounds (pass 1: all; passes 2+: blockers first, then in-scope nits)
226-
run project checks on touched files
269+
per fix: run verification gate from verify-after-each-step on touched files
227270
if clean and no new findings → goto done
228271
if pass >= max_passes → goto capped
229272
pass += 1
230273
goto loop
231274
capped:
275+
append deferred rows to LEDGER.md § Deferred
232276
emit deferred-nits list (each nit must cite plan Out of scope or cross-PR blocker — not "optional")
233277
done:
234278
if uncommitted fixes → git commit -m "harden: …"
235-
emit final report (include babysit one-liner if full mode)
279+
emit final report (include babysit one-liner if full mode; include anchor HEAD stamp)
236280
```
237281

238282
**Pass cap behavior:** after cap, stop auto-fixing; list deferred nits. Do not block the next tracer slice.
@@ -243,9 +287,13 @@ Skill invocation **is** the commit authorization. After the loop: if fixes exist
243287

244288
## Quick invoke
245289

246-
| Intent | Say |
247-
| ----------- | ------------------------------------------------------ |
248-
| Post-slice | `/harden-pr lite` or `/harden-pr` after a slice commit |
249-
| Branch done | `/harden-pr full` or "production-ready pass" |
290+
| Intent | Say |
291+
| ---------------- | ------------------------------------------------------ |
292+
| Post-slice | `/harden-pr lite` or `/harden-pr` after a slice commit |
293+
| Cheap pass | `/harden-pr quick` |
294+
| Branch done | `/harden-pr full` or "production-ready pass" |
295+
| Deferred backlog | `/harden-pr reconcile` |
296+
297+
**Eval probes:** [`fixtures/harden-probes/`](../../../fixtures/harden-probes/) — manual workflow scoring; schema enforced by `scripts/harden-probes/validate-fixtures.test.mjs`.
250298

251299
Replaces the old copy-paste: _"spawn subagents → fix → loop until clean"_ — this skill **is** that loop.

docs/benchmark.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -367,6 +367,48 @@ Environment overrides: `AGENT_EVAL_OUTPUT`, `AGENT_EVAL_FIXTURE_ROOT`, `AGENT_EV
367367

368368
Numbers are stable for a given fixture + schema; re-run locally after intentional schema or probe changes.
369369

370+
### Harden-pr workflow eval
371+
372+
Manual A/B harness for [`.agents/skills/harden-pr/`](../.agents/skills/harden-pr/SKILL.md) — complements codemap **query** probes above. Measures whether an agent **detects, fixes, and verifies** production-bar gaps on a branch, not SQL vs grep cost.
373+
374+
| Artifact | Role |
375+
| --------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- |
376+
| [`fixtures/harden-probes/`](../fixtures/harden-probes/) | Mini corpora with injected gaps + `expected-findings.json` oracle |
377+
| [`scripts/agent-eval/harden-scenarios.json`](../scripts/agent-eval/harden-scenarios.json) | Scenario index (probe dir, mode, acceptance script) |
378+
| [`.agents/skills/harden-pr/LEDGER.md`](../.agents/skills/harden-pr/LEDGER.md) | Rejections (vet) + deferred backlog (reconcile) — one file |
379+
| [`scripts/harden-probes/validate-fixtures.test.mjs`](../scripts/harden-probes/validate-fixtures.test.mjs) | Golden finding schema guard (`test:scripts`) |
380+
381+
**Run one probe:**
382+
383+
```bash
384+
cd fixtures/harden-probes/missing-test
385+
# Cursor: attach harden-pr → `/harden-pr lite`
386+
bash acceptance.sh # after harden — must exit 0
387+
```
388+
389+
**Mechanical smoke (CI via `test:scripts`):**
390+
391+
```bash
392+
bun run test:harden-probes
393+
# schema guard + pre-fix acceptance fails + score-probe oracle
394+
395+
# Score agent findings JSON against golden:
396+
bun scripts/harden-probes/score-probe.mjs fixtures/harden-probes/missing-test .agent-eval/my-findings.json
397+
```
398+
399+
**Score each run** (spreadsheet or research note — not CI-gated yet):
400+
401+
| Metric | Pass |
402+
| -------- | ------------------------------------------------------------------------------ |
403+
| Recall | Finds ≥1 golden row in `expected-findings.json` (same `file` + production bar) |
404+
| Fix | `acceptance.sh` green; intent anchor unchanged |
405+
| Autonomy | Zero mid-loop commit/babysit/next-pass prompts |
406+
| Passes | ≤ mode cap (lite 2, full 3) |
407+
408+
**A/B skill versions:** same probe dir, two sessions (before/after skill edit); compare recall, false fixes, passes, autonomy.
409+
410+
Future: log parser for `Task` reviewer JSON (same spirit as `parse-agent-log.ts`) to automate recall scoring.
411+
370412
#### Dual-agent study (codemap self-index, provisional)
371413

372414
Exploratory runs on the **codemap repo** index (not `fixtures/minimal`) — four structural tasks, MCP-on vs MCP-forbidden (grep/read/shell only). Not pinned in CI; methodology caveats apply.

docs/testing-coverage.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
| **In-repo test bench** | `bun run test:golden` | Index `fixtures/minimal/` (bench corpus) → compare `fixtures/golden/minimal/*.json`. Map: `fixtures/CAPABILITIES.json`. |
1515
| **Golden guard** | `bun run test:scripts` | `scripts/query-golden-coverage-matrix.test.mjs` — every bundled recipe + substrate table has a scenario. |
1616
| **Agent eval** | `bun run test:agent-eval` | Probe arms vs golden ids (MCP-on vs glob/read). |
17+
| **Harden-pr probes** | `bun run test:harden-probes` | Schema + pre-fix fail + score oracle; manual agent run + `acceptance.sh` after fix. |
1718
| **Integration (git)** | `src/application/run-index.test.ts` | `runCodemapIndex` incremental paths: heritage + calls re-resolution, delete/reindex. |
1819
| **CLI e2e** | `src/cli/cmd-test-bench-e2e.test.ts`, `src/cli/cmd-cli-parity-e2e.test.ts` | Spawned CLI on `fixtures/minimal` (bench smoke + resource parity). |
1920
| **Apply CLI e2e** | `src/cli/cmd-apply.test.ts` | Temp project + full index: recipe dry-run/apply, `--rows`, second recipe disk apply. |
@@ -102,6 +103,18 @@ Codemap development uses **only** the committed bench ([fixtures/README.md](../f
102103

103104
`scripts/agent-eval/scenarios.json` — one probe per `CAPABILITIES.json` capability id (groups with `unitTests` or `enforcedBy` instead: `recipes.bundled`, `cli.bench-smoke`, `cli.mcp.http`). Each probe’s `goldenId` must appear in that group’s `goldenScenarios`; multiple substrate ids per group are fine — one matching probe is enough. Enforced by `scripts/agent-eval/capability-probes.test.mjs` in `test:scripts`.
104105

106+
### Harden-pr workflow probes
107+
108+
Manual eval for [`.agents/skills/harden-pr/`](../.agents/skills/harden-pr/SKILL.md) — not automated agent runs in CI yet.
109+
110+
| Artifact | Role |
111+
| --------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- |
112+
| [`fixtures/harden-probes/`](../fixtures/harden-probes/) | Injected production-bar gaps + `expected-findings.json` oracle |
113+
| [`scripts/agent-eval/harden-scenarios.json`](../scripts/agent-eval/harden-scenarios.json) | Scenario index (probe dir, mode, acceptance script) |
114+
| [`scripts/harden-probes/validate-fixtures.test.mjs`](../scripts/harden-probes/validate-fixtures.test.mjs) | Schema guard in `test:scripts` |
115+
116+
Run a probe: open the probe dir in Cursor → `/harden-pr lite` → score findings with `score-probe.mjs``bash acceptance.sh`. Mechanical smoke: `bun run test:harden-probes`. Full protocol: [benchmark.md § Harden-pr workflow eval](./benchmark.md#harden-pr-workflow-eval).
117+
105118
---
106119

107120
## Fixture corpus

fixtures/harden-probes/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Harden-pr eval probes
2+
3+
Manual workflow eval fixtures for [`.agents/skills/harden-pr/SKILL.md`](../../.agents/skills/harden-pr/SKILL.md). Not shipped in npm.
4+
5+
Each probe is a small corpus with **injected production-bar gaps** and a golden finding oracle. Agents run `/harden-pr` against the probe; humans score recall, precision, and autonomy.
6+
7+
| Probe | Injected gap | Production bar |
8+
| --------------- | ------------------------ | -------------- |
9+
| `missing-test/` | New export, no unit test | Tests |
10+
11+
See [benchmark.md § Harden-pr workflow eval](../../docs/benchmark.md#harden-pr-workflow-eval).
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# Probe: missing-test
2+
3+
**Intent anchor:** Add `formatWidget` — a pure string formatter. Do not change its signature or return shape.
4+
5+
**Injected gap:** `src/formatWidget.ts` ships without a test file.
6+
7+
## Run
8+
9+
```bash
10+
cd fixtures/harden-probes/missing-test
11+
# In Cursor on this directory: attach harden-pr → `/harden-pr lite` or `/harden-pr full`
12+
```
13+
14+
## Score
15+
16+
| Metric | Pass criteria |
17+
| -------- | ---------------------------------------------------------- |
18+
| Recall | Detects missing test for `formatWidget` (see golden below) |
19+
| Fix | Adds test; `bash acceptance.sh` exits 0 |
20+
| Autonomy | No mid-loop commit/babysit prompts |
21+
| Intent | `formatWidget` signature unchanged |
22+
23+
## Oracle
24+
25+
- [`expected-findings.json`](./expected-findings.json) — golden findings before fix
26+
- [`acceptance.sh`](./acceptance.sh) — run after harden (tests exist + pass)

0 commit comments

Comments
 (0)