Thread transfer impact
⚠️ The latest CI run did not produce a thread transfer result for a9dc2e3.
This comment will update automatically after the next completed run.
Fixes #15136.
On e27a03e94d, moving a Claude child with t3_worktree_handoff fails its first run and leaves continuation run 2 queued with queueHeld=true and no start time. The Codex control continues and writes its file.
Evidence pending. Before runtime transcript.
Implementation, scope assessment for the related stale handoff banner, and after evidence pending.
Model and harness: GPT-6.1-Sol through Codex in T3 Code.
Head captured: after = ab1166d777 (this branch with main merged in), before = 8d3b061121 (main, the merge base). The PR head, 2251684173, only adds the driver, apps/server/scripts/verify-worktree-handoff-live.ts; the server code is identical to ab1166d777.
Fixture matrix (proof audit pass 1): audit1-matrix.md. A Claude child never shares its parent's provider session, so the bug only shows when the thread that calls t3_worktree_handoff is on Claude. R1 is the child handing itself off (the issue repro); R1b is the brief's literal "parent hands off while its child runs".
| Row | Before (8d3b061121) | After (ab1166d777) |
|---|---|---|
R1: Claude child calls t3_worktree_handoff | Child run 1 failed. Server logs ProviderAdapterEventStreamError … Workspace changed.. Continuation run 2 stays queued, queueHeld:true, never starts. Parent receives failed. | Child run 1 interrupted. Run 2 completed ("DONE") on a new session whose cwd is the worktree. Parent receives completed. No ingestion warnings. |
| R2: user interrupts the child's continuation | Run 2 never starts, so it can't be interrupted. The driver cancels the held queued run instead; the parent already had failed. | run.interrupt → run 2 interrupted 0.67 s after it started. Task interrupted, delivered once, no hang. |
| R3: top-level Claude thread hands off, no child | Run 1 failed, continuation held. | Run 1 interrupted 67 ms after detach, continuation completed in the worktree. (Claude changes here too; only Codex is unchanged.) |
| R1b: Claude parent hands off while its Claude child runs | Parent run 1 failed, continuation held. The child's result still arrives as parent run 3. | Parent run 1 interrupted, continuation completed, then the child's result arrives and is acknowledged. |
After timings: 51–376 ms from detach to the turn being interrupted; 0.98–2.06 s from detach to the new worktree session. The new 30 s terminal wait was never close to being used.
Captures (ghdrop upload -m output):
captures.md
captures-pr-22825.tar.gz
r1-before-child-timeline.txt
r1-after-child-timeline.txt
Environment: fs-33 (Linux 6.1, x86_64), Node v26.12.1. A real dev server per side with isolated --home-dir under /tmp and a throwaway git repo per side. Real Claude turns (claude-sonnet-5-5, effort low) through the CLIProxy hub; prompts forbade every tool except the named T3 MCP tools.
What else changed between sides:
t3_worktree_handoff tool item ends failed/user-rejected in R2, R3 and R1b and interrupted in R1, even though the handoff succeeds. On before it stays running forever. The label comes from the Claude adapter marking a tool call as not executed when its turn is interrupted mid-call. It's cosmetic, and the audit found it isn't a blocker; it's worth a follow-up.Proof audit pass 1: matrix derived from the diff (audit1-matrix.md). It corrected the brief: a parent's handoff can't touch a Claude child, R3 changes for Claude, and R2 can't be reached on before.
Proof audit pass 2: SUFFICIENT (audit2.md).
git diff ab1166d777 2251684173 -- apps/server/src apps/web/src packages is empty.interrupted 71 ms after detach, run 2 completed in the worktree, no warnings.Tests: vp test run src/orchestration-v2/SelectionRestart.integration.test.ts src/orchestration-v2/ProviderSessionManager.test.ts on the merged head: 2 files, 50 tests passed. vp lint and vp fmt --check pass on the three changed files.
CI (cafe pr status at 2251684173): 25 passing, 0 pending, 2 failing (Lint, Check). cafe ci returns no runs for this repo, so I couldn't read the failing logs or confirm which commit they ran on. The changed files lint clean locally.
Not verified:
detach is unchecked at runtime.Model and harness: evidence lead, auditors and capturer were Claude Opus 5.5 (claude-opus-5-5) through Claude Code in T3 Code; the agents inside the fixture were claude-sonnet-5-5.
Takeover CI fix and review round (2026-10-22):
Lint and Check failed on 2251684173 because knip reported apps/server/scripts/verify-worktree-handoff-live.ts as an unused file. Local knip --include files,dependencies showed that same file as the only finding.apps/server entry in knip.jsonc, next to verify-background-live.ts. With that entry, both the files/dependencies knip pass and the apps/server exports pass are clean. CI on the new head is pending.claude-sonnet-5-5): VERDICT: MERGE. No failing sequence, and no auth, permission, mode, sandbox, trust or MCP surface is touched. It notes that other exclusive adapters could wait the full 30s if their interruptTurn no-ops on a turn that has already ended (bounded). The failed/user-rejected label on the tool call isn't blocking and deserves a follow-up.claude-opus-5-5): VERDICT: MERGE. No failing sequence and no security surface touched. Nit: DETACH_TURN_TERMINAL_TIMEOUT_MS (30s) equals the effect worker's 30s lease, so a wedged adapter could outlast the lease; it suggests 15s and calls this theoretical. Same view on the tool-call label.claude-opus-5-5) through Claude Code in T3 Code; reviewers as listed.Fixes #15993 GitCafe: https://git.cafe/pingdotgg/t3code/issues/15993
Added 1 commit
⚠️ The latest CI run did not produce a thread transfer result for a9dc2e3.
This comment will update automatically after the next completed run.
Added 7 commits