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`.
Summary
background work remains.
Grok, and Claude subagents.
background Bash.
Relationship to #4255
This is the orchestrator-v2 counterpart to Theo's
#4255. That PR implements the
current
mainorchestration session path. This PR carries the equivalentprovider-thread and projection contract for orchestrator v2, plus the complete
v2 web UI: a sidebar Waiting pill and a waiting timeline row.
No follow-up frontend plumbing is expected. Once this PR lands on
t3code/codex-turn-mapping, Waiting should work end to end on orchestrator v2without depending on #4255. If #4255 is later back-merged from
main, itsshared-component edits may need conflict resolution, but not another product
feature PR.
Problem and Fix
pendingBackgroundTaskswith an empty-array default for backward compatibility.Defensive Fixes
Validation
vp test runon the focused server, contract, shared, client-runtime, andweb logic suites: 270 tests passed across ten files.
git diff --checkpassed.
subagents, Codex background exec and subagents, and Grok continuations and
subagents.
the timeline row and sidebar pill appeared after root settlement, then both
cleared after task completion.
RunExecutionServiceoverlap that will need conflict resolution if thebranches are combined.
Depends on #4193. This change does not semantically depend on #4218 or #4229.
Note
Surface pending background work in the chat timeline and sidebar status
pendingBackgroundTasksfield to provider thread and thread shell contracts, derived from provider-thread rosters and nonterminal turn items after a run settles.derivePendingBackgroundWorkandformatPendingBackgroundWorkLabelutilities inpackages/sharedto compute and format the task list.waiting-backgroundrow type to the chat timeline that displays an animated indicator with a label when background tasks are pending but the run is not actively working.ClaudeAdapterV2to track background task rosters per native thread using snapshot (background_tasks_changed) and incremental (task_started/task_notification) messages, and emitprovider_thread.updatedevents withpendingBackgroundTaskson turn settlement.ProviderRuntimeRecoveryServiceto cancel stale background-capable turn items and clear provider-thread rosters on startup recovery.📊 Macroscope summarized e46af0a. 11 files reviewed, 0 issues evaluated, 0 issues filtered, 0 comments posted
🗂️ Filtered Issues
No issues evaluated.