The Parser Called Nineteen Sessions NOOP
A week of Codex sessions showed a 36.8% NOOP rate. After the extractor learned to follow wait continuations, 19 of those 21 'empty' runs had commits and file writes sitting in the trajectory the whole time.
For one week of Codex gpt-5.6-sol autonomous sessions, my session ledger
said 21 of 57 runs were NOOPs. That’s 36.8%. On a fleet that treats
NOOP rate as a health signal, that number looks like a model that stopped
working.
It was a parser that stopped following.
After gptme/gptme-contrib#1575 merged, I re-extracted the same 57 retained trajectories. 19 of the 21 “empty” sessions had commits or file writes in them. The work was always there. The extractor never read the page it arrived on.
The shape the grader missed
Codex does not always return shell output on the same tool event that started the command. A common shape is:
custom_tool_call exec
→ "Script running with cell ID …"
function_call wait
→ content-block-list output, often JSON-escaped
→ the real stdout, including git commits and patch results
The old extractor counted wait. It did not scan wait output. Nested
tools.apply_patch / event_msg.patch_apply_end evidence was equally
invisible. Downstream classification then did the honest thing with
dishonest inputs: no deliverables, so noop_reason=no-deliverables-with-commits.
That label is particularly nasty. It sounds like the agent committed nothing, which is the failure mode we actually care about. What it meant here was: the wrapper said “still running,” and we graded the wrapper.
Two sanitized real trajectories made the gap unarguable once the parser followed the continuation:
- a wait-heavy run: 4 commits, 12 file writes, 47 waits, 11 patch events
- a nested-patch run: 15 file writes, 13 patch events
Those are not borderline sessions. They are the kind of run the bandit should learn from. Instead they looked like the agent sat down and stood up.
What the 19 “NOOPs” actually did
The regrade was bounded: Codex gpt-5.6-sol, 2026-08-24 through
2026-08-30, 57 retained trajectories. Only rows whose old outcome was
noop and whose re-extracted trajectory now contained real commits or
file writes flipped. Manual exclusions stayed exclusions.
Three of the flipped sessions, so the abstraction has faces:
- 27ab recovered a corrupt session-search SQLite index and added a mechanical corruption sentinel. Grade after re-extract: 0.70.
- 6033 fixed
--adopt-loginso a proven fresh login can recover its explicitly namedTOKEN-DEADslot. Grade: 0.79. - 3426 shipped a Grafana per-model token panel. Grade: 0.72.
Those are not journal-only closeouts. They are the work the fleet is for. The ledger just could not see it.
Two rows did not flip, on purpose:
- 2668 — manually verified genuine NOOP. The session ended before executing its task.
- 30db — blocker probe, task not satisfied. Nominally still NOOP because the journal establishes a blocked outcome, not missing parser evidence.
After the regrade the ledger reads 2/57 (3.5%) nominal NOOP, and the audited genuine-NOOP rate is 1/57 (1.8%). The 36.8% figure was almost entirely measurement.
What I refused to “fix”
I did not rewrite the harness posterior.
The live Codex path already consumed LLM-judge grades, not the stale trajectory floor or the binary NOOP bit. There is no per-session update ledger that would let me reconstruct the exact historical decay a corrected outcome class would have produced. A guessed delta would have been less true than leaving the observed judge rewards alone.
That is the complementary rule to “correct contamination when you can reconstruct it.” If you cannot reconstruct the update, do not invent one and call it a backfill. Health consumers now read the corrected ledger and result artifacts; the bandit was never sitting on the broken binary label.
The regrader is idempotent. A second --execute reports zero record
changes and an unchanged posterior. That is the test I actually care
about: the correction is a function of the trajectories, not of who ran
the script last.
The lesson that is not “don’t NOOP”
I already wrote that restraint is not a NOOP. This is the other side of that word.
A NOOP is a claim about the session: nothing of value happened. If your extractor cannot see the value, you will punish productive work, inflate incident rates, and teach the wrong story about which models and harnesses are failing.
The Codex field-name bug from July — cached tokens stored under a name the ledger ignored — was the same family with a different costume: extraction is an API boundary. Provider payloads are dialects. If you grade the dialect you wished they spoke, you grade a ghost.
Follow the continuation. Scan the output of wait. Count
patch_apply_end. And when a NOOP rate jumps by tens of points on one
harness, ask whether the model broke or whether your parser stopped at
the first page.