Skip to content

fix(server): a delegated child's answer after its monitor ends reaches the parent - #31856

Closed
juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-31843
Closed

juliusmarminge wants to merge 4 commits into
mainfrom
fix/issue-31843

Conversation

@juliusmarminge

Copy link
Copy Markdown
Member

Problem

A delegated child can arm a Monitor, skip t3_background_wait, and end its turn. Unawaited monitors don't hold completion, so the task finalizes with its "monitor armed" text; that part is by design. When the Monitor later ends, Claude wakes the child and it answers, but that answer never reaches the parent. The same wake from a background command ending does reach it (https://git.cafe/pingdotgg/t3code/pulls/23542).

To reproduce on main (5c004fd), with a dev server on a fresh database: a Claude coordinator delegates an async Claude worker with the task "use only the Monitor tool with sleep 60; echo doneD | tee …, don't call t3_background_wait, end your turn, then reply with what it reported".

  • The task finalizes about 0.5 s after the Monitor is armed.
  • Sixty seconds later the child is woken with {source:{kind:"monitor"}, outcome:"completed"} and replies doneD.
  • The parent gets no subagent.updated and no delivery.

Change

mayStartSubagentFollowup (apps/server/src/orchestration-v2/Orchestrator.ts) blocked every monitor-kind notification from starting a subagent follow-up. It now blocks only monitor notifications with outcome "updated", which are per-event progress. A monitor that ends (completed, failed, cancelled) now starts a follow-up the same way a background command's ending wake does, and its result is delivered to the parent.

The parent-lock decision at dispatch uses the same predicate, so it follows automatically. Nothing else changes: the awaited/hold policy (backgroundWorkHoldsCompletion), the prompts, and the contracts.

PR-watch notifications also use the monitor kind, but a subagent cannot hold a watch. watch_pull_request is hidden from subagents and rejected by the handler, and the reactor ends any watch on a subagent thread.

Scope and approval

Fixes #31843

Verification

  • vp test run apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts: 40/40 pass.
    • The new case delivers the run a monitor's ending wake starts after the original result fails on main with the run woken by the child's monitor ending should be tracked as a follow-up delegated task for the parent: expected undefined to not equal undefined.
    • The background-command case still passes.
    • The existing delivers a %s follow-up independently of the original task case still asserts that an "updated" monitor notification starts no follow-up.
  • vp run check:changed passes after merging main: format, lint, knip, graphql, typecheck (3 packages), and 5 test files with 60 tests.
  • Runtime check with the real dev server, a fresh database, and the same coordinator and worker prompt on each side:
Before, main 5c004fd: worker D's monitor-end answer never reaches the parent
# seq|time UTC|event|thread|status|item|text|pendingBackgroundTasks
745|06:38:28.805|provider-thread.updated|child |active|||monitor:b1f0w07xb:aw=-
756|06:38:30.513|run.updated|child |waiting|||
764|06:38:31.059|subagent.updated|parent|completed||The Monitor is armed (task `b1f0w07xb`). It runs `sleep 60; echo doneD | tee …` and …
815|06:39:28.969|message.updated|child |||Monitor "Waiting for doneD marker after 60s sleep" stream ended   (notification {source:monitor, outcome:completed})
826|06:39:30.519|message.updated|child |||doneD
837|06:39:30.562|run.updated|child |completed|||
(no further subagent.updated or delivery on the parent)
After, fix/issue-31843 405e1db: the monitor-end answer is delivered and wakes the parent
# seq|time UTC|event|thread|status|item|text
124|06:52:13.596|subagent.updated|parent|completed||The Monitor is armed (task `b9c3z2yxp`) and will emit `doneD` after about 60 seconds. …
169|06:53:11.584|message.updated|child |||Monitor "Wait 60s then emit doneD marker" stream ended
185|06:53:12.959|message.updated|child |||doneD
198|06:53:13.554|subagent.updated|parent|completed||doneD
219|06:53:15.624|message.updated|parent|||Worker D's second result arrived and it is `doneD`, …

Not checked: live Grok or ACP monitors. From the code, their monitor wakes carry only terminal outcomes.

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

A delegated child woken by a monitor's terminal notification (completed,
failed, cancelled, unknown) answered without starting a subagent
follow-up, so its new result never reached the parent. Monitor
notifications now gate on outcome: only "updated" progress events skip
the follow-up, matching how a background command's ending wake works.

AI-assisted; verified with the tests and tools listed above.
@github-actions github-actions Bot added vouch:trusted Author is trusted by repo permissions or the VOUCHED list. size:XS 0-9 changed lines (additions + deletions). labels Nov 6, 2026
@macroscopeapp

macroscopeapp Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at a99108b

Macroscope's review found this PR approvable — Monitor-end notifications now let a delegated child’s subsequent answer reach its parent through existing follow-up handling. Progress updates remain excluded, and existing task guards and parent locking constrain the change.

No code changes detected at a61fc32. Prior analysis still applies.

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

@github-actions

github-actions Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Codex Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Codex Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Codex Live turn WebSocket decoded 21.5 KiB 21.5 KiB 0 B (0.0%) 24.4 KiB ✅
Codex Live turn messages 1 1 0 (0.0%) 8 ✅
Claude Total thread wire 5.0 KiB 5.0 KiB 0 B (0.0%) 6.8 KiB ✅
Claude Thread snapshot wire 3.8 KiB 3.8 KiB 0 B (0.0%) 4.9 KiB ✅
Claude Live turn WebSocket wire 1.2 KiB 1.2 KiB 0 B (0.0%) 2.0 KiB ✅
Claude Live turn WebSocket decoded 21.9 KiB 21.9 KiB 0 B (0.0%) 24.4 KiB ✅
Claude Live turn messages 1 1 0 (0.0%) 8 ✅

