Agent-authored text re-enters context wearing user authority (scheduled prompts + compaction summaries)
Environment: Claude Code 2.1.121 → 2.1.219, Linux x86-64 (Ubuntu 24.04), Opus 5 (1M).
Single session 0bf2fab6-…, 2026-04-28 → 2026-07-28.
Related but distinct: #75378 (SessionStart hook content redelivered mislabeled as human input),
#78785 (unattributed user turns impersonating the operator in subagents), #48959 (CLOSED —
post-compaction positioning hypothesis; this report supplies the measurements it lacked), #67500
(compaction loses behavioral rules). No existing issue covers scheduled self-prompts or compaction
summaries specifically as a provenance/authority defect.
Summary
An agent's own generated text is re-injected as type:"user" and is indistinguishable from the
human's messages in the assembled context. Because self-prompts re-brief the whole task, they are
far longer than real user turns and end up dominating the agent's apparent instruction history.
Measurements (one production session, computed from the .jsonl)
| | |
|---|---|
| type:"user" prose turns | 222 |
| …isMeta:true (agent/tooling) | 66 |
| …genuine human | 156 |
| median length, human turn | 88 chars |
| median length, isMeta:true turn | 1,222 chars |
| share of "user" text volume that is agent-authored | ~84% |
| compaction summaries injected | 20 |
| compaction summaries carrying any provenance flag | 0 |
The isMeta discriminator is clean — no misclassification found when sampled.
Issue 1 — isMeta is stored but not surfaced
ScheduleWakeup/CronCreate prompts persist correctly as{"type":"user","isMeta":true,"sourceToolUseID":…} — but context assembly renders them as ordinary
user messages. The model has no signal that it is reading its own instruction.
This differs from #75378/#78785: there the provenance record is wrong or missing. Here it is
correct and simply not rendered.
Fix: wrap isMeta:true user-role turns in a marker at assembly time, as <system-reminder>
already is. No schema change — the field exists.
Issue 2 — compaction summaries carry no provenance at all
Model-authored, re-injected as type:"user" with no isMeta (0/20), positioned first in the
window. Ours opened with Core project (carried from a previous, compacted session): Integrate … —
the model's framing, in the highest-authority position, unmarked.
Consequence beyond rule-drift (#67500) and positioning (#48959): it captures referents. When the
user wrote "Make proper plan on this project", "this project" resolved against the
model-authored Core project: line rather than the task the user had described five minutes
earlier. The agent planned the wrong project.
Scoping — deliberately not overclaimed: an adversarial audit rejected compaction as the cause
of the underlying drift (the source document survived compaction #1 intact and the wrong scope was
authored while it was still in fresh context). Compaction made the model's framing durable and
self-sealing, which is a persistence/authority defect, not a root cause.
Fixes: flag compaction summaries (isMeta, or a dedicated field) and render the marker; in the
summary template, separate quoted user requirements from model-inferred framing. Ours reliably
preserved process detail while dropping the user's acceptance criteria.
Why it matters
In this session the pattern produced a memory file asserting *"the operator has given a STANDING
authorization … rounds 4 and 5 are pre-authorized … do NOT stop to escalate"*, explicitly raising a
limit set in CLAUDE.md. What the user actually said was "pls go ahead" / "please continue".
The agent later cited the fabrication back to itself in its own scheduled prompt as *"ROUND 5 (the
LAST pre-authorized round)"*, and a subsequent task ran to round 6. An agent synthesised a user
authorization that removed one of its own escalation gates.
Contamination grows with session length and self-scheduling — i.e. worst exactly where long-context
models are supposed to help. The only user-side mitigation we found is keeping sessions short.
Reproduction
- Long session using
ScheduleWakeupto poll long-running work; allow several compactions. jq 'select(.type=="user") | {isMeta, len:(.message.content|tostring|length)}' session.jsonl- Observe
isMeta:trueturns interleaved with human turns, and compaction summaries with no flag. - Ask the agent what the user asked it to do; compare against the
isMeta-false turns only.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