Skip to content

fix(ui): preserve question queue order when upserting duplicate requests#479

Open
JDis03 wants to merge 1 commit into
NeuralNomadsAI:devfrom
JDis03:fix/question-queue-ordering-448
Open

fix(ui): preserve question queue order when upserting duplicate requests#479
JDis03 wants to merge 1 commit into
NeuralNomadsAI:devfrom
JDis03:fix/question-queue-ordering-448

Conversation

@JDis03
Copy link
Copy Markdown
Contributor

@JDis03 JDis03 commented May 18, 2026

Summary

Root Cause

upsertQuestion replaced the entire queue entry on duplicate request id, losing the original enqueuedAt value. If question-1 arrived at t=1000 (global), then question-2 at t=1500, and question-1 resolved to a tool part at t=2000, the newer enqueuedAt (2000) would place question-1 behind question-2 in the sorted queue, breaking active interruption ordering.

Fix

  • Add mergeQuestionEntry mirroring mergePermissionEntry behavior
  • Preserve original enqueuedAt using Math.min(existing, new)
  • Keep newer messageId/partId when resolving from global to tool part
  • Clean up stale byMessage entries for the same request id
  • Sort queue by enqueuedAt and recalculate active after each upsert

Validation

  • npm run typecheck --workspace @codenomad/ui
  • node --import tsx --test packages/ui/src/stores/message-v2/instance-store.test.ts
  • Added 3 regression tests covering duplicate resolution and ordering

Fixes NeuralNomadsAI#448 - Questions can deadlock with "Waiting for earlier responses"
when a question arrives first as a global entry and later resolves to a
tool part with a newer enqueuedAt timestamp.

Root cause:
- upsertQuestion replaced the entire queue entry on duplicate request id,
  losing the original enqueuedAt value.
- If question-1 arrived at t=1000 (global), then question-2 at t=1500,
  and question-1 resolved to a tool part at t=2000, the newer enqueuedAt
  (2000) would place question-1 behind question-2 in the sorted queue.
- This broke the active interruption ordering, leaving both questions
  showing "Waiting for earlier responses."

Fix:
- Add mergeQuestionEntry mirroring mergePermissionEntry behavior.
- Preserve original enqueuedAt using Math.min(existing, new).
- Keep newer messageId/partId when resolving from global to tool part.
- Clean up stale byMessage entries for the same request id.
- Sort queue by enqueuedAt and recalculate active after each upsert.

Validation:
- npm run typecheck --workspace @codenomad/ui
- node --import tsx --test packages/ui/src/stores/message-v2/instance-store.test.ts
- Added 3 regression tests covering duplicate resolution and ordering.
@JDis03 JDis03 force-pushed the fix/question-queue-ordering-448 branch from 6636745 to 4dc05d3 Compare May 18, 2026 13:06
@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/26034514624

Artifacts expire in 7 days.
Artifacts:

  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-tauri-macos
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-electron-macos
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-tauri-linux
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-tauri-windows
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-tauri-macos-arm64
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-electron-windows
  • pr-479-66367454adf9ba9c7603ea45be110e1e0ed1fe92-electron-linux

@github-actions
Copy link
Copy Markdown

PR builds are available as GitHub Actions artifacts:

https://github.com/NeuralNomadsAI/CodeNomad/actions/runs/26035402900

Artifacts expire in 7 days.
Artifacts:

  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-tauri-macos
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-tauri-linux
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-tauri-windows
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-electron-macos
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-tauri-macos-arm64
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-electron-windows
  • pr-479-4dc05d3773ea6e82137836637d00b9b89859471e-electron-linux

@pascalandr
Copy link
Copy Markdown
Contributor

@CodeNomadBot

I want you to perform the gatekeeper checks for latest PR state

check for regressions
check for better possible implementations
Be the PR gatekeeper
Be ruthless about code quality

@codenomadbot
Copy link
Copy Markdown
Contributor

codenomadbot Bot commented May 25, 2026

Gatekeeper review complete for latest PR state.

Request understood: perform a strict review for regressions, implementation quality, and whether this should pass as PR gatekeeper. Assumption: review-only, no code changes requested.

Findings: none blocking.

Review notes:

  • The fix correctly mirrors the permission queue pattern for question entries: duplicate request IDs are de-duped from byMessage, the queue entry is replaced in-place, the queue is sorted by preserved enqueue time, and active is recalculated from the first queue item.
  • Preserving enqueuedAt with Math.min(existing.enqueuedAt, entry.enqueuedAt) addresses the reported deadlock/order regression where a global question later resolves to a tool part with a newer timestamp.
  • Keeping newer messageId/partId while preserving original ordering is the right behavior for global-to-tool resolution.
  • Regression coverage is targeted and meaningful: duplicate global-to-tool movement, active recalculation after removal, and original enqueue ordering across multiple questions.

Validation run:

  • npm run typecheck --workspace @codenomad/ui passed.
  • node --import tsx --test packages/ui/src/stores/message-v2/instance-store.test.ts passed, 5 tests.

Environment note:

  • The worktree initially had no installed root dependencies, so the first validation attempt failed to resolve packages like solid-js and tsx. I ran npm ci from the existing lockfile and reran validation successfully.
  • The worktree has an unrelated local modification in .opencode/package-lock.json; I ignored it for this PR review.

Non-blocking quality note:

  • packages/ui/src/stores/message-v2/instance-store.ts is already large at about 1,288 lines. This PR only adds a small focused block, so I would not block or force a refactor here, but future message-store work should consider splitting queue/interrupt state helpers out of the monolith.

Gatekeeper verdict: approve from code-quality and regression-risk perspective.

--
Yours,
CodeNomadBot

@pascalandr
Copy link
Copy Markdown
Contributor

pascalandr commented May 25, 2026

I’m not sure this fully fixes #448.
This PR fixes a narrow question upsert/order case, but the issue discussion points to a broader problem: the blocking interruption is not visible/actionable, and the user gets stuck on “Waiting for earlier responses”.
This PR does not touch activeInterruption, the permission/question center, auto-opening behavior, or any way to act on the blocking item.
Reproduction validation from the reporter is needed.

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.

[Bug]: questions do not open and garble leading to deadlock "Waiting for earlier responses"

2 participants