fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(orchestration): Preserve Claude subagent attribution after settle - #5388

Closed
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution
Closed

fix(orchestration): Preserve Claude subagent attribution after settle#5388
mwolson wants to merge 222 commits into
pingdotgg:t3code/codex-turn-mappingfrom
mwolson:fix/claude-subagent-postsettle-attribution

Conversation

@mwolson

@mwolsonmwolson commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Persist Claude native-task and tool-use attribution across settled root turns
    and fresh buffered continuations.
  • Keep child narration, tools, results, and distinct terminal output in child
    threads without changing ordinary root assistant text.
  • Replace raw task summaries in provider-owned root continuations with a fixed
    generic Claude prompt, preserve other providers' requested detail, and hide
    automatic provider entries from queue controls.

Problem and Fix

Problem and Why it HappenedFix
Claude child SDK frames can arrive after the root turn settles. The fresh continuation no longer has the launch turn's tool-use maps, so child text, tools, and results can leak into the root thread.Keep task identity, launch and resume aliases, bounded registration-race buffering, and resume state together in native-thread-scoped session state. Every attributed child frame resolves through that durable state.
A boolean or one-result latch cannot distinguish streamed narration, an equivalent notification fallback, and a genuinely distinct terminal Agent result across resumes.Track bounded native identities and normalized terminal equivalence per generation. Equivalent fallback text is suppressed, while distinct narration, summaries, and terminal results survive once.
task_notification.summary was used as the provider-owned Claude continuation input and could render as raw child text labelled as another agent's message. A shared override would also erase Codex background-command context.Give Claude an explicit fixed root text, Background task completed., while preserving provider-specific continuation detail for Codex. Omit provider-owned automatic entries from visible queue controls.
Unknown attributed lineage must not silently become root output.Buffer a fixed number of lineages and frames until task registration. Overflow is logged and dropped child-side, never projected into the root.
Multiple pending Agent launches cannot safely recover an omitted task_started.tool_use_id by arrival order.Infer only a single unambiguous launch. Ambiguous siblings wait for an explicit SDK alias, which atomically binds the task, preserves launch identity, and drains its child buffer.
Agent launch results and child approval requests can arrive before task_started registers their alias. A structured agentId can also appear in ordinary root tool output.Remember bounded launch identities, route known native task IDs directly, fail closed for unknown child-looking approvals, and prefer an existing root tool call before interpreting incidental agentId data.

Defensive Fixes

Problem and Why it HappenedFix
A failed continuation dispatch without a buffered terminal result left a native-thread-wide drain marker. A later sibling completion on the same Claude session could be mistaken for stale output and failed.Record the exact task IDs represented by the failed offer. Stale frames for those tasks fail closed, known sibling tasks clear the drain and continue normally, and native-process replacement clears the scoped state.
A sibling completion could append after dispatch failed but before failIfCurrent, where a live-buffer rescan pulled the sibling into the failed cohort.Freeze the failed cohort at offer time and replay later frames through normal wake buffering, so appended siblings receive their own continuation and are never projected failed.
A declined or cancelled Agent approval remained in the per-turn launch-inference queue, making a later alias-free valid launch look ambiguous.Remove only the rejected approval from pending per-turn inference while retaining its durable session launch identity.
Native query replacement, interrupt timeout, or session disposal could delete Claude's registry while durable child projections still said running.Fail-close every still-running native child with one static session-ended artifact before deleting aliases or process state. Already-terminal children remain unchanged.
An unresolved Agent result inside a multi-result SDK frame buffered the whole frame and returned, so later sibling results could be delayed or replayed more than once.Split buffered results into one-block messages and continue through the batch, preserving independent lineage even when aliases arrive in reverse order.
A split multi-result user frame could retain the original frame-level tool_use_result, then incorrectly apply that shared structured result when one buffered block replayed.Remove frame-level structured output from split messages and derive each replayed result from its own content block.
The bounded pending Agent launch queue could evict an unresolved launch while leaving implicit alias inference enabled, allowing a later alias-free start to claim the final retained sibling alias.Disable implicit launch-alias inference for the rest of the turn after overflow. Explicit SDK aliases remain authoritative and drain only their own child buffer.

