Skip to content

fix(web-shell): label usage-limited goal cards correctly - #10553

Open
yu-xin-c wants to merge 4 commits into
QwenLM:mainfrom
yu-xin-c:codex/web-shell-usage-limited-goal
Open

fix(web-shell): label usage-limited goal cards correctly#10553
yu-xin-c wants to merge 4 commits into
QwenLM:mainfrom
yu-xin-c:codex/web-shell-usage-limited-goal

Conversation

@yu-xin-c

@yu-xin-c yu-xin-c commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Web Shell now labels a Goal transcript card as Goal usage limited when the canonical V2 Goal state says usage_limited, instead of displaying the legacy projection's broader Goal aborted label. The daemon adapter reconciles only the specific pair goalStatus.kind: aborted plus goalState.goal.status: usage_limited; the legacy wire value remains unchanged for older clients. The message component accepts the restored kind, renders it with the existing warning treatment, keeps the last-check reason visible, and provides matching English and Chinese copy.

Regression coverage pins both layers: daemon event normalization restores the canonical kind, and the rendered system message is distinct from the aborted label in English and Chinese.

Why it's needed

V2 Goal updates carry both a canonical goalState snapshot and a compatibility goalStatus card. The compatibility projection intentionally collapses blocked and usage_limited into aborted, but Web Shell was rendering only that collapsed value. As a result, the transcript said Goal aborted while the status strip correctly said Usage limited, obscuring a recoverable resource boundary as a generic abort.

Reviewer Test Plan

How to verify

  1. Feed Web Shell an agent_message_chunk whose metadata contains goalStatus.kind: aborted and a matching V2 goalState.goal.status: usage_limited.
  2. Confirm the normalized transcript block has data.kind: usage_limited while the incoming compatibility card remains unchanged.
  3. Render the transcript card and confirm it says Goal usage limited, preserves turns, duration, objective, and last-check reason, and does not say Goal aborted.
  4. Run npm test --workspace=packages/web-shell -- --reporter=default: 233 files and 5,241 tests pass.
  5. Run npm run typecheck --workspace=packages/web-shell and npm run lint --workspace=packages/web-shell: both pass.

Evidence (Before & After)

A deterministic mock-daemon visual scenario now renders this exact paired status in dark and light themes. Both local Playwright captures pass and show Goal usage limited with turns, duration, objective, and reason preserved; the repository visual workflow publishes the generated evidence on each push.

Tested on

OS Status
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

Environment (optional)

macOS, Node.js 22.22.2. The repository npm ci prepare build, Web Shell build, typecheck, lint, changed-file formatting check, targeted tests (306/306), the dark/light Playwright visual scenario (2/2), and full Web Shell tests (5,241/5,241) passed.

The package-wide formatting check still reports nine pre-existing files outside this diff; all six changed files pass Prettier.

Risk & Scope

  • Main risk or tradeoff: metadata could contain conflicting legacy and canonical statuses. The reconciliation is deliberately narrow and changes only aborted when the co-emitted canonical status is exactly usage_limited.
  • Not validated / out of scope: Windows and Linux runtime testing; the separate Goal edit-semantics and unrelated deferred findings in Deferred review findings from PR #9891 #10046; changing how blocked projects for legacy clients.
  • Breaking changes / migration notes: none. The public compatibility projection and legacy terminal metadata are unchanged.

Linked Issues

Addresses the first Web Shell transcript-card finding in #10046 without closing the multi-finding issue.

中文说明

这个 PR 做了什么

当规范的 V2 Goal 状态为 usage_limited 时,Web Shell 现在会把 Goal 历史消息卡标记为 Goal usage limited,不再显示旧兼容投影中更宽泛的 Goal aborted。daemon 适配层只协调这一组特定组合:goalStatus.kind: abortedgoalState.goal.status: usage_limited;面向旧客户端的 wire 值保持不变。消息组件接受恢复后的类型,沿用现有警告色,继续显示上次检查原因,并提供对应的英文和中文文案。

回归测试覆盖两个层次:daemon 事件归一化会恢复规范类型,渲染后的系统消息在英文和中文下都与 aborted 标签明确区分。

为什么需要

