fix(server): completed subagents no longer block their parent's file revert#31877

closed7
main
fix/issue-31865
opened6d agobyJJulius Marminge
View on GitHubTwo-way sync enabledPR last imported 1d ago

Problem

A thread working in its own worktree could not use Edit from here → Revert files too once one of its subagents had finished. The restore-isolation check counted the finished subagent, which keeps the parent's worktree path, as another owner of that worktree, and refused with "File restore requires an isolated worktree." This happened for Claude Task subagents and for delegate_task children with shared isolation.

Reproduction on main 291bcc392c, in the web app with a fresh database and a synthetic repo: start a thread in a new worktree, let it edit a file, then have it run one Claude Task subagent (or one delegate_task child, isolation shared) that edits the file. Edit from here → Revert files too on the second message is rejected, and the file stays as it was. A control thread whose two turns edited the file itself reverts fine.

Change

isCheckpointRestoreIsolated now skips another thread only when all of these hold:

  • it descends from the restoring thread through subagent links only;
  • it uses exactly the same worktree path;
  • it is not live: no active run, activity, pending request or background work;
  • every subagent record its parent keeps for it is completed. Native children have no runs of their own, so this is where their completion is recorded;
  • none of its own runs is unfinished, including a held queued follow-up, which the shell leaves out of its status.

Everything else still blocks as before: running or waiting children, children that failed or were interrupted, forks, side chats with tool activity, unrelated threads, and another thread's subagents. Both rollback admission and execution use this helper.

The user guide's description of when file restore is refused now says that completed subagents don't count.

Scope and approval

Fixes pingdotgg/t3code#31865

Verification

  • vp test run apps/server/src/orchestration-v2/CheckpointRestoreSafety.test.ts: 19 passed. The new table fails on main for a completed subagent and a completed sub-subagent. A further row, a finished native subagent whose shell status is idle, fails on a shell-status-only version of the rule. The blocking rows also pass: running native subagent, running subagent, subagent awaiting approval, subagent with background work, completed subagent with a held queued follow-up, and another thread's subagent. The held-queue row fails if the runs check is removed.
  • vp run check:changed passes (format, lint, knip, typecheck, tests).
  • Runtime check in the web dev server, fresh database, synthetic repo:
ThreadChildren in the parent's worktreemain 291bcc392cthis branch
parentone Claude Task subagent, finishedrejected ("File restore requires an isolated worktree…"), file unchangedaccepted, conversation rewound, file restored to the target checkpoint
delegatorone delegate_task child, isolation shared, finishedrejected the same way, file unchangedaccepted, file restored
controlnoneaccepted, file restoredunchanged
Driver used for the runtime check (scratch, not committed)
sh
1REPO=$SCRATCH/repo; B=$CHECKOUT/.t3
2git init -q -b main "$REPO" && printf 'hello\n' > "$REPO/notes.txt" && git -C "$REPO" add . && git -C "$REPO" commit -qm init
3cd "$CHECKOUT/apps/server"
4node src/bin.ts project add "$REPO" --base-dir "$B"
5node src/bin.ts thread start "$REPO" "Append the line 'turn one' to notes.txt. Do nothing else and reply 'done'." \
6 --title parent --provider claudeAgent --model <model> --worktree main --branch repro-parent --wait --base-dir "$B"
7node src/bin.ts thread send <parent id> "Use the Task tool to launch exactly one general-purpose subagent, in the foreground, with this prompt: 'Append the line turn two from subagent to notes.txt in the current directory, then reply done.' Do not edit any file yourself." --wait --base-dir "$B"
8# delegator: the same, with "Call the T3 MCP tool delegate_task exactly once with mode 'wait', isolation 'shared', and task: ..."
9# control: two turns that append to notes.txt directly
10# Then in the web client: expand the second message, Edit from here → Revert files too, and read notes.txt in the thread's worktree.

Not checked: desktop and mobile clients (the change is server-only and the clients call the same rollback RPC), and providers other than Claude for native subagents (the rule reads provider-neutral subagent records). Screenshots were taken but not attached because the upload tool was unavailable on the verification machine; the table above is the observed result.

AI-assisted; verified with the tests and tools listed above.

Checks

23 successful · 17 skipped
All checks passed
e4000e2

Added 7 commits

  • fix(server): finished native subagents no longer block their parent's file revert38e5672
  • docs: finished subagents no longer block Revert files too4a94cac
  • Merge remote-tracking branch 'origin/main' into fix/issue-3186563b5730
  • test(server): reproduce #31865 completed subagents blocking file restore65b6714
  • fix(server): completed subagents no longer block their parent's file revert99e884a
  • fix(server): a held queued follow-up on a subagent still blocks its parent's file revertb6e6f00
  • docs: say completed subagents, since failed or interrupted ones still blocke4000e2
Synced from GitHub

