Thanks to visit codestin.com
Credit goes to github.com

Skip to content

πŸ€– fix: stop sub-agent progress reports from arriving late and stale - #4100

Merged
ThomasK33 merged 6 commits into
mainfrom
fix/delayed-subagent-progress-reports
Sep 5, 2026
Merged

ThomasK33 merged 6 commits into
mainfrom
fix/delayed-subagent-progress-reports

Conversation

@ThomasK33

@ThomasK33 ThomasK33 commented Sep 5, 2026

Copy link
Copy Markdown
Member

Summary

Sub-agent agent_report progress 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, child Mobile UI Engineer). The child was reawakened via task_send_message; timeline (host clock):

time event
17:33:12 child β†’ parent task_send_message (ancestor target β‡’ default turn-end), queued at the parent's FIFO head
17:39:35 / 17:41:53 child agent_report progress Γ—2 β†’ queued tool-end behind the peer message
17:42:21 continuation completes; terminal report appended via deliverPersistentChildWorkspaceTurnResultUnlocked
17:59:15 / 17:59:58 parent's turn finally ends β†’ peer message refused at admission (sender inactive) β†’ both stale in_progress reports dispatch as new parent turns

Two independent causes:

  1. Head-of-queue gating. MessageQueue.getNextDispatchableMode() (since πŸ€– feat: refine transient transcript interactionsΒ #3790) only looks at the FIFO head, so the stream's hasQueuedMessages("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.)
  2. Missing cleanup on continuation settlement. handleAgentReport removes queued agent-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 option promoteAheadOfHiddenTurnEnd. 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-run revalidateWorkspaceTurnCorrelations() exactly like prioritizeNextUserEntry. addInternal now returns the entry so addOnce attaches 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 in src/constants/agentMessaging.ts.
  • WorkspaceTurnManager.settleWorkspaceTurn: inside the settlement lock, before waiters resolve, removes agent-report:<child>:<handleId>: entries from the owner's queue, so a parent whose task_await just returned cannot be cut at its next step boundary by an update it already outran.
  • Supersession must not fail the parent's own turn (Codex round 1): when the parent itself runs as a delegated workspace turn, each queued report carries continuation-failure callbacks. Both removal sites pass skipCancelCallbacks: true (new TurnAdmissionHost option), and wakeParentWorkspaceWithSyntheticMessage no-ops those callbacks for superseded wakes.
  • Entries already dequeued into PREPARING (Codex round 2): progress sends carry a synchronous supersession probe through the existing admissionStale mechanism (stale once the child's status mirror shows reported/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), so AgentSession'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.
  • Promotion-aware correlation (Codex round 4): a promoted tool-end send judges whether a queued predecessor supersedes its workspace-turn correlation only against the entries it stays behind (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. handleAgentReport also drops queued updates right after the reported commit 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

  • Replayed the affected session's chat.jsonl files to reconstruct the timeline above (per-tool executionStartedAt vs. parent delivery timestamps).
  • New unit tests: queue promotion (head becomes tool-end; user-authored turn-end barrier; FIFO preserved behind earlier tool-end entries; skipped continuation correlation stripped), execution-scoped key + flag on reportAgentProgress, and settlement-time removal ordered before waiter resolution.
  • make static-check green; taskService, workspaceTurnManager*, workspaceService, messageQueue suites: 1142 pass / 0 fail.

Risks

  • Queue ordering (medium, parent/child orchestration). Hidden turn-end entries can now dispatch later than a progress report queued after them. This only affects non-user-authored entries; peer messages already tolerate reordering (correlation revalidation is the same path used by "Send now" and the dispatch-mode dropdown). getQueueCutSupersedeEvidence still classifies a turn-end queued head as other_input (fail-toward-notify).
  • Dropped updates (low). Removal is execution-scoped and happens only at terminal settlement, mirroring the existing original-run behavior; foreground waiters and the direct-parent envelope still deliver the terminal result.

Generated with xum β€’ Model: anthropic:claude-fable-5-1 β€’ Thinking: xhigh β€’ Cost: $18.84

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 -->
@chatgpt-codex-connector

This comment has been minimized.

@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ 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".

Comment thread src/node/services/workspaceTurnManager.ts Outdated
Comment thread src/node/services/messageQueue.ts Outdated
- 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 -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ 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".

Comment thread src/node/services/workspaceTurnManager.ts
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 -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ 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".

Comment thread src/node/services/taskService.ts Outdated
Comment thread src/node/services/taskService.ts Outdated
Comment thread src/node/services/taskService.ts
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 -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ 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".

Comment thread src/node/services/taskService.ts Outdated
Comment thread src/node/services/taskService.ts
Comment thread src/node/services/taskService.ts Outdated
- 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 -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

This comment has been minimized.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

πŸ’‘ 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".

Comment thread src/node/services/taskService.ts
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 -->
@ThomasK33

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. πŸš€

Reviewed commit: 43e95589c6

ℹ️ 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".

@chatgpt-codex-connector

This comment has been minimized.

@ThomasK33
ThomasK33 added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 5cca5bf Sep 5, 2026
19 of 20 checks passed
@ThomasK33
ThomasK33 deleted the fix/delayed-subagent-progress-reports branch September 5, 2026 21:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant