Skip to content

fix(rendering): filter subagent sidechain turns from the main feed (#477, Track A) - #479

Merged
Juliusolsson05 merged 1 commit into
integration/rendering-pipelinefrom
fix/sidechain-committed-leak
Jul 7, 2026
Merged

Juliusolsson05 merged 1 commit into
integration/rendering-pipelinefrom
fix/sidechain-committed-leak

Conversation

@Juliusolsson05

Copy link
Copy Markdown
Owner

Track A of #477 — the committed-plane half of the subagent-into-main-feed leak (the persistent one). Diagnosis: Claude interleaves a subagent's own turns into the parent transcript tagged isSidechain:true; the committed collector was structurally blind to the flag, so they painted as main-feed rows (and their inner Read/Bash got mined as committed-owned).

TDD, in the diff: the committed tests + the e2e adapter fixture were written first and failed (sidechain turn selected, its Read mined) — that failure is the captured leak. Then the fix:

  • RawCommittedEntry gains isSidechain (the field already flows through the claude codec + history.ts:71; only the session picker ever honored it).
  • collectCommittedCandidates rejects sidechain entries before mining tools, reason sidechain-filtered. Safe by construction — the same activity already renders inside the Task card via the watcher channel, so nothing is lost.
  • e2e fixture proves the field flows adapter → collector → ledger; only parent rows paint.

Confirmed live and captured in a session recording (#467) — the recorder caught the symptom, the diagnosis pinned the one-field root cause, this test-locks it.

Not here: the live proxy-plane half (Track B in #477) — subagent turns multiplexed on the shared mitmproxy stream with no isSidechain on the wire; needs flow attribution, separate PR.

Corpus unchanged; 170 rendering tests; full suite 431; both gates clean.

🤖 Generated with Claude Code

, Track A)

Subagent (Task/Agent) output was leaking into the parent's MAIN feed:
Claude interleaves a subagent's OWN turns into the parent transcript
tagged isSidechain:true, and the committed collector was structurally
blind to the flag — RawCommittedEntry didn't even carry it, so a
sidechain assistant turn (type:'assistant', not meta, text not '<')
sailed through to `selected` and painted as a plain main-feed row, with
minedToolIds even stamping its inner Read/Bash as committed-owned.
Confirmed live and captured in a session recording (#467); diagnosis and
plan in #477.

TDD: the two committed tests + the e2e adapter fixture were written
FIRST and FAILED (the sidechain turn was selected, its Read mined) —
that failure IS the leak. Then the fix:
- RawCommittedEntry gains isSidechain (the field already flows through
  the claude codec + history.ts:71; the feed just never honored it —
  only the session picker did, sessionList.ts:160).
- collectCommittedCandidates rejects sidechain entries BEFORE mining
  tools, reason 'sidechain-filtered' (new RenderReason). Safe by
  construction: the same activity already renders inside the Task card
  via the watcher channel (SubAgentState), so nothing is lost.
- e2e fixture proves the field flows adapter -> collector -> ledger and
  only the parent rows paint.

This is the COMMITTED (persistent) half. The LIVE proxy-plane half
(subagent turns multiplexed on the shared proxy stream, no isSidechain
on the wire) is Track B in #477 — needs flow attribution, separate PR.

Corpus unchanged (no fixture had sidechain entries); 170 rendering
tests; full unit suite 431; both gates clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant