Skip to content

bug: post-compression file-read history injected as role=user, breaking message semantics #2224

@teknium1

Description

@teknium1

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

  1. 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.

  2. 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.

  3. The file_tools.py tracking already handles this. The read tracker in file_tools.py warns 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.

  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions