Parent leaves Working when a resumed child waits on its own delegated work#30395

opened7d agobyRrafaeltab

What happened

Parent leaves Working while its delegated child is active. Reproduced when follow-up work sent to an already completed child delegates a grandchild and ends its turn. Grandchild completion resumes the child, but parent stays outside Working while Lineage says 1 running. Expected: remain Working until delegated work finishes or needs user action.

Diagnosis

Affected tag v0.1.7-nightly.20261103.4138, apps/server/src/orchestration-v2/Orchestrator.ts: finalizeAppOwnedSubagent (12685+) waits for overall child progress only for the original fork task, not follow-ups. Follow-up finalizes when its bound run ends despite pending grandchildren. Later agent/server delegatedCompletion wakes have no notification; mayStartSubagentFollowup (682+) rejects them. recordSubagentFollowup (7938+) independently rejects their absent sender/notification. No replacement parent task is recorded. Lineage reads child activityRunStatus (threadRelationships.ts:105+); Working reads parent runtime (threadInbox.ts:15+). Source-extracted guard checks confirm these exclusions; not a full integration test. Incident and controlled repro records match this mechanism. No fix tested.

Steps to reproduce

  1. Enable Working; use Codex/local T3 delegation.
  2. Parent delegate_task: start a trivial async child and let both finish. Keep child ID.
  3. Parent t3_thread_send (mode auto, fresh clientRequestId) to SAME completed child: "Use delegate_task for one local async grandchild. Grandchild runs sleep 30 once foreground, replies GRANDCHILD_DONE. After dispatch reply CHILD_WAITING and end your turn; no polling. When completion resumes you, run sleep 90 foreground then reply CHILD_DONE. Do not edit files."
  4. Top-level parent ends turn after send; no waiting/polling.
  5. During nested work and resumed 90s run, parent is outside Working; Lineage says 1 running. Controls passed: initial sleep delegation; follow-up without grandchild; NEW child with grandchild. Only existing-child follow-up plus nested work reproduced.

Version

0.1.7-nightly.20261103.4138 (user reported; exact tag inspected). Server version not independently verified. Triage CLI separately: 0.0.46-nightly.20261012.3010.

Environment

app.t3.codes on Windows; inspected server state Linux x64. Codex GPT-6.1-Sol High; local T3-owned children. Browser version and server launch method not captured.

Evidence

shell
1Read-only SQLite, 2026-11-03 UTC. Probe parent de1d344a-a31b-4a2e-8b58-b821e21a56d2:
221:14:12.015 child run3 starts, delegatedTaskId bound
321:14:22.551 run3 completed CHILD_WAITING
421:14:22.632 parent follow-up completed
521:14:55.703 child run4 starts: agent/server, delegatedCompletion present, notification and delegatedTaskId absent
621:16:31.943 child run4 completes
7Two screenshots show parent outside Working, second with Lineage 1 running. Original parent aa4edb4d-3fd6-43de-8aae-7e79e9f42d14: follow-up completed18:10:50.444, child resumed18:12:00.459 unbound; refresh did not fix. Attach screenshots manually.

No exact duplicate found. #29485: ordering within Lineage. #26740: remote follow-up delivery; this repro is local with server nested completion wakes.

Fix applied or workaround

No state, configuration, service, or source changes. No verified workaround. Full report available separately; screenshots must be attached manually.

Filed by

Codex (GPT-6.1-Sol, default), via T3 triage, with three parallel Codex subagents.

Synced from GitHub

Same bug here with Claude (Fable 5.2, then Opus 5.5) on a three-level delegate_task chain, main @ 6c9adfb273. Adding it because it shows the gap isn't Codex-specific, and that it covers grandchildren started from follow-ups, not only a single resumed child.

Setup. A (top-level coordinator) delegated to B; B delegated to C1 and C2. All app-owned, all on one machine. A's original delegate_task for B had finished the day before, so everything below runs through follow-ups.

What I saw. For over an hour, A showed as Done in the sidebar, chat, and mobile while C1 and C2 were working and B kept waking up to send them more work. Nothing on A said anything was still running, and A's lineage row for B said Done. A's agent was never woken again either, so it never heard about the later results (that half looks like #21837).

Timeline (UTC, 2026-11-10, from a read-only copy of the state DB):

text
111:45:08-11:45:45 A gets a follow-up row for a B run (a "Background command finished" notification).
2 It closes when that run ends, while C1 (since 11:32) and C2 (since 11:40) keep running.
3 From here on A shows nothing for them.
411:53-12:09 Usage limit hits all four threads. Each automatic "Continue where you left off."
5 gets a (failed) row in its parent, so that part is tracked fine.
612:11 A messages B after I switched to Opus -> row in A, closes 12:15
712:14 B messages C1 and C2 -> rows in B
812:45:51-12:46:02 A's last follow-up row for B (another background-command notification run) closes.
912:46:04 A's turn ends.
1012:46:19 C2 completes -> B woken by a delegatedCompletion run. No row in A.
1112:46:26 B sends C2 another pass -> row in B
1213:16:26 Another delegatedCompletion wake in B, no row in A. B sends C1 another pass at 13:16:37.
13~13:54 Chain finishes. A showed no running work from 12:46 on, and never ran again.

Why (agrees with the diagnosis above):

  • Only the original task waits for nested work before it finalizes (finalizeAppOwnedSubagent). A follow-up task finalizes as soon as its bound run ends, even when that run left subagents running.
  • B's later runs are delegatedCompletion wakes (agent/server, no notification, no sender), which mayStartSubagentFollowup and recordSubagentFollowup never turn into a parent row.
  • On the client, thread shells are per thread. Approvals already roll up the chain (pendingSubagentRequestsByThread), but running work doesn't, so A has nothing to show once its own rows close.

From reading the code, a subagent picked back up after a server restart (restartContinuationOfRunId) is excluded the same way. I haven't reproduced that one.

Steps to reproduce (any provider; these mirror the live incident, I haven't run them in isolation):

  1. In thread A, delegate_task an async child B with a trivial task. Let A and B both finish, so the original task is done.
  2. From A, t3_thread_send to B: "Use delegate_task to start two async subagents. Each runs sleep 120 in the foreground and replies DONE. After dispatching, end your turn without waiting or polling. When a completion wakes you, reply GOT_ONE."
  3. Let A end its turn.
  4. While the grandchildren sleep: A shows Done in the sidebar, chat, and mobile thread list, with no background-work indicator, and A's lineage row for B says Done. B shows Waiting; the grandchildren show Working.
  5. When the first grandchild finishes, B wakes and runs. A still shows nothing and gets no row for that run.

Expected: A shows that work is still running below it until the last descendant ends, as it already does while the original task is open. The sidebar docs say subagents keep a thread in Working.

Fix. I have a fix ready. It's a client-side roll-up in client-runtime that does for running work what pendingSubagentRequestsByThread already does for approvals. A thread lists a subagent child as background work while that child, or anything under it, is still running and the thread's own rows no longer cover it. The entry clears when the last descendant ends. It covers web, desktop, and mobile, has tests that fail on main, and is running on my machine now against the chain above.

It's around 230 lines across client-runtime, web, and mobile, so I wanted to ask first: would you take it as a PR? Or would you rather fix this on the server by keeping follow-up tasks open until nested work settles? That route would also cover task_status and result delivery (#21837), which a client-side roll-up doesn't.

Investigated with Claude Opus 5.5 (Claude Code in T3 Code).