-
Notifications
You must be signed in to change notification settings - Fork 2.6k
bug: post-compression file-read history injected as role=user, breaking message semantics #2224
Description
Summary
After context compression, a [Files already read in this session — do NOT re-read these] message is injected into the conversation with role="user". This creates a fake user message that the model interprets as user instructions, which can confuse state tracking and contribute to task-redo behavior.
Code location
run_agent.py lines 4348-4363, inside _compress_context():
compressed.append({"role": "user", "content": (
"[Files already read in this session — do NOT re-read these]\n"
f"{file_list}\n"
"Use the information from the context summary above. "
"Proceed with writing, editing, or responding."
)})Added in commit 9eee529a (PR #705) by @0xbyt4.
Real example
Session session_20260320_061912_0ecbf3.json, message [11]:
[10] tool: {"bytes_written": 22723} ← write_file result
[11] user: [Files already read — do NOT re-read these] ← INJECTED
[12] assistant: Let me run the unit tests...
The model sees what looks like the user giving a new instruction. Found in 7 sessions in our logs.
Why it was added
PR #705 addressed a real problem: after context compression, the agent loses track of which files it already read and re-reads them in a loop. The file-read tracking in file_tools.py (warn on 3rd read, block on 4th+) handles the detection/blocking side. The injection in _compress_context was meant to proactively tell the model which files were already read.
Why it's problematic
-
Wrong role. The message uses
role="user"but it's not from the user — it's system-generated context. The model treats it as a user instruction ("Proceed with writing, editing, or responding") which can influence behavior unpredictably. -
Breaks tool→user→assistant flow. After a tool result, this user message appears without the assistant having responded to the tool result first. The model sees: tool result → user says "don't re-read" → assistant continues. This creates a synthetic turn boundary mid-agent-loop.
-
The file_tools.py tracking already handles this. The read tracker in
file_tools.pywarns on 3rd read and blocks on 4th+. This is the correct mechanism — it's inline with tool execution, doesn't inject fake messages, and the model gets feedback at the point of action. The compression injection is redundant. -
Compression summary already describes completed work. The
[CONTEXT COMPACTION]summary at the compression boundary already describes what files were examined and what work was done. A separate file list is redundant with the summary.
Recommendation
Remove the file-read history injection from _compress_context() (lines 4348-4365). The file_tools.py read tracker (warn/block escalation) is the correct defense against re-read loops and doesn't require injecting fake user messages into the conversation.
If we want to preserve the file list information, it could be included in the compression summary itself rather than as a separate user message.