You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
fix(server): a delegated task waiting for an approval notifies its idle parent - #35433
Cause: DelegatedTaskQuestionReactor only handled runtime requests of kind user_input, in its live path (runtime-request.updated), its run-end catch-up path and its subagent-settled cancel path. Separately, Orchestrator.isStaleQuestionWake only kept a queued delegated-task notice alive while a pending user_input request matched it, so even a reactor fix produced 0 notices. Fix: the reactor now also handles provider approval kinds (the ProviderRequestKind set, the same set OrchestratorMcpService.pendingTaskApproval treats as approvals). Only pending live approvals raise a notice, matching pendingTaskApproval and the catch-up path. An approval that cannot be answered live (for example the message-capability command request in the existing question test) is still skipped. For approvals, wakeParent queues an advisory notice that names the task, request kind, requestId and child threadId. It says the parent agent cannot approve it and should tell the user or task_cancel and relaunch the task. Its summary reads 'Delegated task "…" needs approval'. Approval notices reuse the message:delegated-task-question:: id, so the existing cancelQueuedWakes logic cancels them once the approval resolves or the task settles. isStaleQuestionWake now matches by request id and pending status, not by kind. No new regex or parser.
On origin/main f03db4f: started T3CODE_DEV_SEED=0 vp run dev (fresh database) and added a scratch git project. Ran node apps/server/src/bin.ts thread start --provider claudeAgent --model claude-haiku-5-5 with a prompt telling the parent to call delegate_task once (mode async, same provider instance, runtimeMode approval-required) for a child that runs the Bash command date > stamp.txt, then end its turn without polling. Observed in the dev database (read-only) and the web UI: the parent's only run completed at 17:20:02 after replying with the childThreadId. The subagent was app_owned, running, completionWake=always. The child raised a command runtime request at 17:20:03 that stayed pending. At 17:26:43 the parent still had only its two original messages and one run: no notification message and no queued or new run. The parent's UI showed the 'Subagent needs approval' banner with Approve/Decline while the parent agent stayed idle. A unit test added to Orchestrator.control-reads.test.ts (idle parent, async child with a pending live command request, expect one delegated_task notice) fails on main with 0 notices, and passes when the request kind is switched to user_input.
Evidence
Run the parent with one async MCP delegation, let its turn finish, then raise the child's command approval for date > stamp.txt. Leave approval unanswered. Real web client: Chromium, 1280×800, light theme, fresh synthetic data on both commits.
Scenario driver. The approval remains unanswered in both captures. The base parent has one run and no approval notice; the fixed parent receives one notice and starts a second turn.
AI-assisted; verified with the tests and tools listed above.
Not shown:
Live-provider behavior and approval kinds other than command were not exercised.
Tests
Reproduced first: vp test run src/orchestration-v2/Orchestrator.control-reads.test.ts -t approval in apps/server failed on the repro branch (expected +0 to equal 1). After the fix: vp test run src/orchestration-v2/Orchestrator.control-reads.test.ts src/orchestration-v2/SteeringCompletion.integration.test.ts: 88/88 passed. vp run check:changed: format, lint, knip, graphql, typecheck (3 packages) and tests (4 files, 32 tests) all passed.
Not in this change
The reactor and its message/command ids are still named 'question' (DelegatedTaskQuestionReactor, delegated-task-question:*). They were not renamed, to keep ids stable and the diff minimal.
Orchestrator's task-completion cleanup (dismissing pending questions via thread.user-input.dismiss) only covers user_input requests. Pending approvals on a finished task are left alone, as before.
No new eval case was added for the new advisory notice text under evals/.
AI-assisted; verified with the tests and tools listed above.
Fails on main: DelegatedTaskQuestionReactor routes only user_input requests,
so a child's command approval never notifies the parent agent (#22925).
AI-assisted; verified with the tests and tools listed above.
…le parent
DelegatedTaskQuestionReactor routed only user_input requests, so an async
delegated child stuck on a command or other approval never told its parent
agent, and the Orchestrator also treated any such queued notice as stale.
Live approval requests now queue an advisory notice that names the child
thread and request and says the agent cannot approve it; the notice is
cancelled once the approval resolves or the task settles, as for questions.
Fixes#22925
AI-assisted; verified with the tests and tools listed above.
Macroscope's review found this PR approvable — Delegated tasks waiting for live approval now notify their parent, with obsolete queued notices removed. The change preserves user-controlled approval and existing task lifecycle guards.
You can add or adjust custom eligibility rules. Learn more.
No actionable comments were generated in the recent review.
Walkthrough
The delegated-task reactor now notifies parent runs about eligible child approval requests. The orchestrator also treats wakes matching pending requests of any kind as current.
Changes: Delegated approval notifications
Layer / File(s)
Summary
Approval request notifications and cleanup apps/server/src/orchestration-v2/DelegatedTaskQuestionReactor.ts
The reactor recognizes approval requests alongside user_input requests. It sends approval-specific notices and includes approvals in catch-up handling and settled-task cleanup.
Pending approval wake handling apps/server/src/orchestration-v2/Orchestrator.ts, apps/server/src/orchestration-v2/Orchestrator.control-reads.test.ts
A matching pending request of any kind keeps a wake current. The test checks that a pending child approval produces one parent notification with the request ID, delegated task, and child thread.
sequenceDiagram
participant ChildRuntime
participant DelegatedTaskQuestionReactor
participant ParentRun
ChildRuntime->>DelegatedTaskQuestionReactor: Submit pending live approval request
DelegatedTaskQuestionReactor->>ParentRun: Send approval notice with child-thread link
Loading
Possibly related issues
pingdotgg/t3code#21935: This issue concerns approval requests that remain pending after a delegated task is interrupted or cancelled; this change includes approval requests in settled-task cleanup.
Possibly related PRs
pingdotgg/t3code#20928: This PR also routes delegated-child approval requests to the parent and adds approval-specific messages.
pingdotgg/t3code#25680: This PR changes delegated-task wake cleanup and stale-wake checks, which this change extends to approval requests.
Suggested labels:bugfix
Suggested reviewers:flamboh, t3dotgg
Merge Risk: Minimal · up to d9a1e
Delegated child approval requests now notify an idle parent, and no concrete merge-blocking issue remains in the reviewed changes.
Pre-merge checks: 4
Check name
Status
Explanation
Title check
The title names the main change: a delegated task that waits for an approval now notifies its idle parent. It uses the conventional commit form, and it matches the raw summary, the PR objectives, and …
Linked Issues check
Issue #22925 requires that a parent receive a notice when an async delegated child waits on an approval after the parent's turn ends. The notice must link the child thread, and the parent must be able…
Out of Scope Changes check
All changes in the whole-PR diff (merge base f03db4f to head d9a1ed3) serve the approval-notice objective. DelegatedTaskQuestionReactor.ts adds approval handling in its three paths: live, catch-up a…
Description check
The description covers the required template sections in substance. Problem: the reactor handled only user_input requests, and isStaleQuestionWake kept notices alive only for pending user_input reques…
Full details: Title check
Explanation
The title names the main change: a delegated task that waits for an approval now notifies its idle parent. It uses the conventional commit form, and it matches the raw summary, the PR objectives, and linked issue #22925. The title is 78 characters, which is within a normal length for a conventional commit subject.
Full details: Linked Issues check
Explanation
Issue #22925 requires that a parent receive a notice when an async delegated child waits on an approval after the parent's turn ends. The notice must link the child thread, and the parent must be able to tell the user or stop and relaunch the child. The diff covers this in apps/server/src/orchestration-v2/DelegatedTaskQuestionReactor.ts. notifiesParent() now admits ProviderRequestKind approval kinds as well as user_input. The live path (runtime-request.updated) returns early only for non-live pending approvals. For approvals, wakeParent builds advisory text that names the task, request kind, requestId and childThreadId. The text states that the parent cannot approve, and it directs the parent to tell the user or use task_cancel and relaunch. The summary reads 'Delegated task "..." needs approval'. The catch-up and settled-cancel paths also use notifiesParent(), so pending approvals are handled there too. In apps/server/src/orchestration-v2/Orchestrator.ts, isStaleQuestionWake no longer requires kind === user_input. It matches on pending status and the message id, so a queued approval notice stays current while its request is pending. The PR states that a regression test in Orchestrator.control-reads.test.ts reproduces the 0-notice failure on main and passes after the fix. I did not run the tests. The result rests on the reviewed diff and the PR's reported test results. The PR's real-app evidence section is unfinished, but that does not affect the coding requirement. Issue #21935 (stale approvals after interrupt) is context only and is not required here.
Full details: Out of Scope Changes check
Explanation
All changes in the whole-PR diff (merge base f03db4f to head d9a1ed3) serve the approval-notice objective. DelegatedTaskQuestionReactor.ts adds approval handling in its three paths: live, catch-up and settled cancel. It also adds the advisory text and the approval summary. Orchestrator.ts removes the user_input-only restriction in isStaleQuestionWake, which is needed so approval notices remain valid. The regression test in Orchestrator.control-reads.test.ts supports the change. The changes to comments and the summary are small and tied to the same behavior. The PR's 'Not in this change' items (renaming question ids, finished-task dismissal of approvals, and the eval case) are explicit deferrals. They are not unrelated changes. No change was found that is unconnected to issue #22925.
Full details: Description check
Explanation
The description covers the required template sections in substance. Problem: the reactor handled only user_input requests, and isStaleQuestionWake kept notices alive only for pending user_input requests, so approval waits produced zero parent notices. Change: the reactor now handles provider approval kinds, queues an advisory notice for pending live approvals, and isStaleQuestionWake matches by request id and pending status. Scope: it links issue #22925 as the bug report. Verification: it gives a reproduction on main, a failing-then-passing focused test, the exact test commands with results, and before/after screenshots and recordings. It also states what was not shown, namely live-provider behavior and non-command approval kinds. The title is a conventional commit title in the expected form, although the template's example is a plain-language title. Minor gaps: the template asks for a conventional commit title, and the description does not state the title; the PR title in the objectives reads as conventional. The 'Not in this change' section is extra but useful. The model and harness line for the AI-assisted work is present as 'AI-assisted' but does not name the model and harness, which the template requests. The description is complete enough to pass.
Finishing Touches
Generate unit tests (beta)
Commit to this branch
Create a new PR
Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
size:M30-99 changed lines (additions + deletions).vouch:trustedAuthor is trusted by repo permissions or the VOUCHED list.
1 participant
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cause: DelegatedTaskQuestionReactor only handled runtime requests of kind user_input, in its live path (runtime-request.updated), its run-end catch-up path and its subagent-settled cancel path. Separately, Orchestrator.isStaleQuestionWake only kept a queued delegated-task notice alive while a pending user_input request matched it, so even a reactor fix produced 0 notices. Fix: the reactor now also handles provider approval kinds (the ProviderRequestKind set, the same set OrchestratorMcpService.pendingTaskApproval treats as approvals). Only pending live approvals raise a notice, matching pendingTaskApproval and the catch-up path. An approval that cannot be answered live (for example the message-capability command request in the existing question test) is still skipped. For approvals, wakeParent queues an advisory notice that names the task, request kind, requestId and child threadId. It says the parent agent cannot approve it and should tell the user or task_cancel and relaunch the task. Its summary reads 'Delegated task "…" needs approval'. Approval notices reuse the message:delegated-task-question:: id, so the existing cancelQueuedWakes logic cancels them once the approval resolves or the task settles. isStaleQuestionWake now matches by request id and pending status, not by kind. No new regex or parser.
Fixes #22925
Reproduction on main
On origin/main f03db4f: started
T3CODE_DEV_SEED=0 vp run dev(fresh database) and added a scratch git project. Rannode apps/server/src/bin.ts thread start --provider claudeAgent --model claude-haiku-5-5with a prompt telling the parent to call delegate_task once (mode async, same provider instance, runtimeMode approval-required) for a child that runs the Bash commanddate > stamp.txt, then end its turn without polling. Observed in the dev database (read-only) and the web UI: the parent's only run completed at 17:20:02 after replying with the childThreadId. The subagent was app_owned, running, completionWake=always. The child raised acommandruntime request at 17:20:03 that stayed pending. At 17:26:43 the parent still had only its two original messages and one run: no notification message and no queued or new run. The parent's UI showed the 'Subagent needs approval' banner with Approve/Decline while the parent agent stayed idle. A unit test added to Orchestrator.control-reads.test.ts (idle parent, async child with a pending livecommandrequest, expect one delegated_task notice) fails on main with 0 notices, and passes when the request kind is switched to user_input.Evidence
Run the parent with one async MCP delegation, let its turn finish, then raise the child's command approval for
date > stamp.txt. Leave approval unanswered. Real web client: Chromium, 1280×800, light theme, fresh synthetic data on both commits.Scenario driver. The approval remains unanswered in both captures. The base parent has one run and no approval notice; the fixed parent receives one notice and starts a second turn.
AI-assisted; verified with the tests and tools listed above.
Not shown:
Tests
Reproduced first:
vp test run src/orchestration-v2/Orchestrator.control-reads.test.ts -t approvalin apps/server failed on the repro branch (expected +0 to equal 1). After the fix:vp test run src/orchestration-v2/Orchestrator.control-reads.test.ts src/orchestration-v2/SteeringCompletion.integration.test.ts: 88/88 passed.vp run check:changed: format, lint, knip, graphql, typecheck (3 packages) and tests (4 files, 32 tests) all passed.Not in this change
AI-assisted; verified with the tests and tools listed above.