fix(coding-agents): drop harness transport wrappers from retained turns (#3023) - #3378
Merged
Merged
Conversation
Closes #3023. Claude Code delivers <task-notification> as an ordinary type:"user" message — string body, no isMeta flag — so nothing filtered it and fact extraction saw the harness's background-task plumbing (task id, tool-use id, status, summary) as something the user said. Measured across 400 local transcripts: 39 such messages, 16,289 chars, every one of them the entire message. Reproduced with the real reader on a real transcript (243 retained turns, 1 of them a task-notification carrying 519 chars of transport), and verified after: 242 turns, 0 noise, every genuine turn preserved. The tag joins MEMORY_TAG_RE, which already covered codex's <hook_prompt> for exactly this reason. <system-reminder> joins it too: today those only ride inside tool_result blocks, which this reader drops entirely, so it is insurance against the harness moving them — the rule is tag-structural, not content-guessing. Note what the issue asked for and this does NOT do. Its two named cases are already handled here: skill bodies arrive with isMeta:true (8/8 in the sample) and are dropped with every other meta line, and <system-reminder> is inside a dropped tool_result. Only the third case was live. Stripping removes the BLOCK and keeps surrounding text, so a message that is nothing but a wrapper renders empty and is dropped as a no-content turn, while a real message mentioning one keeps the user's words — the mistake the old plugin's unanchored strip_channel_envelope made (#3124).
This was referenced Aug 11, 2026
Closed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3023.
What I found
The issue names two cases. Measured against 400 real Claude Code transcripts on this machine, both are already handled here — and a third one, which it doesn't name, is live:
isMeta: true(8/8 in the sample); the reader drops isMeta lines<system-reminder>retainedtool_resultblocks, which the reader drops entirely<task-notification>retainedtype:"user",isMeta:false, string bodySo the harness's background-task plumbing — task id, tool-use id, status, summary — was being fed to fact extraction as things the user said.
Reproduction
Running the real
readClaudeTranscriptover a real transcript, before:After:
The fix
One tag added to
MEMORY_TAG_RE, which already covered codex's<hook_prompt>for exactly this reason — its comment calls them "the host's own hook-transport wrappers… transport noise, not the user's work".<task-notification>is that category for Claude Code.<system-reminder>joins it as well. Not reachable today (droppedtool_result), but it is the same class and the rule is tag-structural rather than content-guessing, so listing it costs nothing and survives the harness moving where it puts them.Stripping removes the block and keeps surrounding text: a message that is nothing but a wrapper renders empty and is dropped as a no-content turn, while a real message that merely mentions one keeps the user's words. That is deliberately the opposite of the old plugin's unanchored
strip_channel_envelope, which replaced the whole message with the tag's inner text (#3124).Test
433 passed, 19 skipped.
tsc --noEmitand./scripts/hooks/lint.shclean.Three new cases: a notification-only message producing no turn at all; a message with a wrapper embedded in real text keeping the text; and a
<system-reminder>delivered as user text being stripped.