Baseline: d290c1f · PR result: a61fc32 · 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.

@coderabbitai

coderabbitai Bot commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor

Explain Change →

No actionable comments were generated in the recent review.

Walkthrough

The orchestrator now allows monitor notifications whose outcome is not "updated" to start delegated follow-up work. Tests cover command and monitor notifications through follow-up completion, claiming, and a distinct completion wake for the parent.

Changes: Delegated follow-up

Layer / File(s) Summary
Monitor notification follow-up rule
apps/server/src/orchestration-v2/Orchestrator.ts
mayStartSubagentFollowup now allows monitor notifications unless their outcome is "updated".
Follow-up completion and parent wake
apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts
Tests cover command and monitor notifications, verify that the follow-up task completes and is claimed, and check that recovery requests a distinct completion wake for the parent.

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

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: yash-singh1

Merge Risk: Minimal · up to a61fc

Terminal monitor notifications can start the intended follow-up and deliver its result to the parent. No actionable merge-blocking risk remains after normal checks.

Pre-merge checks: Passed 4

Check name Status Explanation
Title check Passed The title clearly describes the fix: a delegated child’s answer after its monitor ends reaches the parent.
Description check Passed The description includes the required Problem, Change, Scope and approval, and Verification sections. It gives detailed reproduction and test results and links the issue. It says AI-assisted but does …
Linked Issues check Passed [#31843] The predicate change lets terminal Monitor notifications start a delegated follow-up while keeping updated notifications excluded. The delivery test checks follow-up tracking, result delive…
Out of Scope Changes check Passed The Orchestrator predicate change and delegated-delivery test both support [#31843]. The test retains background-command coverage and checks the Monitor behavior. The change summary identifies no unre…
Full details: Description check

Explanation

The description includes the required Problem, Change, Scope and approval, and Verification sections. It gives detailed reproduction and test results and links the issue. It says AI-assisted but does not name the model and harness, as the template requests when an agent was used.

Full details: Linked Issues check

Explanation

[#31843] The predicate change lets terminal Monitor notifications start a delegated follow-up while keeping updated notifications excluded. The delivery test checks follow-up tracking, result delivery, and a parent wake. The reported runtime check confirms the child’s later answer reaches the parent. This implements the issue’s later-notification approach without changing the awaited-work policy.

Full details: Out of Scope Changes check

Explanation

The Orchestrator predicate change and delegated-delivery test both support [#31843]. The test retains background-command coverage and checks the Monitor behavior. The change summary identifies no unrelated changes.

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

Usage-based review receipt

Note

This review was completed with usage-based billing: files reviewed beyond your plan's included limits are billed at $0.25/file. View usage-based billing.

Recent review info

Run configuration

Commits

Reviewing files that changed from the base of the PR and between a99108b and a61fc32.

Files selected for processing: 2

  • apps/server/src/orchestration-v2/DelegatedCompletionDelivery.test.ts
  • apps/server/src/orchestration-v2/Orchestrator.ts

Limit details: You’ve used all 10 included reviews currently available.

@juliusmarminge juliusmarminge added bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed labels Nov 6, 2026

Copy link
Copy Markdown
Member Author

Evidence

Run the same coordinator prompt on both commits: delegate an asynchronous local Claude worker using only Monitor with sleep 60; echo MONITOR_DONE, omit t3_background_wait, finish initially with WAITING, and answer FINAL when Monitor ends. Let the parent finish; inspect the worker and then the parent after Monitor completes.

Both captures: real development web client in Chromium on Linux, dark theme, 1280×800 CSS viewport, separate fresh databases, identical synthetic workspace and driver. Setup trimmed; private runtime metadata cropped or labeled as redacted; remaining footage plays in real time.

Before After
d290c1f1b6 a61fc3286e
Before recording After recording
Worker answers FINAL; parent remains at WAITING. Separate delegated FINAL result reaches the parent, which answers FINAL.

Driver · Seed · Observation script · Rendering script · Environment and instructions

Supporting persisted observations: base, head. No tests used as evidence.

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

Not shown:

  • Monitor progress updates and unsuccessful endings were not exercised; this reproduction covers a successful Monitor stream end.
  • Other providers, remote delegation, desktop, and mobile were not exercised; verification followed the requested local Claude flow through the web client.

@juliusmarminge

Copy link
Copy Markdown
Member Author

Thanks for this. Main has the change since #36339.

Main now delivers a delegated child's answer when its Monitor ends while suppressing Monitor progress follow-ups. I compared this PR's complete diff with the merge: mayStartSubagentFollowup has the identical production predicate, and main's existing delivery test now covers monitor/completed; the remaining differences restructure tests and clarify a comment.

Closing as superseded.

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

Labels

bugfix Fix PR opened by an issue lead; enters the PR loop pr:gate-failed A land gate failed size:XS 0-9 changed lines (additions + deletions). vouch:trusted Author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Delegated Claude workers are finalized when they end their turn on a Monitor without calling t3_background_wait

1 participant