V2 Goal 更新同时携带规范的 goalState 快照和兼容用的 goalStatus 卡片。兼容投影会有意把 blockedusage_limited 都折叠为 aborted,但 Web Shell 之前只渲染这个已经折叠的值。因此历史消息显示 Goal aborted,状态条却正确显示 Usage limited,把一个可以恢复的资源边界误写成了通用中止。

Reviewer Test Plan

如何验证

  1. 向 Web Shell 输入一个 agent_message_chunk,其 metadata 同时包含 goalStatus.kind: aborted 和匹配的 V2 goalState.goal.status: usage_limited
  2. 确认归一化后的历史消息块为 data.kind: usage_limited,而输入的兼容卡片本身没有改变。
  3. 渲染历史消息卡,确认它显示 Goal usage limited,保留轮次、时长、目标和上次检查原因,并且不再显示 Goal aborted
  4. 运行 npm test --workspace=packages/web-shell -- --reporter=default:233 个文件、5,241 条测试全部通过。
  5. 运行 npm run typecheck --workspace=packages/web-shellnpm run lint --workspace=packages/web-shell:均通过。

证据(修改前后)

现在新增了一个确定性的 mock-daemon 视觉场景,在暗色和亮色主题下渲染这一精确的成对状态。本地两套 Playwright 截图均通过,显示 Goal usage limited,同时保留轮次、时长、目标和原因;仓库视觉工作流会在每次推送后发布生成的证据。

测试平台

系统 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

macOS,Node.js 22.22.2。仓库 npm ci 的 prepare 构建、Web Shell 构建、类型检查、lint、改动文件格式检查、针对性测试(306/306)、暗色/亮色 Playwright 视觉场景(2/2)和完整 Web Shell 测试(5,241/5,241)均通过。

包级格式检查仍会报告本 diff 之外的九个主线既有文件;本次修改的六个文件全部通过 Prettier。

风险与范围

  • 主要风险或取舍:metadata 可能携带互相冲突的旧状态和规范状态。协调逻辑刻意保持严格,只在旧状态为 aborted 且同一事件中的规范状态恰好为 usage_limited 时修改显示类型。
  • 未验证或范围之外:Windows 与 Linux 运行时测试;Deferred review findings from PR #9891 #10046 中单独的 Goal 编辑语义问题和其他延期发现;改变 blocked 对旧客户端的投影方式。
  • 破坏性变更或迁移说明:无。公开兼容投影和旧终止 metadata 均未改变。

关联 Issue

处理 #10046 中第一条 Web Shell 历史消息卡发现,但不会关闭这个包含多项发现的 issue。

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Approval still deferred — 1 PR CI workflow run(s) still in progress for 74a13d8; the finalize job approves automatically once every run lands green. finalize run

审批仍在延迟中 —— 74a13d8 还有 1 个 PR CI workflow 在运行,全部通过后 finalize 任务会自动提交审批。查看 finalize 运行

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓

  • Problem: observed, not theoretical. This implements the first deferred finding in Deferred review findings from PR #9891 #10046 — the Web Shell transcript card labeling usage_limited stops as Goal aborted while the status strip correctly shows Usage limited was verified real and pre-existing there, and explicitly deferred to "main or a dedicated Web Shell PR", which is exactly what this is. One evidence note: the before/after screenshots said to be attached in the first PR comment are not actually present in the thread — worth re-attaching for reviewers, though the verified finding is sufficient evidence of the bug on its own.
  • Direction: aligned. A correctness fix for a user-visible mislabel in Web Shell, and it deliberately avoids the riskier alternative (changing the legacy projection would change the wire value older clients see). CHANGELOG (reference agent): no direct entry for this label, but /goal status surfaces are actively iterated there, so the area is clearly live territory.
  • Size: not applicable — no core paths touched. Everything is inside packages/web-shell/client: 28 production lines across 3 files, 103 test lines across 2.
  • Approach: scope feels right. Reconciling the folded legacy card against the co-emitted canonical V2 state on the consumer side is the minimal fix; the reconciliation is intentionally narrow (only the aborted + usage_limited pair, copy instead of mutation). Nothing I'd cut.
  • Risk: no elevated risk signals — none of the changed files match the revert-correlated path list.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓

  • 问题:已观测到的问题,不是理论性的。本 PR 实现的是 Deferred review findings from PR #9891 #10046 中第一条被延期的发现——Web Shell 历史消息卡把 usage_limited 终止标记为 Goal aborted,而状态条却正确显示 Usage limited——该问题在那里已被验证为真实且主线既有,并明确延期到"main 或专门的 Web Shell PR"处理,本 PR 正是如此。一点证据上的提醒:描述中说"附在第一条 PR 评论"的修改前后截图实际并不在评论区——建议补上方便审阅,不过已验证的发现本身已足以证明该 bug。
  • 方向:对齐。这是 Web Shell 中用户可见标签错误的正确性修复,且刻意避开了更危险的替代方案(修改旧兼容投影会改变旧客户端看到的 wire 值)。CHANGELOG(参考实现):没有与此标签直接相关的条目,但 /goal 状态界面在那边持续迭代,该领域显然活跃。
  • 规模:不适用——未触及核心路径。所有改动都在 packages/web-shell/client 内:3 个文件 28 行生产代码,2 个文件 103 行测试。
  • 方案:范围合理。在消费端把折叠后的旧卡片与同发的规范 V2 状态做协调,是最小修复;协调逻辑刻意收窄(只处理 aborted + usage_limited 组合,拷贝而非原地修改)。没有可砍的部分。
  • 风险:无升级风险信号——改动文件均未命中与 revert 相关的路径列表。

