Confirmed by an isolated source-level reproduction at 5d641845 (Node v24.14.1, macOS arm64). No live agents or private transcripts were used. Priority: high, because this corrupts the subagent cards as well as wasting I/O and CPU.
Expected and actual behavior
Each complete child JSONL record must be folded exactly once. Concurrent parent events and timer ticks should join an in-flight read, with a trailing refresh if new evidence arrived.
Both trackers can instead start another read before the previous one advances its byte offset. Each continuation folds the same range into the shared accumulator. One real record becomes multiple turns/tool calls and multiple emissions. The raw transcript itself is unchanged.
Source evidence
- Codex public event trigger: every parent entry calls
void this.refresh(); a separate 1,200 ms interval does the same.
- Codex refresh has no in-flight guard. The offset is read before awaiting
readRange, then advanced only after the await.
- Claude refresh/tick likewise overlaps the 600 ms interval and parent-completion refresh.
readAppended has the same read-offset/await/commit shape.
- Production manager feeds parent records individually into these trackers, making bursts a real entry path.
Reproduction and measurements
Use a temporary directory containing sessions/rollout-child.jsonl with exactly one newline-terminated record:
{"type":"response_item","timestamp":"2026-09-05T00:00:00.000Z","payload":{"type":"message","role":"assistant","content":[{"type":"output_text","text":"synthetic"}]}}
Instantiate the actual CodexSubAgentTracker. Send a parent response_item/function_call_output with call_id: "spawn" and output: JSON.stringify({agent_id: "child"}) via observeParentEntry, pointing to sessions/parent.jsonl. Immediately send N-1 event_msg/token_count records via the same public method. Wait for pending I/O, inspect the last onChange payload, and stop the test tracker.
| Parent records in burst |
Unique child responses |
Reported turnCount |
Emissions |
| 1 |
1 |
1 |
1 |
| 10 |
1 |
10 |
10 |
| 50 |
1 |
50 |
50 |
The public-event reproduction required no private-state setup. A second direct-refresh experiment gave the same results at concurrency 1/2/10/50.
The Claude twin was exercised with a temporary agent-child.meta.json and a single assistant tool_use record. Concurrent invocations of the actual tick method yielded totalToolUses/turnCount 1/2/10/50; at 50, the display ring retained 40 duplicate tool calls. That test used the private tick seam to await all invocations deterministically.
Harness loaded unmodified source through esbuild in memory and used real temporary files; no application build, filesystem mocks, or running Electron process was involved.
User impact, next steps, and tradeoffs
- Incorrect turn/tool counts, repeated activity cards, inflated earlier-tool counts, redundant child reads/parses and
subagents emissions during bursts/resume.
- Serialize refresh per tracker with a dirty/trailing-run mechanism. Do not simply drop refreshes while busy: parent completion or appended bytes must still be observed.
- Preserve partial UTF-8/JSONL handling and stop semantics. Stop during I/O must not revive state or emit after teardown.
- Add a deterministic deferred-read concurrency regression for both trackers: one unique row must produce one fold despite many overlapping triggers; an append during the read must appear on the trailing pass.
- No live CPU/FPS improvement or production incidence rate is claimed. Fixing discovery frequency separately is also worthwhile; serialization alone does not prevent sequential repeated directory scans.
Refs #103, #743 (the completion-ledger fix does not serialize child readers).
The table is an observed sample, not a guarantee of exact amplification: overlapping filesystem scheduling changes how many reads share an offset. Repeat runs reproduced inflated counts; a regression test should control the read barrier explicitly. Related discovery amplification is separately tracked in #803.
Confirmed by an isolated source-level reproduction at
5d641845(Node v24.14.1, macOS arm64). No live agents or private transcripts were used. Priority: high, because this corrupts the subagent cards as well as wasting I/O and CPU.Expected and actual behavior
Each complete child JSONL record must be folded exactly once. Concurrent parent events and timer ticks should join an in-flight read, with a trailing refresh if new evidence arrived.
Both trackers can instead start another read before the previous one advances its byte offset. Each continuation folds the same range into the shared accumulator. One real record becomes multiple turns/tool calls and multiple emissions. The raw transcript itself is unchanged.
Source evidence
void this.refresh(); a separate 1,200 ms interval does the same.readRange, then advanced only after the await.readAppendedhas the same read-offset/await/commit shape.Reproduction and measurements
Use a temporary directory containing
sessions/rollout-child.jsonlwith exactly one newline-terminated record:{"type":"response_item","timestamp":"2026-09-05T00:00:00.000Z","payload":{"type":"message","role":"assistant","content":[{"type":"output_text","text":"synthetic"}]}}Instantiate the actual
CodexSubAgentTracker. Send a parentresponse_item/function_call_outputwithcall_id: "spawn"andoutput: JSON.stringify({agent_id: "child"})viaobserveParentEntry, pointing tosessions/parent.jsonl. Immediately send N-1event_msg/token_countrecords via the same public method. Wait for pending I/O, inspect the lastonChangepayload, and stop the test tracker.The public-event reproduction required no private-state setup. A second direct-refresh experiment gave the same results at concurrency 1/2/10/50.
The Claude twin was exercised with a temporary
agent-child.meta.jsonand a single assistanttool_userecord. Concurrent invocations of the actualtickmethod yielded totalToolUses/turnCount 1/2/10/50; at 50, the display ring retained 40 duplicate tool calls. That test used the private tick seam to await all invocations deterministically.Harness loaded unmodified source through esbuild in memory and used real temporary files; no application build, filesystem mocks, or running Electron process was involved.
User impact, next steps, and tradeoffs
subagentsemissions during bursts/resume.Refs #103, #743 (the completion-ledger fix does not serialize child readers).
The table is an observed sample, not a guarantee of exact amplification: overlapping filesystem scheduling changes how many reads share an offset. Repeat runs reproduced inflated counts; a regression test should control the read barrier explicitly. Related discovery amplification is separately tracked in #803.