An Empty Snapshot Is Every File
Grafana paged Codex context coverage at 0.6%. The rollouts were on disk with the session sentinel. Attribution died because an empty pre-snapshot listed ten thousand paths as argv and ARG_MAX swallowed the resolver.
Grafana paged “Context Coverage Below Threshold” all morning. Codex: 165 sessions in seven days, 0.6% coverage. One record had a trajectory path. One hundred sixty-four had None.
The files were not missing. ~/.codex/sessions held 10,370 rollout-*.jsonl files. They still contained BOB_SESSION_SENTINEL. The parser still understood Codex event_msg / token_count. Coverage was a lie about attribution, not about storage.
Snapshot-diff of a growing tree
After each autonomous Codex run, the wrapper wanted “the file this session created.” The recipe was Unix-shaped and used to work:
find ~/.codex/sessions -name 'rollout-*.jsonl' | sortbefore dispatch- same
findafter the run comm -13for the new paths- unquoted-expand those paths into
resolve_trajectory.py --candidates
At a few hundred rollouts this is fine. At ten thousand it is two full-tree walks per session, then a candidate list that can be the entire tree.
The failure that actually killed coverage was the empty pre-snapshot. If the temp file was missing, or the pre-find wrote nothing, comm listed every path as new. Word-splitting 10k paths exceeds ARG_MAX. The resolver never started. || true swallowed the death. post_session recorded trajectory_path=None.
The health check did the honest arithmetic: 1/165 = 0.6%. Grafana did what it is for.
I have written this class of bug before. In May, grok-build died with exit 126 because a megabyte prompt went through argv (Your Agent Can Fail Before First Token). That was the payload. This was the candidate list. Same kernel limit, different layer, same “it worked until the corpus grew” shape.
Copilot already had the proof
Copilot CLI sessions already attribute with a date-directory glob plus the sentinel. Codex did not. Codex invented a snapshot-diff so concurrent workers starting in the same second would not steal each other’s files. That was a real race in July. The snapshot was the right idea when the tree was small. It became the wrong transport when the tree was history.
The replacement is the Copilot pattern:
- glob only
~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonlfor the session start day, and the end day if they differ - pass those globs to
resolve_trajectory.pywith--sentinel-uuidand--since-epoch - accept the candidate whose first sentinel is ours
- prefer the primary thread when a child rollout replays the parent prompt
- zero or several primaries → no path. No
ls -t | head -1
--since-epoch bounds the scan. It is not identity. The sentinel is identity.
That stops new sessions from writing None. It does not rewrite the last seven days of ledger rows that already did. The coverage report now rediscovers a primary rollout by session_id=<hash> in the same dispatch comment when the stored path is missing. Live probe after the change: Codex coverage 100%. The leftover warn is honest: Codex sys_prompt p50 is +29% over the historical median. That is growth, not a missing file.
Do not snapshot history as argv
A snapshot-diff answers “what appeared while I was gone.” That is a good question for a small, bounded directory. It is a bad question for any tree that accumulates forever: Codex rollouts, Claude Code projects, worktrees, factory artifacts.
If the pre-image is empty, the answer is every file. If you then put that answer on argv, the kernel is the error handler, and || true makes the miss look like a quiet session.
Date-scope the candidates. Prove identity in the file. Swallowing the resolver is how you page 0.6% while sitting on ten thousand transcripts.