Skip to content

fix(server): restart recovery keeps a finished delegated child's background tasks - #28994

Open
NotAnNvidiaFan wants to merge 2 commits into
pingdotgg:mainfrom
NotAnNvidiaFan:fix/delegated-child-restart-roster
Open

NotAnNvidiaFan wants to merge 2 commits into
pingdotgg:mainfrom
NotAnNvidiaFan:fix/delegated-child-restart-roster

Conversation

@NotAnNvidiaFan

@NotAnNvidiaFan NotAnNvidiaFan commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Refs #28208. This is its second cause, separate from #28500, and it happens on every platform.

Problem

A delegated task (delegate_task child) can finish while the child still holds an awaited background task. The delegated-task:provider-continuation follow-up is the usual case. When that happens, a server restart loses the background task without telling the child.

Finalizing the task writes the child's provider thread onto the parent's app_owned subagent row. Two runtime recovery queries in ProjectionStore, the getRecoveryThreadIds("runtime") candidates and getRuntimeRecoveryProjection's provider threads, follow any subagent row's provider_thread_id. Only provider-native subagents need that. So the parent's reconcile clears the child's pendingBackgroundTasks on the parent's stream. It writes no restart note, because no parent run uses that provider thread. The child's own reconcile then sees an empty roster, and the child is never continued.

Change

Limit both subagent joins to origin = 'provider_native'. Delegated children are already reconciled as their own threads (see "Delegated tasks" in docs/internals/server-updates.md). Every app_owned row has a local or remote child thread, so this matches isAppOwnedDelegation in ProviderRuntimeRecoveryService. The provider-control and rollback lookups that also join subagent rows target one provider thread by id and are not part of recovery, so they are unchanged.

Scope and approval

Refs #28208. The issue was filed through npx t3 triage and has no maintainer triage yet, so this PR relies on CONTRIBUTING's exception for a very small, focused fix of an obvious bug:

Verification

Tests

  • New case in ProjectionRecovery.test.ts (real SQLite store and recovery service). A finished app-owned delegation's child holds an awaited roster task. The parent sorts first, so it reconciles first. After startup reconcile, the child's run carries the restart note and its provider-runtime.continue effect is queued. On main the effect list is empty.
  • vp run check:changed passes. Neighbouring suites also pass (ProjectionStore, ProviderRuntimeRecoveryService, RestartContinuation, DelegatedCompletionDelivery, FoundationPersistence, runtimeLayer: 310 tests).

Live runtime proof

Live runs on Windows with Claude Haiku 5.5 as parent and child, each in a fresh scratch T3 home. The parent delegates a child that waits on sleep 20, wakes, starts an awaited sleep 600 in the background, and ends its turn, which finalizes the delegated task. The server is then stopped through the graceful path (desktop stop notice; the backend shuts itself down) and started again. Both builds are #28500's head, because without its stop-pipe fix Windows hard-kills the backend. They differ only by this PR's ProjectionStore.ts change.

Before the stop, both builds were in the same state: the parent's app_owned subagent rows pointed at the child's provider thread, and that thread's roster held the awaited sleep 600.

Without this fix With this fix
Shutdown reconcile's roster-clearing provider-thread.updated on the parent's stream on the child's stream
Child run restartCancelledBackgroundWork none shell: Sleep 600 seconds then echo second (id bwkoe0eg9), awaited
Child continuation run after restart none (prepared continuation declined) ran
Child's last message "Phase 2 is running as background task bmk7zje0i … I'll wait for its completion notification." "Phase 2's 600-second sleep was cancelled by the server restart, so I restarted it as task bv26gvti7 and set t3_background_wait on it."

Whether the bug fires depends on reconcile order, since recovery visits threads by updated_at. On the graceful path, stopping sessions touches the parent just before the child, so the parent reconciles first. In a Windows hard-kill run on main, the parent's later reply kept the child first and the child recovered correctly. The unit test pins the parent-first order.

Model and harness: Claude Opus 5.5 (claude-opus-5-5) in Claude Code.

🤖 Generated with Claude Code

…ground tasks

Finalizing a delegated task records the child's provider thread on the
parent's subagent row. The runtime recovery queries followed that row, so
the parent's reconcile emptied the child's background roster without a
restart note, and the child was never continued. Only provider-native
subagents now pull their provider thread into the parent's recovery.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added vouch:unvouched PR author is not yet trusted in the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 2, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at dcb3c93

Macroscope's review found this PR approvable — Runtime recovery now distinguishes provider-native subagents from independently recovered delegated children, preventing parent recovery from clearing a child's background tasks. The change is narrowly scoped and preserves existing recovery paths.

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

@coderabbitai

coderabbitai Bot commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
Commits

Reviewing files that changed from the base of the PR and between dcb3c93 and a19fd4a.

Files selected for processing: 2
  • apps/server/src/orchestration-v2/ProjectionRecovery.test.ts
  • apps/server/src/orchestration-v2/ProjectionStore.ts

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

Walkthrough

Runtime recovery now filters subagent-linked provider threads by origin in both selection paths. A test checks startup reconciliation for a delegated child with an awaited shell task.

Changes: Runtime recovery thread selection

Layer / File(s) Summary
Filter recovery threads by subagent origin
apps/server/src/orchestration-v2/ProjectionStore.ts, apps/server/src/orchestration-v2/ProjectionRecovery.test.ts
Both runtime recovery selection paths now require a matching subagent to have provider_native origin. A recovery test checks that startup reconciliation associates the restart-continuation effect and cancelled shell task with the child run.

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

Change: Bug fix

Suggested labels: bugfix

Suggested reviewers: t3dotgg, juliusmarminge

Merge Risk: Minimal · up to a19fd

The change isolates delegated-child recovery. No merge-blocking risk was identified.

Pre-merge checks: Passed 4
Check name Status Explanation
Title check Passed The title clearly identifies the server recovery fix for finished delegated children and their background tasks.
Description check Passed The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the failure, the SQL join change, scope rationale, focused tests, live runtime evidenc…
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes the required Problem, Change, Scope and approval, and Verification sections. It explains the failure, the SQL join change, scope rationale, focused tests, live runtime evidence, and limitations.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

…estart-roster

# Conflicts:
#	apps/server/src/orchestration-v2/ProjectionStore.ts

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XS 0-9 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant