feat(orchestrator): Surface waiting background work#4378
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🟡 Medium
isBackgroundCapableTurnItemType causes replayable waiting runs to have their background-capable turn items (command_execution, dynamic_tool, subagent) cancelled, even though those runs were intentionally skipped from terminalization because a pending/running checkpoint effect still exists. When a waiting run has a replayable checkpoint, reconcileProjection omits it from runs, so its ID is absent from recoveredNonterminalRunIds. The later stale-item loop then matches that run's items via isBackgroundCapableTurnItemType + isNonterminalTurnItemStatus and emits turn-item.updated cancellation events, destroying items that belong to a still-recoverable run and checkpoint. The stale-item loop needs to also exclude turn items whose runId belongs to a replayable waiting run, not just those already in recoveredNonterminalRunIds.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts around line 304:
`isBackgroundCapableTurnItemType` causes replayable `waiting` runs to have their background-capable turn items (command_execution, dynamic_tool, subagent) cancelled, even though those runs were intentionally skipped from terminalization because a pending/running checkpoint effect still exists. When a `waiting` run has a replayable checkpoint, `reconcileProjection` omits it from `runs`, so its ID is absent from `recoveredNonterminalRunIds`. The later stale-item loop then matches that run's items via `isBackgroundCapableTurnItemType` + `isNonterminalTurnItemStatus` and emits `turn-item.updated` cancellation events, destroying items that belong to a still-recoverable run and checkpoint. The stale-item loop needs to also exclude turn items whose `runId` belongs to a replayable `waiting` run, not just those already in `recoveredNonterminalRunIds`.
e46af0a to
f318877
Compare
| // After the root turn terminals the run leaves "running", so | ||
| // writeIfRunCurrent would drop late provider_thread.updated | ||
| // roster clears. Only gate pre-terminal root-thread updates. | ||
| const rootTerminalAlreadySeen = yield* Ref.get(rootTerminalSeen); |
There was a problem hiding this comment.
🟡 Medium orchestration-v2/RunExecutionService.ts:738
After the root terminal is observed, writeIfRunCurrent is stripped from all provider_thread.updated events for the root thread — including events from a superseded attempt whose subscription stays open for background work. When that stale subscription receives a late provider_thread.updated and persists the old providerThread snapshot, it overwrites the new attempt's thread state (for example reverting status or roster cleared by the new attempt). The gate should use a settled-run-safe conditional write or field-specific merge rather than bypassing attempt ownership entirely based on rootTerminalAlreadySeen.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration-v2/RunExecutionService.ts around line 738:
After the root terminal is observed, `writeIfRunCurrent` is stripped from all `provider_thread.updated` events for the root thread — including events from a superseded attempt whose subscription stays open for background work. When that stale subscription receives a late `provider_thread.updated` and persists the old `providerThread` snapshot, it overwrites the new attempt's thread state (for example reverting status or roster cleared by the new attempt). The gate should use a settled-run-safe conditional write or field-specific merge rather than bypassing attempt ownership entirely based on `rootTerminalAlreadySeen`.
1e58e65 to
a286c60
Compare
f318877 to
2bf4cdf
Compare
| if (providerInstanceId === undefined) { | ||
| continue; | ||
| } | ||
| events.push({ |
There was a problem hiding this comment.
🟡 Medium orchestration-v2/ProviderRuntimeRecoveryService.ts:319
A stale subagent turn item on an already-settled run gets only a turn-item.updated event, leaving the matching projection.subagents entry in pending/running/waiting indefinitely while its turn item becomes cancelled. The existing nonterminal-run cancellation loop above emits both subagent.updated and turn-item.updated, so the new settled-run path produces contradictory state — downstream code reading the subagent entity will treat a dead subagent as active. The settled-run loop should also emit a subagent.updated event (status cancelled) for matching subagent entries.
🤖 Copy this AI Prompt to have your agent fix this:
In file @apps/server/src/orchestration-v2/ProviderRuntimeRecoveryService.ts around line 319:
A stale `subagent` turn item on an already-settled run gets only a `turn-item.updated` event, leaving the matching `projection.subagents` entry in `pending`/`running`/`waiting` indefinitely while its turn item becomes `cancelled`. The existing nonterminal-run cancellation loop above emits both `subagent.updated` and `turn-item.updated`, so the new settled-run path produces contradictory state — downstream code reading the subagent entity will treat a dead subagent as active. The settled-run loop should also emit a `subagent.updated` event (status `cancelled`) for matching subagent entries.
Summary
clears.
Waiting UI from feat: "waiting" status for threads with open background tasks (i.e. pr monitors) #4415 can consume it without a second sidebar implementation.
Relationship to #4415
#4415 owns the frontend
Waiting direction on
main: an idle provider session renders as Waiting in theweb and mobile thread lists. This PR supplies the orchestrator-v2 counterpart.
It projects the provider-thread background roster, keeps late roster clears
observable, and presents a nonempty post-settlement roster as runtime
idle.This PR intentionally does not carry its former sidebar or mobile status
precedence changes. The GitHub base remains
t3code/codex-turn-mappingso thediff contains only the orchestrator-v2 delta. Using #4415's branch as the
literal GitHub base would include the entire CTM stack in this PR. The intended
integration order is #4415 into
main, CTM absorbsmain, then this focuseddelta completes Waiting for orchestrator v2.
Problem and Fix
runningor checkpointwaitingafter the authoritative post-settlement roster arrives.idlein client presentation while preserving the original status inlatestRun.pendingBackgroundTaskswith an empty-array default.Defensive Fixes
Validation
recovery, contracts, shared pending-work derivation, client presentation, and
the web timeline.
server, and web.
vp checkpasses for every changed file, andgit diff --checkisclean.
t3code/codex-turn-mapping.runningand checkpointwaitingshell states without restoring direct sidebar roster precedence.