Skill: trace-grader
Grade an agent's trajectory, not just its output. A run can return the right
answer via a bad path (force-pushed, retry-stormed, edited a file it never read) —
"corrupt success" — and it can be flaky (passes once, fails on rerun). An
answer-only checker is blind to both; TraceGrader inspects the path.
When to use
- You have one or more agent runs (traces) and want to know how the answer was
reached, not only whether it was reached.
- You need a repeatability gate: "does this task pass on ALL k reruns?" (
pass^k).
- You want a deterministic, CI-gateable signal on tool choice / ordering / retries
/ step budget — before reaching for an LLM judge.
What it is (build-now vs adapter)
Built now — deterministic, no unknowns:
- Trace schema (the contract): a run = list of steps
{step, tool, args, observation, ok}.
- Parsers: canonical
traces.json; the audit jsonl shape written by
llmwiki/.claude/hooks/hooklib.audit() (and code-logger events.jsonl); OR a full
Claude Code session transcript (--transcript) — the richest source: it recovers
retrieval args (Grep pattern, Read file_path), observation, and per-step ok,
so grounding + outcome are observable. Sidechain (sub-agent) lines are skipped; a
transcript carries no task-verdict so its run.ok is forced True — trust the FLAGS.
pass^k: k runs of one task → require ALL k to deliver; report pass^k.
- Rule checks (config-driven):
forbidden_tool (high), out_of_order (medium,
via must_precede), retry_storm (medium, >N consecutive same-tool failures),
excessive_steps (low, > budget), edited_without_read (medium — grounding proxy:
an Edit/MultiEdit to a file never Read/Grep'd/Write-authored first; gated by
grounding.enabled).
- Verdict per run:
clean-pass / pass-with-warnings / corrupt-success
(delivered and a high-severity flag) / fail (not delivered).
Quarantined behind ONE adapter — harness/trace-grader.config.yaml, verified:false:
- the rule PARAMETERS (forbidden list, retry threshold, step budget, order
constraints) — project-specific best-guesses, each
# ASSUMPTION;
- the inferential agent-as-judge axis (task-completion / tool-rationale /
planning rubric) —
judge_trajectory() is a STUB, not called until a judge
model is configured and the file is flipped verified:true.
Adapting later = edit that one file, never the engine.
How to run
# bundled 3-case self-test (clean / corrupt / flaky)
python3 harness/scripts/trace-grader.py --self-test
# grade canonical traces, an audit log, or a full session transcript; --json for machine output
python3 harness/scripts/trace-grader.py --traces traces.json
python3 harness/scripts/trace-grader.py --audit .claude/audit/<date>.jsonl --task my-task
python3 harness/scripts/trace-grader.py --transcript ~/.claude/projects/<proj>/<session>.jsonl --task my-task
Exit code is 3 if any run is corrupt-success or any task is flaky (so CI can gate).
Steps
- Collect traces. Either emit
traces.json in the schema above, or point
--audit at a hooklib.audit() jsonl (one run per session_id).
- Set the boundary. Edit
harness/trace-grader.config.yaml ONLY — forbidden
tools, retry_threshold, step_budget, order_constraints. Keep it the single
place these values live.
- Grade. Run the grader; read per-run verdicts + flags + the
pass^k block.
- Act on signal.
corrupt-success = right answer, bad/unsafe path → investigate
the flagged step. pass^k = FAIL = flaky → not safe to rely on.
- (Later) verify the adapter. Validate thresholds against real traces, then flip
verified:true. Only then consider enabling the agent-as-judge axis.
Rules
- The config file is the ONLY place rule parameters live. Same guess in two places = a leak.
- Never present a guessed threshold as verified — the file stays
verified:false until validated.
- The agent-as-judge axis is OFF and uncalled until configured + verified; never fake an inferential score.
- The audit parser cannot see per-step
ok/observations, so retry_storm won't fire from a pure
audit log — use full traces.json when you need failure-aware checks.
Files
| File |
Role |
harness/scripts/trace-grader.py |
deterministic engine: schema, parsers, pass^k, rule checks, report, judge STUB |
harness/trace-grader.config.yaml |
the ONE adapter (verified:false) — every value a # ASSUMPTION |
harness/tests/trace-grader.fixtures.json |
self-test fixtures: clean / corrupt-success / flaky |
1---2name: trace-grader3description: Score the PATH an agent took (tool choice, ordering, retries, repeatability, grounding) — not just its final answer — to catch "corrupt success" (right answer via a bad/unsafe path) and flakiness. Deterministic trace schema + pass^k repeatability + config-driven rule checks (forbidden tool, out-of-order, retry-storm, excessive steps, edited-without-read grounding). Parses canonical traces.json, hooklib.audit / code-logger events.jsonl, OR a full Claude Code session transcript (--transcript, richest source: recovers retrieval args + observation + per- step ok). Trigger when the user says "grade the trajectory", "score the path not the answer", "did it cheat / take a bad path", "did it look before it acted / grounding", "corrupt success", "is this flaky / does it repeat", "pass^k", "grade a session transcript", "trace grader", or invokes /trace-grader.4---56# Skill: trace-grader78Grade an agent's **trajectory**, not just its output. A run can return the right9answer via a bad path (force-pushed, retry-stormed, edited a file it never read) —10"corrupt success" — and it can be flaky (passes once, fails on rerun). An11answer-only checker is blind to both; TraceGrader inspects the path.1213## When to use14- You have one or more agent runs (traces) and want to know *how* the answer was15 reached, not only *whether* it was reached.16- You need a repeatability gate: "does this task pass on ALL k reruns?" (`pass^k`).17- You want a deterministic, CI-gateable signal on tool choice / ordering / retries18 / step budget — before reaching for an LLM judge.1920## What it is (build-now vs adapter)21Built now — deterministic, no unknowns:22- **Trace schema (the contract):** a run = list of steps `{step, tool, args, observation, ok}`.23- **Parsers:** canonical `traces.json`; the audit jsonl shape written by24 `llmwiki/.claude/hooks/hooklib.audit()` (and code-logger `events.jsonl`); OR a full25 Claude Code **session transcript** (`--transcript`) — the richest source: it recovers26 retrieval args (Grep `pattern`, Read `file_path`), `observation`, and per-step `ok`,27 so grounding + outcome are observable. Sidechain (sub-agent) lines are skipped; a28 transcript carries no task-verdict so its `run.ok` is forced True — trust the FLAGS.29- **`pass^k`:** k runs of one task → require ALL k to deliver; report `pass^k`.30- **Rule checks (config-driven):** `forbidden_tool` (high), `out_of_order` (medium,31 via `must_precede`), `retry_storm` (medium, >N consecutive same-tool failures),32 `excessive_steps` (low, > budget), `edited_without_read` (medium — grounding proxy:33 an Edit/MultiEdit to a file never Read/Grep'd/Write-authored first; gated by34 `grounding.enabled`).35- **Verdict per run:** `clean-pass` / `pass-with-warnings` / `corrupt-success`36 (delivered **and** a high-severity flag) / `fail` (not delivered).3738Quarantined behind ONE adapter — `harness/trace-grader.config.yaml`, `verified:false`:39- the rule PARAMETERS (forbidden list, retry threshold, step budget, order40 constraints) — project-specific best-guesses, each `# ASSUMPTION`;41- the inferential **agent-as-judge** axis (task-completion / tool-rationale /42 planning rubric) — `judge_trajectory()` is a STUB, **not called** until a judge43 model is configured and the file is flipped `verified:true`.4445Adapting later = edit that one file, never the engine.4647## How to run48```bash49# bundled 3-case self-test (clean / corrupt / flaky)50python3 harness/scripts/trace-grader.py --self-test5152# grade canonical traces, an audit log, or a full session transcript; --json for machine output53python3 harness/scripts/trace-grader.py --traces traces.json54python3 harness/scripts/trace-grader.py --audit .claude/audit/<date>.jsonl --task my-task55python3 harness/scripts/trace-grader.py --transcript ~/.claude/projects/<proj>/<session>.jsonl --task my-task56```57Exit code is `3` if any run is `corrupt-success` or any task is flaky (so CI can gate).5859## Steps601. **Collect traces.** Either emit `traces.json` in the schema above, or point61 `--audit` at a `hooklib.audit()` jsonl (one run per `session_id`).622. **Set the boundary.** Edit `harness/trace-grader.config.yaml` ONLY — forbidden63 tools, `retry_threshold`, `step_budget`, `order_constraints`. Keep it the single64 place these values live.653. **Grade.** Run the grader; read per-run verdicts + flags + the `pass^k` block.664. **Act on signal.** `corrupt-success` = right answer, bad/unsafe path → investigate67 the flagged step. `pass^k = FAIL` = flaky → not safe to rely on.685. **(Later) verify the adapter.** Validate thresholds against real traces, then flip69 `verified:true`. Only then consider enabling the agent-as-judge axis.7071## Rules72- The config file is the ONLY place rule parameters live. Same guess in two places = a leak.73- Never present a guessed threshold as verified — the file stays `verified:false` until validated.74- The agent-as-judge axis is OFF and uncalled until configured + verified; never fake an inferential score.75- The audit parser cannot see per-step `ok`/observations, so `retry_storm` won't fire from a pure76 audit log — use full `traces.json` when you need failure-aware checks.7778## Files79| File | Role |80|------|------|81| `harness/scripts/trace-grader.py` | deterministic engine: schema, parsers, pass^k, rule checks, report, judge STUB |82| `harness/trace-grader.config.yaml` | the ONE adapter (`verified:false`) — every value a `# ASSUMPTION` |83| `harness/tests/trace-grader.fixtures.json` | self-test fixtures: clean / corrupt-success / flaky |