Validation

  • The final MCP, Claude adapter, provider continuation, replay, and
    client-runtime set passed 189 tests with 1 skipped.
  • vp check: passed all 2,690 files.
  • vp run typecheck: passed all 15 packages.
  • A fresh isolated v2.1 Nightly AppImage built from the deliberate final union
    passed the exact packaged Grok post-settlement subagent, Claude
    post-settlement subagent, and Codex background-command wake scenarios. Claude
    projected one generic provider-owned root input while keeping raw child
    output on the child; Grok and Codex retained their provider-specific
    behavior.
  • Codex GPT-5.6 Luna identified the incidental root agentId collision. The
    regression was fixed and covered. Fresh Grok 4.5 high-reasoning source and deliberate-integration reviews
    returned SHIP after verifying teardown ordering, exactly-once terminals,
    semaphore safety, independent batched-result lineage, and fail-closed pending-launch overflow.

Note

High Risk
Large Claude adapter lifecycle refactor touching attribution, approvals, wake buffering, and continuation failure paths—behavioral regressions could mis-route subagent output or drop completions.

Overview
Fixes post-settlement leakage where Claude SDK frames for native tasks and tool use could land on the root thread after a turn settled and a fresh buffered continuation started, because launch-turn tool maps were gone.

makeClaudeAdapterV2 now holds native-thread-scoped state (task IDs, launch/resume aliases, bounded pre-registration buffering, resume/generation tracking) so child narration, tools, results, and approvals route to child threads with generation-based dedup and serialized SDK handling. Unknown lineages buffer or fail closed instead of becoming root output; launch-alias inference is single-candidate only, with overflow and session teardown paths tightened (including failIfCurrent cohorts on failed continuation dispatch).

Provider continuations: Claude root wakes use a fixed Background task completed. via optional messageText (not raw task_notification.summary); other providers keep their detail. ProviderContinuationService invokes failIfCurrent on dispatch failure. deriveThreadQueueWorkflowState omits provider-owned automatic completion messages from visible queue controls.

Reviewed by Cursor Bugbot for commit a45acc5. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Preserve Claude subagent attribution and approval routing after turn settlement

  • Enables approvals to originate from subagents by routing permission requests to the correct child thread when callbackOptions.agentID is present; unknown agent requests are denied without side effects.
  • Replaces per-turn subagent maps with a session-scoped Ref<ClaudeSessionSubagentState>, enabling durable aliasing between toolUseId and taskId across turn boundaries and preventing attribution loss after settlement.
  • Serializes SDK message handling under a Semaphore to prevent races between buffered wake frame drains and incoming frames.
  • Deduplicates emitted subagent text and terminal result artifacts using normalized text and native item IDs; adds failClaudeSubagent to emit failure terminals when wake delivery fails.
  • Hides provider-buffered automatic continuation messages ("Background task completed.") from the visible thread queue in deriveThreadQueueWorkflowState.
  • Risk: turn finalization now atomically claims the active turn to prevent duplicate terminal emissions, which changes the timing of teardown for steered and interrupted turns.

Macroscope summarized a45acc5.

@coderabbitai

coderabbitaiBot commented Aug 5, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 15ae4641-4c62-4546-8ffd-d4515e7c864c

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actionsgithub-actionsBot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XXL 1,000+ changed lines (additions + deletions). labels Aug 5, 2026

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Effect Service Conventions review: one convention issue found in the newly added test module's service imports. Everything else in the changed Effect code (subpath namespace imports, layer construction, dependency acquisition via yield* Service, error modeling) matches the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/orchestration-v2/CheckpointCaptureService.test.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@macroscopeapp

macroscopeappBot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Needs human review

Diff is too large for automated approval analysis. A human reviewer should evaluate this PR.

