Skip to content

fix(web-shell): suppress stale pending prompt refresh errors - #6352

Merged
wenshao merged 1 commit into
QwenLM:mainfrom
chiga0:fix/suppress-superseded-pending-refresh
Jul 6, 2026
Merged

fix(web-shell): suppress stale pending prompt refresh errors#6352
wenshao merged 1 commit into
QwenLM:mainfrom
chiga0:fix/suppress-superseded-pending-refresh

Conversation

@ytahdn

@ytahdn ytahdn commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR makes pending prompt refresh results explicit so the web shell can distinguish a real refresh failure from a refresh that was skipped or superseded by a newer request. Queue insert, edit, and delete flows now only show the pending prompt refresh error when the refresh request itself fails.

Why it's needed

When a queued prompt is inserted, edited, or removed, the web shell removes the server-side pending prompt and then refreshes the pending prompt list. The refresh helper previously returned false for several different outcomes, including normal races where a later refresh superseded the current one. Those benign races were treated as user-visible failures, which could show misleading messages such as “Queue changed but pending prompts could not refresh” even though the queue mutation had already succeeded.

Reviewer Test Plan

How to verify

Review the queue insert/edit/delete path and confirm that a superseded or skipped pending prompt refresh no longer reports a user-visible error after removePendingPrompt succeeds. A reviewer can also trigger rapid queued prompt changes while a turn is active and confirm that stale refreshes do not surface the “pending prompts could not refresh” error unless the pending prompt fetch actually fails.

Evidence (Before & After)

Before: a stale pending prompt refresh could return the same failure value as an actual request error, causing a misleading user-visible queue error after a successful queue mutation.

After: refresh results distinguish refreshed, skipped, superseded, and failed; only failed reports the pending prompt refresh error after a successful queue mutation.

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

Local validation: npx prettier --check packages/web-shell/client/hooks/useQueuedPrompts.ts; npm run build --workspace=packages/webui; npm run build --workspace=packages/sdk-typescript; npm run build --workspace=packages/web-shell.

Risk & Scope

  • Main risk or tradeoff: The queue UI still relies on the follow-up refresh to reconcile the visible queue after a successful server-side removal, so this PR intentionally changes error reporting semantics without changing queue reconciliation behavior.
  • Not validated / out of scope: Full end-to-end browser reproduction across all operating systems was not run locally.
  • Breaking changes / migration notes: None.

Linked Issues

N/A

中文说明

What this PR does

这个 PR 让 pending prompt 的刷新结果变得更明确,因此 web shell 可以区分真正的刷新失败,以及刷新被跳过或被更新请求覆盖的正常情况。队列插入、编辑、删除流程现在只会在刷新请求本身失败时显示 pending prompt 刷新错误。

Why it's needed

当用户插入、编辑或移除一个排队 prompt 时,web shell 会先移除服务端的 pending prompt,然后刷新 pending prompt 列表。之前刷新辅助函数对多种结果都返回 false,其中包括后续刷新覆盖当前刷新这种正常竞态。这类正常竞态会被当成用户可见失败,导致即使队列变更已经成功,也可能显示 “Queue changed but pending prompts could not refresh” 这类误导提示。

Reviewer Test Plan

How to verify

检查队列插入、编辑、删除路径,确认 removePendingPrompt 成功后,如果 pending prompt 刷新被覆盖或跳过,不再报告用户可见错误。审核者也可以在一个 turn 正在运行时快速变更多个队列 prompt,确认过期刷新不会弹出 “pending prompts could not refresh” 错误,除非 pending prompt 获取请求本身确实失败。

Evidence (Before & After)

Before:过期的 pending prompt 刷新会返回和真实请求错误相同的失败值,导致成功变更队列后仍可能出现误导性的用户可见队列错误。

After:刷新结果区分为 refreshedskippedsupersededfailed;成功变更队列后,只有 failed 会报告 pending prompt 刷新错误。