进入代码审查 🔍

Qwen Code · qwen3.8-max

Reviewed at 74a13d80552af9b203738e1784874646b5c3c43c · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Code review

No blockers. Before reading the diff, my independent take on "transcript card says Goal aborted while the status strip says Usage limited" was: reconcile the folded compatibility card against the co-emitted canonical V2 state in the daemon-event normalization layer, leave the wire value alone, teach the card component the restored kind with the existing warning treatment, add both locales, test both layers. That is exactly what this diff does, so the review focused on coverage and invariants rather than the approach.

What I verified against the code on main:

  • Both emission paths are covered. Live events (MessageEmitter.buildGoalStateUpdate) and transcript replay (transcript-replayprojectSystemRecord) both put goalStatus and the canonical goalState into the same _meta, and normalizeGoalStatusEvent consumes goalStatus before goalTerminal — so live and replayed history both flow through the reconciled branch. The unpatched goalTerminal fallback only fires when no goalStatus is present, where there is no canonical state to reconcile against anyway; leaving it alone is correct, not a coverage gap.
  • Copy semantics. restoreCanonicalGoalStatusKind returns a new object only for the exact aborted + usage_limited pair and hands the status through untouched otherwise; the incoming card is never mutated.
  • The VALID_GOAL_KINDS addition is load-bearing. SystemMessage parses goal data through that gate, so without it the restored kind would be dropped and the card would not render at all.
  • Tests pin the change at both layers. The provider test fails against pre-PR behavior (the block's kind stays aborted), and the render test fails too (the gate rejects the kind, so the new copy never appears) — in English and Chinese.

Notes, not blockers: blocked intentionally still folds to Goal aborted (the finding's scope is usage_limited only, and the PR calls this out); and the before/after screenshots referenced in the description are still absent from the thread — worth re-attaching for human reviewers.

Testing evidence

Unattended CI run — I did not build or execute any PR code; the evidence below is the PR's own CI on the reviewed commit, fetched via the API at review time. The macOS/Windows test matrix jobs were skipped by the repo's PR classification profile (not failures); the ubuntu jobs carry the suite signal. Nothing red so far — three checks were still running at review time, and the table is updated by the finalize job once CI settles.

CI results for 74a13d8 — this table auto-updates as CI workflows complete:

Check Conclusion
Test (ubuntu-latest, Node 22.x) ⏳ running
Capture web-shell visuals (ubuntu-latest, Node 22.x) ✅ success
Classify PR ✅ success
Dependency CVE audit ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Secret scan (TruffleHog) ✅ success

One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。

The author's reported local numbers (5,241 web-shell tests passing) are their claim, not independently re-run here — the CI suite above is the evidence.

Sandboxed verification would settle what is still unproven: @qwen-code /verify — that in a live Web Shell session the transcript card renders Goal usage limited end-to-end when the daemon emits a paired goalStatus: aborted + goalState.goal.status: usage_limited update. The unit tests pin the normalization and render layers separately, but the full path in a real browser is unverified (the author's evidence covers macOS only, and the referenced screenshots are missing from the thread). The author lacks write access, so this would be a sponsored run: a maintainer's @qwen-code /verify approves the head it was written against and runs with a pre-execution risk screen and a full workspace wipe — read the resulting report with the same skepticism as the fork's own CI logs.

中文说明

代码审查

无阻塞问题。在读 diff 之前,我对"历史消息卡显示 Goal aborted 而状态条显示 Usage limited"的独立方案是:在 daemon 事件归一化层把折叠后的兼容卡片与同发的规范 V2 状态做协调,不动 wire 值,让消息组件以现有警告样式接受恢复后的类型,补齐中英文文案,并对两个层次加测试。diff 的做法与此完全一致,因此审查重点放在覆盖面和不变量上。

对照 main 上的代码核实:

  • 两条发射路径都覆盖了。 实时事件与历史回放都把 goalStatus 和规范 goalState 放进同一个 _meta,而归一化逻辑先消费 goalStatus 再考虑 goalTerminal——所以实时和回放的历史都会走协调分支。未修改的 goalTerminal 回退分支只在没有 goalStatus 时触发,此时也没有规范状态可供协调,不修改它是正确的,不是覆盖缺口。
  • 拷贝语义。 协调函数只在 aborted + usage_limited 这一精确组合下返回新对象,其余情况原样透传,入参卡片不会被改写。
  • VALID_GOAL_KINDS 的新增是必要的。 消息组件经由该白名单解析数据,缺了它恢复后的类型会被丢弃,卡片根本不会渲染。
  • 测试在两个层次上锁住了改动。 去掉本 PR 的改动后,两个测试都会失败(归一化后 kind 仍是 aborted;渲染层白名单拒绝该类型),且中英文都验证了。

非阻塞说明:blocked 仍按设计折叠为 Goal aborted(发现的范围仅限 usage_limited,PR 也明确说明了);描述中提到的修改前后截图仍不在评论区,建议补上。

测试证据

无人值守 CI 运行——未构建或执行任何 PR 代码;以上证据是审查时刻通过 API 获取的该提交自身 CI 结果。macOS/Windows 测试矩阵作业被仓库的 PR 分类配置跳过(并非失败);ubuntu 作业承载测试信号。目前没有红色项——审查时还有三项在运行,表格会由收尾任务在 CI 结束后更新。作者自报的本地数字(5,241 项 web-shell 测试通过)是其声明,不作为证据。沙箱验证一行见英文部分:实时会话端到端渲染仍可通过维护者发起的赞助版 @qwen-code /verify 运行来证实(作者无写权限,无法使用 /tmux)。

Qwen Code · qwen3.8-max

Reviewed at 74a13d80552af9b203738e1784874646b5c3c43c · re-run with @qwen-code /triage

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, minimal fix that lands a verified deferred finding exactly as prescribed; the only nit is that the referenced before/after screenshots never actually made it into the thread.

Stepping back: the PR chose the right side of the fix. Reconciling on the Web Shell consumer keeps the legacy wire projection untouched for older clients, which is the conservative call — my independent proposal arrived at the same shape without seeing the diff. Every hunk earns its place (normalizer hook, render gate, title case, reason line, two i18n keys, two pinning tests); there is no drive-by churn, and blocked staying folded is deliberate scope discipline, not an oversight. The author has two previously merged PRs and this one stands on its own evidence — the verified finding in #10046 — rather than on volume. If I were maintaining this in six months, the narrow reconciliation with its one-line rationale comment would read fine.

The verdict is approve, but CI has not settled yet: the ubuntu unit suite, the no-AK integration run, and the web-shell visuals capture were still in flight at review time (two pending pull_request workflow runs). Approval is deferred until CI lands green on 74a13d80552af9b203738e1784874646b5c3c43c; the finalize job posts the commit-pinned approval once every check completes green, and flags instead if anything lands red or the head moves.

中文说明

回顾整体:PR 选择了正确的修复侧。在 Web Shell 消费端做协调,旧客户端看到的兼容 wire 投影保持不变,这是保守而正确的取舍——我在看 diff 之前独立得出的方案也是同样的形态。每一处改动都有必要(归一化钩子、渲染白名单、标题分支、原因行、两个 i18n 键、两个锁定行为的测试),没有顺手改动;blocked 继续保持折叠是刻意的范围纪律,不是遗漏。作者此前有两个已合并的 PR,本 PR 依靠的是 #10046 中已验证的发现这一独立证据。

结论是批准,但 CI 尚未结束:审查时 ubuntu 单元测试、无 AK 集成测试和 web-shell 视觉采集仍在运行(两个待完成的 pull_request 工作流)。批准将推迟到所审提交全部变绿后执行——收尾任务会在所有检查变绿后提交绑定该提交的批准;如有检查变红或提交变动,则会改为标记提醒。

Qwen Code · qwen3.8-max

Reviewed at 74a13d80552af9b203738e1784874646b5c3c43c · re-run with @qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

🖼️ web-shell visual preview

Rendered against a mock daemon (no real backend): the PR base vs this PR head ee70e01. Only screenshots that changed are shown (flows below, if any, are head-only) — refreshes on every push.

Screenshots · before / after

goal-usage-limited-dark before/after

goal-usage-limited-light before/after

Full-resolution recordings (.webm) are attached to the workflow run.

Qwen Code · web-shell visuals

@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.

Reviewed. Suggestions are inline.

Not explored to full depth (tool budget reached): "agent 1a": none — no check was cut short..

Test Plan (not a blocker): 241 tests pass — this review observed 5067, 481 passed.

中文说明

已审查。 建议见行内评论。

未探索到全部深度(达到工具调用预算):"agent 1a"none — no check was cut short.

Test Plan(非阻断):241 tests pass — this review observed 5067, 481 passed

— qwen3.8-max via Qwen Code /review (v0.22.3)

Comment on lines +4860 to +4862
if (status['kind'] !== 'aborted' || !isRecord(goalState)) return status;
const goal = goalState['goal'];
if (!isRecord(goal) || goal['status'] !== 'usage_limited') return status;

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.

[Suggestion] R1-1: The pass-through branches of this restore guard have no test witness: nothing pins that a legacy kind: 'aborted' card stays aborted when no canonical goalState is co-emitted, or when the canonical status is not 'usage_limited'. Core's legacy projection maps 'blocked' goals onto this same legacy 'aborted' kind (case 'blocked': case 'usage_limited': return 'aborted'; in packages/core/src/goals/goal-legacy-projection.ts), so if a future edit weakens the guard — dropping the goal['status'] !== 'usage_limited' comparison, or keying the restore on goalState presence alone — every blocked goal's transcript card silently re-labels to "Goal usage limited" and the suite stays green: every kind: 'aborted' fixture in packages/web-shell/client today is paired with canonical status: 'usage_limited', and the only restore test asserts the positive direction.

Witness:

guard mutated (goal['status'] !== 'usage_limited' removed):
  DaemonSessionProvider.test.tsx + SystemMessage.test.tsx — Tests 306 passed (306)  ← suite stays green
negative probe added beside the restore test:
  × keeps aborted label when canonical state is blocked
      Expected: data ObjectContaining { "kind": "aborted" }
      Received: data { "kind": "usage_limited", "condition": "finish the evaluation", ... }

Add a negative case beside restores usage-limited semantics from canonical goal state metadata in DaemonSessionProvider.test.tsx: emit _meta.goalStatus with kind: 'aborted' paired (a) with goalState.goal.status: 'blocked' and (b) with no goalState at all, and assert the transcript block keeps data.kind: 'aborted':

expect(blocks).toContainEqual(
  expect.objectContaining({
    kind: 'status',
    source: 'goal',
    data: expect.objectContaining({ kind: 'aborted' }),
  }),
);

The fixture must include a non-empty conditionnormalizeGoalStatus rejects any goal status without one (if (!condition) return null;, DaemonSessionProvider.tsx:4876), or the card silently disappears — and it must distinguish by canonical goalState.goal.status, because blocked goals arrive on the wire as the identical legacy 'aborted' kind, the only differentiator available to the client.

The new test must go red if the guard is removed: delete goal['status'] !== 'usage_limited' from the condition and run it — the blocked case should fail with kind: 'usage_limited'.

中文说明

这个恢复逻辑的直通分支没有测试锁定:当没有同发的规范 goalState,或者规范状态不是 'usage_limited' 时,旧的 kind: 'aborted' 卡片应保持 aborted,但目前没有任何测试钉住这一点。core 的旧版投影会把 'blocked' 目标映射成同一个旧类型 'aborted'packages/core/src/goals/goal-legacy-projection.ts 中的 case 'blocked': case 'usage_limited': return 'aborted';),因此如果未来有人放宽这个守卫——删掉 goal['status'] !== 'usage_limited' 比较,或改成只要存在 goalState 就恢复——所有被阻断目标的卡片都会被悄悄改标为 "Goal usage limited",而整个测试套件依然全绿:目前 packages/web-shell/client 里所有 kind: 'aborted' 的测试数据都与规范状态 'usage_limited' 成对出现,唯一的恢复测试也只断言了正向转换。

证据(变异验证):删掉守卫中的 goal['status'] !== 'usage_limited' 后,DaemonSessionProvider.test.tsx + SystemMessage.test.tsx 仍为 306/306 全部通过;在恢复测试旁加入负向探针测试后,blocked 场景立即失败:期望 kind: "aborted",实际收到 kind: "usage_limited"

建议在 DaemonSessionProvider.test.tsx 的新恢复测试旁补一个负向用例:_meta.goalStatuskind: 'aborted',分别(a)与 goalState.goal.status: 'blocked' 成对、(b)完全不带 goalState,断言历史消息块的 data.kind 保持 'aborted'

约束:测试数据必须包含非空 condition——normalizeGoalStatus 会拒绝缺少 condition 的状态(if (!condition) return null;DaemonSessionProvider.tsx:4876),否则卡片会静默消失;同时只能通过规范的 goalState.goal.status 来区分场景,因为 blocked 目标在传输层就是以相同的旧类型 'aborted' 到达的,这是客户端唯一可用的区分依据。

新测试必须在移除守卫时变红:删除条件中的 goal['status'] !== 'usage_limited' 后运行该测试,blocked 场景应以 kind: 'usage_limited' 失败。

— qwen3.8-max via Qwen Code /review (v0.22.3)

@qqqys

qqqys commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Verification report — head 3e34f6a599

Reviewed the full diff (6 files) against head 3e34f6a599 — no merge-blocking issue found.

Code-review notes:

  • The restore gate is exact and non-mutating: it fires only on the pair goalStatus.kind === 'aborted' + goalState.goal.status === 'usage_limited' and returns the original object otherwise, so genuine aborts and legacy/v1 events render exactly as before and the wire value for older clients is untouched.
  • The pairing exists on both ingestion paths: live daemon events already carry _meta.goalState next to _meta.goalStatus (that is how the status strip gets the canonical state today), and transcript replay emits goalState: payload.snapshot right next to goalStatus (acp-bridge/src/transcript-replay.ts), so the corrected label applies to live sessions and resumed transcripts alike.
  • usage_limited is added consistently across the kind union, the valid-kind set, the warning-style title and the last-reason gate, with matching EN/ZH copy.

Local verification on a head-equivalent tree (base e40040cd26 + this PR's exact diff, applied cleanly):

  • Full npm ci build: passed (including the web-shell bundle).
  • The two changed test files (SystemMessage.test.tsx, DaemonSessionProvider.test.tsx): 306 tests passed, including the new normalization test and the EN/ZH render tests.
  • Daemon smoke from the bundled build in a tmux session: qwen serve --port 47931 booted cleanly, /health returned {"status":"ok"}, the Web Shell page was served, and the new copy (Goal usage limited) is present in the served JS bundle.

The rendered-label leg needs a browser plus a Goal that actually exhausted its token budget (not reproducible in a scratch session), so that leg rests on the Playwright visual scenario this PR adds (goal-usage-limited-{dark,light}), which is green in CI alongside the web-shell E2E smoke and the unit lanes.

— Qwen Code verification run · built from this PR head · 2026-08-31

@wenshao

wenshao commented Sep 1, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /resolve

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

Qwen Code resolved the merge conflicts and pushed the branch update.

Root cause

Main's feat(web-shell): allow copying terminal turn errors (#10001, commit dd5b857) inserted new test blocks at the exact anchors this PR used:

All goal-logic files auto-merged cleanly: main's goal rework (#10171) does not touch the goal-status normalization region, GoalStatusMessage.tsx, or the goal.* i18n keys this PR edits.

Textual only

Both sides added entirely new, independent blocks; no shared logic was modified by both. The resolution keeps both, PR block first:

test(`usage-limited goal status`, async ({ page }, testInfo) => { ... });

test(`terminal turn error`, async ({ browser, page }, testInfo) => { ... });

Verified the result is a strict union: diffing each resolved file against its stage-2 and stage-3 blobs shows zero dropped lines from either side (the only replaced line is the auto-merged import, which gains devices from @playwright/test for #10001's touch-context test). The staged tree vs origin/main is byte-identical to the original PR diff (6 files, +191/−1).

Load-bearing

  • Each conflict hunk shared the closing }); line that followed the conflict markers, so the union appends an explicit closer to the PR's block before main's block begins. If either test is ever moved, each must keep its own }); / }); closer.
  • The devices import and the top-level createTerminalTurnErrorScenario helper (both from main) are required by the terminal turn error test and must stay.
  • Screenshot names stay disjoint: goal-usage-limited-${theme} vs terminal-turn-error-copy{,-narrow,-touch}-${theme}.

