π€ fix: stop sub-agent progress reports from arriving late and stale - #4100
Conversation
Progress reports (agent_report) queued behind a busy parent were held until the parent's turn ended whenever a hidden turn-end entry (a child's ancestor-bound peer message) sat at the queue head, and a reawakened child's terminal settlement never dropped its queued in-progress updates, so they later dispatched as stale turns. - MessageQueue: promoteAheadOfHiddenTurnEnd enqueues a tool-end send ahead of hidden turn-end predecessors (never past user-authored ones). - reportAgentProgress: execution-scoped dedupe keys + promotion flag. - WorkspaceTurnManager.settleWorkspaceTurn: drop that execution's queued progress before waiters resolve. --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
This comment has been minimized.
This comment has been minimized.
|
@codex review |
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 820dd63b97
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- Promote after the entry's muxMetadata/callbacks are populated so correlation revalidation keeps same-turn continuations intact. - Supersession removals pass skipCancelCallbacks so a dropped report's workspace-turn continuation callbacks cannot settle the owner's live delegated turn as interrupted (also applied to handleAgentReport). --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
|
@codex review |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 183b97c5a7
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
A progress entry dequeued into the parent's asynchronous PREPARING phase is invisible to prefix removal. Progress sends now carry a synchronous supersession probe (admissionStale) that the session re-checks at its turn-admission gates, and the wake helper's continuation-failure callbacks no-op for superseded wakes so a stale refusal never settles the parent's live delegated turn. --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
|
@codex review |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42548fae40
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The progress-report supersession probe now also flips for an original run that was interrupted without a report and for a continuation whose execution mirror moved to a successor generation (or was cleared). A send-time consistency check falls back to removal-only supersession if the mirror ever lags the active record, so a live update is never refused. --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
|
@codex review |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: aaaef5c5d8
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
- A probe that is already true at send time refuses the update instead of disabling the guard. - handleAgentReport drops queued updates right after the `reported` commit so a stale tool-end entry cannot cut the parent's turn while artifacts/goal attribution/patch generation are awaited. - A promoted tool-end send judges its workspace-turn correlation only against the entries it stays behind, so a queued heartbeat no longer strips the correlation of a report that dispatches ahead of it. --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
|
@codex review |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
π‘ Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5b9eabb719
βΉοΈ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with π.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Every interrupted transition passes through recordTaskInterrupted right after the status commit; remove the child's queued agent_report entries there so a stale tool-end head cannot cut the parent's active turn before its admission probe refuses it. --- _Generated with `xum` β’ Model: `anthropic:claude-fable-5-1` β’ Thinking: `xhigh` β’ Cost: `$18.84`_ <!-- mux-attribution: model=anthropic:claude-fable-5-1 thinking=xhigh costs=18.84 -->
|
@codex review |
|
Codex Review: Didn't find any major issues. π Reviewed commit: βΉοΈ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with π. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
Summary
Sub-agent
agent_reportprogress updates could reach the parent 8β40 minutes late, and β for a reawakened (workspace-turn continuation) child β kept arriving after the child's terminal report, each burning a full parent turn just to be dismissed as "a delayed interim report". This PR makes progress reports cut the parent's turn promptly even when a hidden turn-end entry sits ahead of them, and drops queued in-progress updates when the continuation that produced them settles.Background
Diagnosed from the session shown in the report (
Build React Native Xum mobile app, childMobile UI Engineer). The child was reawakened viatask_send_message; timeline (host clock):task_send_message(ancestor target β default turn-end), queued at the parent's FIFO headagent_reportprogress Γ2 β queued tool-end behind the peer messagedeliverPersistentChildWorkspaceTurnResultUnlockedin_progressreports dispatch as new parent turnsTwo independent causes:
MessageQueue.getNextDispatchableMode()(since π€ feat: refine transient transcript interactionsΒ #3790) only looks at the FIFO head, so the stream'shasQueuedMessages("tool-end")stop condition stays false while a hidden turn-end entry is at the head. Every childβancestor peer message defaults to turn-end, so any progress report queued behind one waits for the parent's natural turn end. (The peer messages themselves were later refused as "sender no longer active" β they never delivered, they only blocked.)handleAgentReportremoves queuedagent-report:<child>:entries when a child's original run reports, but the workspace-turn continuation path (WorkspaceTurnManager.settleWorkspaceTurn) never did, so stale updates survived the terminal report.Implementation
MessageQueue: new internal optionpromoteAheadOfHiddenTurnEnd. A tool-end entry flagged with it is inserted ahead of the contiguous run of hidden (non-user-authored) turn-end predecessors, stopping at the first user-authored or tool-end entry β the user's visible "wait for turn end" choice is never pulled forward (the π€ feat: refine transient transcript interactionsΒ #3790 invariant). Reorders re-runrevalidateWorkspaceTurnCorrelations()exactly likeprioritizeNextUserEntry.addInternalnow returns the entry soaddOnceattaches the dedupe key to the promoted entry rather than the tail.reportAgentProgress: sets the flag and scopes the dedupe key to the active continuation execution (agent-report:<child>:<executionId>:<toolCallId>), so a settlement drops exactly the updates it superseded and never a successor execution's. Prefix/reason literals are centralized insrc/constants/agentMessaging.ts.WorkspaceTurnManager.settleWorkspaceTurn: inside the settlement lock, before waiters resolve, removesagent-report:<child>:<handleId>:entries from the owner's queue, so a parent whosetask_awaitjust returned cannot be cut at its next step boundary by an update it already outran.skipCancelCallbacks: true(newTurnAdmissionHostoption), andwakeParentWorkspaceWithSyntheticMessageno-ops those callbacks for superseded wakes.admissionStalemechanism (stale once the child's status mirror showsreported/interrupted, or β for a continuation β a terminal execution status or a mirror that moved to a successor generation; all persisted before removal runs; a send-time consistency check falls back to removal-only supersession if the mirror ever lags), soAgentSession's turn-admission gates refuse a stale entry that prefix removal could no longer see. A probe already true at send time refuses the update outright.MessageQueue.hasAllWorkspaceTurnContinuationsAheadOfPromotedToolEnd, sharing the trailing-run helper with the promotion), so a queued heartbeat cannot strip the correlation of a report that dispatches ahead of it.handleAgentReportalso drops queued updates right after thereportedcommit rather than after artifact/goal/patch work. Interrupted transitions (recordTaskInterrupted, the choke point for task_stop and interrupt cascades) drop the child's queued updates the same way.Validation
chat.jsonlfiles to reconstruct the timeline above (per-toolexecutionStartedAtvs. parent delivery timestamps).reportAgentProgress, and settlement-time removal ordered before waiter resolution.make static-checkgreen;taskService,workspaceTurnManager*,workspaceService,messageQueuesuites: 1142 pass / 0 fail.Risks
getQueueCutSupersedeEvidencestill classifies a turn-end queued head asother_input(fail-toward-notify).Generated with
xumβ’ Model:anthropic:claude-fable-5-1β’ Thinking:xhighβ’ Cost:$18.84