The agent-eval harness reports three metrics on every run. They are not three views of one number; each answers a different question, and a retrieval change can move one without moving the others. This page says which is which, which harness to run, and how to read the output. The per-metric docs carry the derivations and the caveats — read the one that matters once a number moves.
| Metric | The question it answers | Doc |
|---|---|---|
| Residual context occupancy (CG-7) | How much of the window does this arm's retrieval still hold when the run ends — i.e. what does every following turn have to work in? | residual-context-occupancy.md |
| Explore sufficiency (CG-8) | Was a response enough? Read off what the agent did next: explored again, read a file, or answered. | explore-sufficiency.md |
| Allocation efficiency (CG-9) | Of the bytes a response spent, what share went to files the answer actually drew on? | explore-allocation-efficiency.md |
All three are harness-only: parsed out of transcripts we already write. Nothing is emitted from the product and nothing leaves the machine.
Pick by the question you are actually asking. All three metrics print in both.
Isolating a retrieval change — ab-new-vs-baseline.sh. New build (HEAD) vs
a baseline build (a git ref), both arms codegraph-on, same task. This is
the harness the three metrics were built for: with codegraph on in both arms,
every number is measuring the change rather than adoption.
RUNS=3 scripts/agent-eval/ab-new-vs-baseline.sh /tmp/codegraph-corpus/express \
"Add a charset option to res.send and wire it through" main
It builds each arm, indexes a throwaway copy of the target, pre-warms a
codegraph daemon per run, runs the task RUNS times per arm, prints the three
metric blocks under each run, and ends with the side-by-side table below. The
pre-warm is load-bearing and must not be removed: without it the agent dives
into Read/grep before codegraph finishes its ~2–3s startup, and the run measures
attach latency instead of retrieval.
With vs without codegraph — run-all.sh. Codegraph-on against an empty MCP
config. A different question: displacement and adoption, not the effect of a
change. Multi-turn is where occupancy is actually charged, so separate turns
with ||.
scripts/agent-eval/run-all.sh /tmp/codegraph-corpus/gin \
"How does gin route requests through its middleware chain?||\
Where is the 404 / no-route case handled in that same chain?"
CG_ARMS=with|without re-runs one arm without redoing the other; the comparison
table still renders against whichever arm's logs are already in $AGENT_EVAL_OUT.
A campaign — bench-readme.sh. The 7 README repos, three turns each,
RUNS per arm, through run-all.sh — so every run in a campaign carries all
three metrics. Aggregate with parse-bench-readme.mjs. One has been run:
the 2026-08-05 baseline
(sonnet, 3 turns, 4 runs/arm) — read its regime box before comparing anything to
it, and note that it is not the regime the README's table was published in.
A log you already have. parse-run.mjs <run.jsonl> [run.tN.jsonl …] prints
the three blocks for any stream-json log; --brief drops the numbered call
transcript. parse-session.mjs <project-dir> does sufficiency and allocation
for an interactive session. compare-arms.mjs <out-dir> <label>… builds the
table from logs on disk, at any time, for any labels.
Model policy, both harnesses, not negotiable: --model sonnet --effort high
on every arm, both arms the same model. Sonnet is the deliberate floor — an
affordance that lands on it generalizes up to every host; one that only works on
a stronger model does not generalize down to the agents most users have.
Each run prints its three blocks (see the per-metric docs for the shape of each), then one table puts the arms side by side:
====== ARM COMPARISON — /private/tmp/cg22/ab-express ======
new baseline
runs 3 3
behavior
duration (s) 24 [18–35] 26 [24–30]
Read 0 1
codegraph calls 2 [1–2] 2
residual context occupancy (CG-7) — tokens still resident at end of run
codegraph residual (tok) 11,549 [7,193–12,591] 10,388 [10,373–10,447]
file-access residual (tok) 231 [0–242] 1,661 [1,306–1,663]
→ retrieval residual (tok) 11,780 [7,193–12,833] 12,034 [11,753–12,051]
→ share of final context 23.3% [15.8%–24.9%] 23.8% [23.4%–23.9%]
explore sufficiency (CG-8) — pooled over every answered explore call
answered explore calls 5 6
explore again 2 40% 3 50%
Read a file we returned 0 0% 3 50%
Read a file we did not return 0 0% 0 0%
Grep/Glob 0 0% 0 0%
moved on / answered 3 60% 0 0%
explore allocation efficiency (CG-9) — share of returned bytes the answer cited
pooled efficiency 96.9% 82.0%
per-run efficiency 100.0% [92.5%–100.0%] 81.9% [81.9%–82.0%]
contamination — the CLI must never be how codegraph is reached
CLI calls that RETURNED output 0 0
CLI attempts blocked 0 0
That is the real CG-22 express pass, and it is a worked example of all three reading together: the baseline spent 18% of its envelope on a file no answer ever cited, so the agent read a file we had already returned in 3 of 6 calls and the run ended at 82% efficiency. The new build ships the right bytes — 0 of 5 in that bucket, 96.9% — for about the same residual. Occupancy alone would have called these arms equivalent.
The table is "did it move?"; the per-run blocks are "why?" Only the blocks
name the query that fell short and the file the agent went and read instead,
which is usually enough to reproduce a miss with probe-explore.mjs.
The sufficiency buckets are chosen so each maps to a distinct fix, and two of them tie directly to the other metrics:
Read a file we returned → allocation: right file, wrong bytes. Expect
allocation efficiency to be soft on the same runs, and note the asymmetry —
efficiency scores a cited file at 100% of its section even if the agent then
had to read it for the part we clipped. This bucket is what catches that.Read a file we did not return / Grep/Glob → recall: the file never
surfaced. Allocation efficiency cannot see this at all; the envelope was
simply missing something.explore again → ambiguous by construction. It says the response did not
answer, not whether that was allocation or recall. The follow-up query
usually says which.moved on / answered → sufficient, which is not the same as correct.Efficiency is not value, and occupancy is not sufficiency. A response can be 100% efficient and useless — one small file the answer names in passing — and a small residual is only good if the answer was still right. Read all three, which is the point of wiring them into the same run.
Each metric's doc has the full list. These are the ones that change how you should read the table itself:
CG_WINDOW_TOKENS overrides it). Window size, system
prompt, and compaction policy all differ elsewhere. The ratio between the
arms is the part that travels; the percentages are not a claim about Cursor.cat through Bash.median [min–max] for exactly this reason: report
the range. Use RUNS>=2, and a campaign for a verdict.Task subagent has its
own window and only its summary returns. Sufficiency does follow the
subagent thread (a delegation is judged by what the subagent did first), so
the two metrics treat delegation differently on purpose.base. codegraph_explore is
deferred: ToolSearch pulls the schema in later, and that injection is not a
tool result. The fixed-overhead line prices the part present from the start.Both harnesses run every arm with the codegraph CLI blocked: a PATH with the
binary symlinked out, plus a PreToolUse hook that blocks absolute-path
invocations (no-cli-shim.sh, shared by both). Both layers exist because both
were needed — an agent denied codegraph on PATH ran find / -iname
"*codegraph*" and invoked it by absolute path.
The contamination row is the detection half, and it is not redundant with the prevention half: prevention fails silently the next time the binary lands somewhere new.
CLI attempts blocked is benign — the agent tried, nothing entered the window.
CLI calls that RETURNED output is not.
node scripts/agent-eval/parse-run.mjs --selftest # 68/68
Covers all three metrics over synthetic transcripts with known answers: the occupancy math (calibration, eviction, compaction), every sufficiency bucket plus the same-message / thread / delegation rules, and the allocation citation channels with their guards. See each metric's doc for the case list.