Could not verify

No build/tests were run (conflict resolution only; PR CI covers correctness). One note: main added vi.useRealTimers() / vi.restoreAllMocks() to the afterEach in SystemMessage.test.tsx (outside the conflict), which now also wraps this PR's goal-status test — a no-op there (it uses no fake timers or mocks), but future tests in that file inherit the restoration.

中文说明

根因:main 上的 feat(web-shell): allow copying terminal turn errors#10001,提交 dd5b857)在与本 PR 相同的插入锚点新增了测试块:SystemMessage.test.tsxprompt_cancelled describe 之后、screenshots.spec.ts 中每个主题的 session transcript 测试之后。

纯文本冲突:双方都是新增互不相关的测试块,没有修改同一逻辑。解决方式为两者都保留(PR 的块在前)。已逐一核对:解决结果相对双方暂存版本零行丢失;合并后相对 origin/main 的差异与原 PR 差异逐字节一致(6 个文件,+191/−1)。

关键点:两个冲突块共用标记后的闭合 });,因此在 main 的块之前为 PR 的块补了一个显式闭合;devices 导入和 createTerminalTurnErrorScenario 辅助函数来自 main,是 terminal turn error 测试所必需;截图命名互不冲突。

未能验证:本次仅解决冲突,未运行构建或测试(由 PR 自身 CI 覆盖)。另注:main 在 SystemMessage.test.tsxafterEach 中加入的 vi.useRealTimers() / vi.restoreAllMocks()(冲突区域之外)同样作用于本 PR 新增的目标状态测试——对该测试无影响,但该文件后续新增测试会继承此清理行为。

@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.

Reviewed.

1 Suggestion-level finding(s) this review confirmed are already reported on this PR and are not repeated:

  • R1-1 restore guard pass-through branches lack a test witness — already reported (comment 3889623685)

Not explored to full depth (tool budget reached): "agent 3b": none — no check was cut short..

Test Plan (not a blocker): 241 tests pass — this review observed 5324, 503 passed.

中文说明

已审查。

本轮确认的 1 条建议级发现已在 PR 上报告过,不再重复发布(列表见上方英文部分)。

未探索到全部深度(达到工具调用预算):"agent 3b"none — no check was cut short.

Test Plan(非阻断):241 tests pass — this review observed 5324, 503 passed

— qwen3.8-max via Qwen Code /review (v0.22.3)

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review did not complete successfully. The review pipeline failed before a review could be posted. A transient error is retried automatically; if you are seeing this, retry with @qwen-code /review. See workflow logs.

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.

5 participants