You can customize Macroscope's approvability policy. Learn more.

@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38aefa4 to f1d3119CompareAugust 5, 2026 06:30
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f1d3119 to 7370ca6CompareAugust 5, 2026 06:58
Comment threadapps/server/src/mcp/OrchestratorMcpService.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 7370ca6 to 1a18bbaCompareAugust 5, 2026 08:04

@macroscopeappmacroscopeappBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changed Effect service code against the service conventions. One import-shape issue in the new server test; everything else (namespace imports from effect/* subpaths, Context.Service shapes, error modelling, and dependency acquisition in the touched orchestration/adapter code) looks consistent with the conventions.

Posted via Macroscope — Effect Service Conventions

Comment threadapps/server/src/mcp/OrchestratorMcpService.test.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 1a18bba to f80ad36CompareAugust 5, 2026 08:26
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from f80ad36 to 38e7184CompareAugust 5, 2026 10:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 38e7184 to 49162a7CompareAugust 5, 2026 11:06
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 49162a7 to fe0878fCompareAugust 5, 2026 11:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from fe0878f to 8e4dc09CompareAugust 5, 2026 12:24
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 8e4dc09 to 905b5a1CompareAugust 5, 2026 12:40
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts Outdated
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from e24b59c to 4213ac5CompareAugust 5, 2026 14:35
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 905b5a1 to 48f545dCompareAugust 5, 2026 14:38
juliusmarmingeand others added 16 commits August 10, 2026 18:05
- Port thread pinning (pingdotgg#5312) into the orchestration-v2 command pipeline:
thread.pin/unpin commands, thread.pinned/unpinned events, pinnedAt on the
v2 thread state and projected shells, promotion semantics (pin clears
settle/snooze, settle clears pin) matching the v1 decider, and client
pin/unpin operations in the v2 dispatch style.
- Port the regenerated-title context anchoring (pingdotgg#5365) into
ThreadTitleRegenerationService: pin the first user message ahead of the
retained tail when the digest is truncated.
- Re-apply the right-panel controls positioning from pingdotgg#5260 to the v2
ChatView title bar controls.
- Repair merge artifacts: committed conflict markers in BranchToolbar,
duplicate capability keys, duplicate CommandPalette import, v1 turn
naming in DiffPanel's focus-refresh effect, onSend signature merge.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Match progress button spacing and single-line height to static git actions
rerere replayed stale resolutions during the rebase and committed nested
conflict markers in several files. Restore the branch-intended v2 shapes
and re-graft main's compatible additions (pending-card opacity comments,
theme-editor keybinding test, mobile scroll re-arm effects from pingdotgg#5566).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eysetIndex
Main owns migration numbering: 037_ProjectionTurnsKeysetIndex landed on
main (pingdotgg#5493), so the v2 migrations shift from 037-045 to 038-046.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Native subagent observability (pingdotgg#5219), wired per its spec's v2 merge plan:
- getWorkflowScript RPC re-homed onto the v2 WS surface (contracts, rpc
group, ws handler, auth scope, client atom).
- AgentsPanel fed by the spec's mapper swap: projectedSubagentsToRuntime
maps orchestration-v2 subagent entities into the panel model;
deriveAgentPanelModel's v2Projection leg is now live and the v1 fold
never runs. Agents surface wired into ChatView + RightPanelTabs.
Other ports and reconciliations:
- Shell reconnect-loop fix (pingdotgg#5561) ported into the v2 shell sync
(same-session resubscribes resume from the in-memory cursor), with the
cursor-resume regression test adapted to v2 fixtures.
- Mobile end-follow latch (pingdotgg#5566) ported onto the v2 ThreadFeed.
- Claude ede_diagnostic interrupt classification (pingdotgg#5557) ported into
ClaudeAdapterV2 (aborted_tools/aborted_streaming => interrupted; CLI
telemetry never becomes the failure banner). pingdotgg#5559 needs no v2 port
(unknown system subtypes are already ignored).
- Plan sidebar removed from the v2 ChatView/ChatComposer per main's
plans-fold-into-chat rework (pingdotgg#5558); rightPanelStore stays at main's
surface set.
- SettingsPanels rebuilt as main's refactored version plus the branch's
composer-context setting; sidebar snooze respects the time format
(pingdotgg#4438 follow-through).
- v1-only leftovers deleted: zombie v1 adapters/ingestion/tests the v2
rewrite removes, the v1-bound transfer-budget CI harness (pingdotgg#5350, needs
a v2 rebuild), and main's v1 client pagination machinery (pingdotgg#5493 client
side; the 037 keyset migration is kept — server-side v2 windowing is a
follow-up).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The rebase kept the LegendList 3.3.3 upgrade and patch from pingdotgg#5449 and the
mobile end-follow latch from pingdotgg#5566, but the v2 MessagesTimeline/ChatView
still carried the branch's blunt any-gesture-breaks-follow listeners.
Port main's pingdotgg#5566 web mechanics onto the v2 follow architecture:
- resolveTimelineIsAtEnd measures the 40px follow re-arm band from real
geometry (contentLength/scroll/scrollLength minus the composer inset),
keeping the isNearEnd fallback for older state shapes.
- Follow now breaks only on gestures that can actually leave the live
edge: upward wheel with overflowing content, touch drags that exited
the end band, scrollbar drags vs content clicks, and keyboard
navigation (PageUp/Home/ArrowUp) — previously keyboard scrolling never
broke follow and the next stream chunk yanked the view back down.
- Listener attach retries across frames so a thread switch cannot mount
the list without its opt-out listeners.
Deliberately not ported: pingdotgg#5449's shouldRestorePosition disclosure
anchoring and follow-gated maintainScrollAtEnd — the v2 timeline keeps
maintainScrollAtEnd={false} with its own follow scrolls and anchor
system; flipping that core is a separate change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…gdotgg#5449)
Complete the pingdotgg#5449 architecture on the v2 timeline, following the
LegendList author's direction to lean on the list's native mechanisms
instead of app-side scroll layers:
- maintainScrollAtEnd is enabled and owned by LegendList, gated off only
while the user reads history (liveFollowEnabled), while a sent turn
anchors near the top (anchoredEndSpace), or during the two-frame settle
of a fold toggle.
- maintainVisibleContentPosition compensates size changes natively
({data, size, shouldRestorePosition}); fold toggles anchor compensation
to the toggled row via a disclosure anchor key, so the trigger stays
under the pointer instead of the viewport chasing the end.
- ChatView's hand-rolled streaming follow (double-rAF scrollToEnd on
every data change) is gone; the app now only owns streaming
adjustments during anchored-end-space mode, mirroring main.
- timelineLiveFollowEnabled state mirrors the follow refs so the
render-visible gate switches native follow off when a gesture breaks
follow and back on when the viewport returns to the end band.
Timeline tests updated to assert the native-ownership invariants.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Keep success feedback visible in the Git action control for 10 seconds
- Move the running elapsed timer into the panel menu slot
…s with v2
Post-rebase reconciliation sweep:
- Sidebar: main's folded Sidebar.tsx/Sidebar.logic.ts adapted to v2 shells
(latestRun/runtime naming, waiting status instead of monitoring), with
subagent-thread filtering and main's pinned-reorder helpers re-exported
- Pinned drag reorder (pingdotgg#5581) ported into v2: thread.pin orderKey +
thread.pin.reorder command, thread.pin-reordered event, Orchestrator fold,
ProjectionStore/Maintenance, client-runtime commands and shell mapping
- Project favicon (pingdotgg#4849-era) and defaultThreadEnvMode flowed through v2
contracts (OrchestrationProjectShell, application event payloads)
- ChatView: main's pingdotgg#5592 header props, pull-request right-panel surfaces,
liveAgentCount badge (pingdotgg#5745) wired into the v2 panel layout
- enableAssistantStreaming -> enableLegacyTokenStreaming rename applied to
v2 RunExecutionService and replay testkit
- Removed v1 zombies resurrected by the rebase (provider service/reaper/
ingestion + v1 layer tests, server.test.ts, integration harness)
- routeTree: main's tree + branch's /settings/scheduled-tasks route
- Misc marker-sweep syntax repairs (rpc.ts, entities.ts, localApi.test.ts,
rightPanelStore.test.ts, GitManager.test.ts, mobile model menu helpers)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… 038-040
Main released ProjectionThreadsPinOrderKey (038),
ProjectionProjectsDefaultThreadEnvMode (039) and
ProjectionProjectFaviconPath (040), so the branch-private v2 stack shifts
up by three. Registry ids were already 41-49; this renames the files and
identifiers to match and updates the ledger expectations and through-id
boundaries in the migration tests (released boundary 37 -> 40).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a6c9b41 (agents open pasted images): ClaudeAdapterV2 now grants the
attachments dir alongside cwd via additionalDirectories and appends
'[Attached ... is saved at: path]' lines to the turn text so tools can
dereference pasted images (pixels alone are not tool-readable).
- 5bb8c03 (settle leaves monitors running): thread.settle now joins
archive/delete in the provider-session detach set, so PR monitors, dev
servers and subagent fleets stop when the user parks the thread. The
settle guard already rejects active runs, and serialized dispatch closes
the re-engage race the v1 fix handled with onlyIfSettled.
- e70cdb4 (Claude resume handshakes) and 2c7267a (reaper vs live
background subagents) are already covered structurally in v2: results
are turn-scoped with explicit zero-turn handshake drops, and idle
release is pinned while background work is pending.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…e panel-visibility merge
The keep-both merge nested main's plan-surface migration test inside a
branch popover test and dropped the threadPanelVisibilityByThreadKey key
from the migration results. Restore main's test body and include the
branch's (empty) visibility map in the expected persisted shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n text
Follow-up to the pingdotgg#5757 port: start and steer turns now append the
'[Attached ... is saved at: path]' line, so the adapter fixtures assert it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 22bd872 to a27c1ccCompareAugust 10, 2026 17:05
The round-3 reconciliation took main's ChatHeader wholesale and wired its
full prop set, resurrecting the scripts/open-in/git-actions cluster the
branch had deliberately relocated into the thread panel. Restore the
79-line slim header (project favicon + name + thread title) and its
minimal ChatView call. pingdotgg#5592's header actions stay a documented v2
follow-up, as decided in round 2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
mwolson added a commit to mwolson/t3code that referenced this pull request Aug 10, 2026
Re-apply pingdotgg#4547 stranded-output continuation and the full mid-tool steer
handoff path that pure pingdotgg#5388 checkout dropped on the CTM pin move.
- Add ready, merge, and conflict-resolution actions to the PR row
- Share pull request action and handoff logic with the detail panel
- Fix thread details scrolling and row alignment
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from a593d5c to 572e689CompareAugust 11, 2026 13:37
Comment threadapps/server/src/orchestration-v2/Adapters/ClaudeAdapterV2.ts
@mwolson
mwolsonforce-pushed the fix/claude-subagent-postsettle-attribution branch from 572e689 to a45acc5CompareAugust 11, 2026 13:51
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from 72e3863 to a186d64CompareAugust 11, 2026 17:07
@juliusmarminge
juliusmarmingeforce-pushed the t3code/codex-turn-mapping branch from a186d64 to 5b1a115CompareAugust 12, 2026 23:19
@mwolson

Copy link
Copy Markdown
ContributorAuthor

Absorbed into #5456 claude-empty-prompt. That PR now carries this commit plus the blank opening-message fix on current t3code/codex-turn-mapping. Review the Claude work there.

@mwolsonmwolson closed this Aug 13, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL1,000+ changed lines (additions + deletions).vouch:unvouchedPR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants

@mwolson@juliusmarminge@maria-rcks@PixPMusic@nsxdavid@Yusuf007R