Tested on

OS Status
🍏 macOS ✅ tested
🪟 Windows ⚠️ not tested
🐧 Linux ⚠️ not tested

Environment (optional)

本地验证:npx prettier --check packages/web-shell/client/hooks/useQueuedPrompts.tsnpm run build --workspace=packages/webuinpm run build --workspace=packages/sdk-typescriptnpm run build --workspace=packages/web-shell

Risk & Scope

  • Main risk or tradeoff:队列 UI 在服务端移除成功后仍依赖后续刷新来校准可见队列,所以这个 PR 只调整错误报告语义,不改变队列校准行为。
  • Not validated / out of scope:本地没有在所有操作系统上跑完整浏览器端到端复现。
  • Breaking changes / migration notes:无。

Linked Issues

N/A

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

Problem: The refreshPendingPrompts helper returning false for multiple distinct outcomes (skipped, superseded, failed) is a real code-level ambiguity — callers can't distinguish benign races from actual failures. However, no reproduction is provided, and the PR body uses conditional language ("could show misleading messages"). Has this been observed by a user, or is this a proactive cleanup? The race condition scenario (rapid queue mutations while a turn is active) is plausible and specific, so I'll proceed — but a before/after reproduction would strengthen this.

Direction: Aligned. Web-shell queued-prompt UX is an active area (CHANGELOG #6025). Fixing error-reporting semantics so that benign races don't surface misleading messages is a reasonable incremental improvement.

Approach: The discriminated union ('refreshed' | 'skipped' | 'superseded' | 'failed') replacing a boolean is clean and minimal. One observation: the PR body mentions "insert, edit, and delete flows" but the diff only changes removeServerPromptForAction (which handles edit/delete). The insert path already calls refreshPendingPrompts fire-and-forget (void) and never checks the return value — so it was never affected. The PR description could be more precise about scope.

Two call-site changes with asymmetric conditions — === 'failed' on the success path, !== 'refreshed' on the catch path. The catch path restores queued prompts for any non-refresh result (including superseded), which is more conservative than the success path. This makes sense as a safety default but is worth calling out.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

问题: refreshPendingPrompts 对多种结果(skipped、superseded、failed)都返回 false,确实是一个代码层面的歧义——调用方无法区分正常竞态和真正的失败。但 PR 未提供复现,且使用了条件语气("could show misleading messages")。这是用户实际观察到的,还是主动加固?竞态场景(turn 活跃时快速变更队列)是合理的,我先继续审查——但 before/after 复现会更有说服力。

方向: 对齐。Web-shell 排队 prompt UX 是活跃开发领域(CHANGELOG #6025)。修复错误报告语义,避免正常竞态显示误导性信息,是合理的增量改进。

方案: 用可辨识联合类型('refreshed' | 'skipped' | 'superseded' | 'failed')替代布尔值,干净且最小化。一个观察:PR 描述提到"插入、编辑、删除流程",但 diff 只改了 removeServerPromptForAction(处理编辑和删除)。插入路径已经以 void 方式调用 refreshPendingPrompts,从不检查返回值——因此从未受影响。PR 描述在范围上可以更精确。

两处调用点使用了不对称的条件——成功路径用 === 'failed',catch 路径用 !== 'refreshed'。catch 路径对任何非刷新结果(包括 superseded)都恢复队列 prompt,比成功路径更保守。作为安全默认值这是合理的,但值得指出。

进入代码审查 🔍

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Code Review

Independent proposal: I'd replace the boolean return with a discriminated union, map each early-exit to a semantically correct value, and update the two callers to check only for 'failed' on the success path while being more conservative on the catch path.

The PR matches this approach exactly. The four return sites in refreshPendingPrompts map cleanly:

  • Connection/session guard → 'skipped' (not reached, nothing to do)
  • Stale-session guard → 'skipped' (same)
  • Sequence-number race → 'superseded' (a newer request took over — this is the core fix)
  • Post-sync session mismatch → 'skipped'
  • Success → 'refreshed'
  • Exception → 'failed'

Both call sites in removeServerPromptForAction are correct. The success path uses === 'failed' (only report error on actual failure — the stated goal). The catch path uses !== 'refreshed' (restore queued prompts on any non-success — conservative and appropriate for error recovery).

No correctness bugs, security issues, or convention violations found. One observation: the PR body claims "insert, edit, and delete" but the diff only touches removeServerPromptForAction (edit/delete). The insert path was never affected — it calls refreshPendingPrompts fire-and-forget without checking the return value.

Build & Tests

vite build (web-shell):          ✓ built in 8.18s
vite lib build (web-shell):      ✓ built in 1.33s
tsc --noEmit (web-shell):        ✓ clean
prettier --check (changed file): ✓ passes
vitest (web-shell):              ✓ 1016/1016 tests pass (67 test files + build-artifact)

Real-Scenario Testing

This PR changes web-shell error-reporting semantics for a race condition that requires a browser client and specific async timing (rapid queue mutations during an active turn). The race condition cannot be driven from tmux — it needs a browser connected to a running daemon with concurrent turn activity.

Web-shell dev server smoke test (PR code):

$ cd packages/web-shell && npx vite --port 5199

  VITE v5.4.21  ready in 173 ms

  ➜  Local:   http://localhost:5199/
  ➜  Network: use --host to expose

$ curl -s http://localhost:5199/ | head -5
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module">import { injectIntoGlobalHook } from "/@react-refresh";
    injectIntoGlobalHook(window);

Server starts cleanly and serves the app without errors. The behavior change (suppressing misleading error messages on superseded refreshes) is internal to the hook's error-reporting logic and only manifests under the specific race condition timing.

中文说明

代码审查

独立方案:将布尔返回值替换为可辨识联合类型,将每个提前退出映射到语义正确的值,成功路径只检查 'failed',catch 路径更保守。

PR 完全匹配此方案。refreshPendingPrompts 中四个返回点映射清晰:

  • 连接/会话守卫 → 'skipped'
  • 过期会话守卫 → 'skipped'
  • 序列号竞态 → 'superseded'(核心修复)
  • 同步后会话不匹配 → 'skipped'
  • 成功 → 'refreshed'
  • 异常 → 'failed'

removeServerPromptForAction 中两个调用点正确。成功路径用 === 'failed'(只在真正失败时报告错误)。catch 路径用 !== 'refreshed'(任何非成功状态都恢复队列 prompt——保守且适合错误恢复)。

无正确性 bug、安全问题或规范违反。一个观察:PR 描述声称"插入、编辑、删除",但 diff 只改了 removeServerPromptForAction(编辑/删除)。插入路径从未受影响——它以 void 方式调用 refreshPendingPrompts,不检查返回值。

构建与测试

vite build (web-shell):          ✓ 8.18s 构建完成
vite lib build (web-shell):      ✓ 1.33s 构建完成
tsc --noEmit (web-shell):        ✓ 无错误
prettier --check (变更文件):     ✓ 通过
vitest (web-shell):              ✓ 1016/1016 测试通过

真实场景测试

此 PR 修改了 web-shell 的错误报告语义,针对的是需要浏览器客户端和特定时序(活跃 turn 期间快速队列变更)的竞态条件。该竞态无法通过 tmux 驱动——需要连接到运行中 daemon 的浏览器。

Web-shell 开发服务器冒烟测试显示服务器正常启动,无错误。行为变更仅在特定竞态时序下才会触发。

Qwen Code · qwen3.7-max

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

This is a small, focused fix that solves a real code-level problem: refreshPendingPrompts returned false for four distinct outcomes, and callers couldn't tell a benign race from an actual failure. The discriminated union is the right tool for this job, and the PR applies it cleanly — every changed line earns its place.

Build is green, all 1016 tests pass, web-shell serves without errors. The specific race condition (superseded refresh during rapid queue mutations) can't be driven from tmux, but the code change is narrow enough that the risk is low: only removeServerPromptForAction's error-reporting paths are affected, and both conditions are correct for their context.

One honest reservation: the PR body mentions "insert, edit, and delete" but the diff only touches edit/delete via removeServerPromptForAction. The insert path was never affected. Not a blocker — the fix still covers the cases that needed it — but the description could be tighter.

The lack of a reproduction is noted but not blocking. The boolean conflation is a verifiable code smell, and the fix makes the code strictly more correct regardless of whether a user has hit the specific timing window.

Approving. ✅

中文说明

这是一个小而集中的修复,解决了一个真实的代码层面问题:refreshPendingPrompts 对四种不同结果都返回 false,调用方无法区分正常竞态和真正的失败。可辨识联合类型是正确的工具,PR 干净地应用了它——每一行变更都有其必要性。

构建通过,1016 个测试全部通过,web-shell 无错误运行。特定的竞态条件(快速队列变更期间被覆盖的刷新)无法通过 tmux 驱动,但代码变更范围足够窄,风险很低:仅影响 removeServerPromptForAction 的错误报告路径,两个条件对其各自的上下文都是正确的。

一个诚实的保留意见:PR 描述提到"插入、编辑、删除",但 diff 只通过 removeServerPromptForAction 修改了编辑/删除。插入路径从未受影响。不是阻塞问题——修复仍然覆盖了需要修复的场景——但描述可以更精确。

缺乏复现已记录但不构成阻塞。布尔值混淆是可验证的代码异味,无论用户是否真正触发了特定的时序窗口,修复都使代码更加正确。

批准。✅

Qwen Code · qwen3.7-max

@qwen-code-ci-bot qwen-code-ci-bot left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM, looks ready to ship. ✅

@ytahdn
ytahdn requested review from qqqys and wenshao and removed request for qqqys July 6, 2026 02:58

@wenshao wenshao left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

⚠️ Downgraded from Approve to Comment: CI still running.

No blockers found. Suggestion-level recommendations are in the Suggestion summary comment below.

— qwen3.7-max via Qwen Code /review

@wenshao

wenshao commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Suggestions — commit 44b6b3ab

File Issue Suggested fix
useQueuedPrompts.ts:606,623 Asymmetric predicates (=== 'failed' vs !== 'refreshed') lack comments explaining why the two call sites use different tolerance levels. Future maintainer risk of "normalizing" them. Add 1-2 line comments at each call site explaining the optimistic vs conservative rationale.
history-store.ts:143-196 No logging in the recovery path. If a corrupted history file loses entries during compaction, it would be invisible in logs. Add log.debug when recovery produces 0 entries from a non-empty line, or when a glued line is split into multiple entries.
history-store.ts:143 recoverHistoryObjectsFromLine silently drops top-level JSON arrays — the assumption "entries are always plain objects" is implicit and undocumented. Add a one-line comment at the top of the function: // Assumes entries are always plain JSON objects, not arrays or primitives.
history-store.test.ts Missing test for unterminated string input (e.g., crash mid-serialization leaves {"id":"a1","data":"unterminated). The function handles this correctly but the edge case deserves coverage. Add a test case with a line containing an unterminated string, asserting the complete objects before it are preserved and the incomplete one is dropped.
useQueuedPrompts.ts:55-60 RefreshPendingPromptsResult type has no JSDoc explaining when each variant is returned or how callers should react. Add a JSDoc block documenting each variant's semantics and the expected caller behavior.

— qwen3.7-max via Qwen Code /review

@wenshao
wenshao added this pull request to the merge queue Jul 6, 2026
Merged via the queue into QwenLM:main with commit fa6e0f9 Jul 6, 2026
38 of 39 checks passed
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.

3 participants