Approvability

Verdict: Approved at e4000e2

Macroscope's review found this PR approvable — Completed subagent descendants no longer block their parent's requested file restore, and their edits revert with the parent's. The exception requires verified completion and a shared worktree; protections for unrelated threads remain unchanged.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

ProviderMetricMain baselineThis PRImpactPR ceiling
CodexTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
CodexThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
CodexLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
CodexLive turn WebSocket decoded21.5 KiB21.5 KiB0 B (0.0%)24.4 KiB✅
CodexLive turn messages110 (0.0%)8✅
ClaudeTotal thread wire5.0 KiB5.0 KiB0 B (0.0%)6.8 KiB✅
ClaudeThread snapshot wire3.8 KiB3.8 KiB0 B (0.0%)4.9 KiB✅
ClaudeLive turn WebSocket wire1.2 KiB1.2 KiB0 B (0.0%)2.0 KiB✅
ClaudeLive turn WebSocket decoded21.9 KiB21.9 KiB0 B (0.0%)24.4 KiB✅
ClaudeLive turn messages110 (0.0%)8✅

Baseline: 2756da4 · PR result: e4000e2 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 108.5 KiB
  • Claude decoded thread snapshot: 108.8 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

Synced from GitHub

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The restore-isolation check now allows file restoration when same-worktree subagent descendants meet completion and liveness conditions. Tests cover descendant lineage and activity states. The composer documentation describes how completed subagent edits are reverted with the thread.

Changes: Restore isolation

Layer / File(s)Summary
Descendant eligibility checks
apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts
The helper checks subagent ancestry across active and archived threads. It defines preparing, queued, starting, running, and waiting as unfinished run statuses.
Owner scan and regression coverage
apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts, apps/server/src/orchestration-v2/CheckpointRestoreSafety.test.ts, docs/user/composer.md
The owner scan skips same-worktree descendants only when the subagent records are completed and the descendant has no active run, live subagent shell, or unfinished run. Tests cover lineage and activity cases. The documentation states that completed subagent edits are reverted with the thread.

Estimated code review effort: 2 (Simple) | ~12 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: t3dotgg

Merge Risk: External image Minimal · up to e4000

This change lets a parent thread revert files when its completed subagents share the same worktree, while running, queued, forked and unrelated threads still block the revert. No merge-blocking risk was found in the supplied context.

Pre-merge checks: Passed 4

Check nameStatusExplanation
Title checkPassedThe title clearly describes the main change.
Description checkPassedThe description covers the problem, change, scope, and verification. It links an issue, but does not establish whether it is triaged. It also omits the model and harness used for AI assistance.
Linked Issues checkPassed[#31865] The isolation check skips only same-worktree descendants with strict subagent ancestry, completed parent records, no live shell or active run, and no unfinished runs. It also checks archived …
Out of Scope Changes checkPassed[#31865] The safety-check changes, regression tests, and user guide update support the requested restore behavior. The changes do not add the excluded shared-workspace restore or rollback error wordin…
Full details: Linked Issues check

Explanation

[#31865] The isolation check skips only same-worktree descendants with strict subagent ancestry, completed parent records, no live shell or active run, and no unfinished runs. It also checks archived threads. Other owners retain the existing isolation checks. The helper remains in use at admission and execution. Automated tests cover completed children and grandchildren, native children, active or waiting children, background work, queued runs, and unrelated subagents.

Full details: Out of Scope Changes check

Explanation

[#31865] The safety-check changes, regression tests, and user guide update support the requested restore behavior. The changes do not add the excluded shared-workspace restore or rollback error wording work.

Finishing Touches

Generate docstrings

  • Commit to this branch
  • Create a new PR
Generate unit tests (beta)

  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
External image Recent review info

External image Run configuration

External image Commits

Reviewing files that changed from the base of the PR and between 2756da402b5d795baa05762cba1706a2c0e2fac8 and e4000e221bf7633bc6eb9258379fd2436ff7894e.

External image Files selected for processing: 3

  • apps/server/src/orchestration-v2/CheckpointRestoreSafety.test.ts
  • apps/server/src/orchestration-v2/CheckpointRestoreSafety.ts
  • docs/user/composer.md

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Thanks for this. #31874 makes the same change and is the one being kept: Keep #31874 instead: it addresses the same open issue, #31865, and is the more complete fix. Both PRs currently merge without conflicts and have all reported checks passing: 30 for that PR and 33 for this one. The other PR additionally inspects native children's raw pending tools and provider activity, and ancestor-owned background work. Those checks matter because main's background-work derivation omits provider and tool tasks when a child has no app-owned run; this PR relies on that shell summary and can therefore overlook unfinished native-child work. The other PR includes regression cases for those conditions. Main still lacks either ownership exception, and distinctive-line history searches found no implementation of this change.

Closing as a duplicate.