Skip to content

Codex QueueStrip can keep processed follow-up prompts stuck after idle #241

Closed

Description

@Juliusolsson05

Problem

Saved debug bundles show a Codex queued prompt remaining in QueueStrip after the agent has already processed it and the session has returned to idle. This is separate from the earlier user-prompt ordering issue: here the stale queue item is still present in state-snapshot.json, so the HTML is faithfully rendering bad runtime state rather than merely leaving behind a stale DOM artifact.

Evidence

Saved debug manifest notes:

  • 2026-05-19T09:23:48.299Z note on bundle ~/.config/agent-code/debug-bundles/2026-05-19T09-23-36-093-21e655e8: weird thing? que is not cleared in this example UI wise.
  • 2026-05-19T09:26:18.873Z note on bundle ~/.config/agent-code/debug-bundles/2026-05-19T09-26-14-911-21e655e8: same issue, no user propmt

First bundle state:

  • manifest captured at 2026-05-19T09:23:35.966Z
  • sessionStatus: running
  • streamPhase: thinking
  • processActive: true
  • queuedMessages.length: 1
  • queued timestamp: 1779182503356 (2026-05-19T09:21:43.356Z)
  • queued content begins: After that, you can post a new issue for me. Actually, can you read the two scripts we had left? ...

Second bundle state:

  • manifest captured at 2026-05-19T09:26:14.828Z
  • sessionStatus: idle
  • streamPhase: idle
  • processActive: false
  • awaitingAssistant: false
  • queuedMessages.length: 1
  • same queued content and timestamp still present

HTML confirms the queue strip is rendering from state:

<div ... aria-label="queued messages">
  <div ...>1 queued</div>
  ... After that, you can post a new issue for me ...
</div>

The later screen / recentScreen in the same idle bundle do not contain that queued prompt text, but they do contain later assistant output and later user input. The assistant clearly progressed through subsequent turns while the local queue surface remained stuck.

Likely Cause

addOptimisticCodexUserEntry() queues follow-up Codex submits when shouldQueueOptimisticCodexUserEntry() sees a live semantic turn. The clearing path in useIpcSubscriptions.ts removes a queued item only when an authoritative Codex rollout user row maps to matching prompt text:

if (queuedMessages.some(q => codexPromptsMatchForOwnership(q.content, mappedText))) {
  queuedMessages = queuedMessages.filter(q =>
    !codexPromptsMatchForOwnership(q.content, mappedText),
  )
}

That assumes every queued follow-up will later appear as a committed user rollout entry. The saved bundle disproves that assumption: the prompt was submitted/processed, but no matching committed user row arrived in the captured durable feed, leaving queuedMessages permanently non-empty.

Desired Behavior

A queued prompt should not remain visible after the app has convincing evidence that the queued turn has been accepted or superseded.

Potential fixes to investigate:

  • Clear the head queued item when a queued follow-up transitions into a new provider turn, even if no committed user rollout row arrives.
  • Add an idle/session-complete reconciliation guard: if streamPhase === 'idle', processActive === false, and awaitingAssistant === false, queuedMessages should be empty unless the provider explicitly reports pending queued work.
  • Emit feed-debug events when queue items are added, reconciled by committed user row, or force-cleared by lifecycle state, so future saved bundles show exactly which invariant fired.

Acceptance Criteria

  • Add a regression fixture/script for: queued Codex follow-up, provider responds, no matching committed user rollout row, session returns idle.
  • The queued strip disappears by the time the session is idle.
  • Existing behavior still preserves genuinely pending follow-up prompts during an active semantic turn.
  • Saved debug bundle state cannot show sessionStatus: idle, streamPhase: idle, processActive: false, awaitingAssistant: false, and queuedMessages.length > 0 for a Codex pane unless there is a real provider queue signal explaining it.

Related

Activity

  1. Juliusolsson05 commented on May 19, 2026

    @Juliusolsson05
    OwnerAuthor

    Review-agent correction

    The stale QueueStrip symptom is confirmed, but the root-cause statement needs correction.

    Confirmed:

    • queuedMessages.length === 1 persisted while the saved runtime was idle:
      • sessionStatus: "idle"
      • streamPhase: "idle"
      • processActive: false
      • awaitingAssistant: false
    • The DOM was faithfully rendering QueueStrip from that stale queuedMessages state.
    • Queue items are only cleared by the renderer when a mapped Codex rollout user entry matches the queued prompt text.

    Correction:

    The issue body says the matching committed user rollout row never appeared in the captured durable feed. A review agent found that the actual Codex rollout file did contain the matching user row on disk.

    Evidence from review:

    • ~/.codex/sessions/2026/05/19/rollout-2026-05-19T10-12-27-019e3f4b-1c22-7962-b85b-0f09f03eb68d.jsonl:1063 was a response_item user message with the queued STT prompt.
    • :1064 was the paired event_msg.user_message.
    • Later rollout lines showed the agent processed that follow-up and answered it.

    Sharper likely cause:

    The renderer's committed-rollout ingestion/tailing appears to have gone stale or attached to an old cursor. The saved bundles reported lastJsonlEntryAt: 1779181656586 (2026-05-19T09:07:36.586Z), while the queued prompt committed later at 2026-05-19T09:21:44.497Z. So the clearing logic probably never saw the committed user row, even though it existed on disk and should have mapped through the rollout ingestion path.

    Code references from review:

    • src/renderer/src/workspace/hook/actions/streaming.ts: queued follow-up prompts are added when a semantic turn is live.
    • src/renderer/src/workspace/hook/ipc/useIpcSubscriptions.ts: queued prompts are cleared only when a mapped rollout user entry matches.
    • src/renderer/src/workspace/tile-tree/TileLeaf/QueueStrip.tsx: QueueStrip directly renders queuedMessages.
    • src/renderer/src/workspace/codex/rollout.ts: expected mapping path for committed Codex rollout user rows.

    The desired idle invariant is still reasonable: an idle Codex pane should not retain stale queuedMessages. But the fix should investigate rollout tail/ingestion staleness, not only queue reconciliation matching.

  2. added a commit that references this issue on Jun 21, 2026
  3. Juliusolsson05 commented on Jun 21, 2026

    @Juliusolsson05
    OwnerAuthor

    Fixed and merged in #304 (fix(#241): clear stale Codex queued prompts via idle-convergence reconciliation). Closing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions