fix(cli): switch @ completion category tabs with bare arrow keys - #8576
Conversation
|
✅ Qwen Triage finished — CI landed green on ✅ Qwen Triage 已完成 —— |
|
@qwen-code /tmux |
|
Thanks for the PR! Template looks good ✓ Problem: observed bug with solid evidence. #8069 documents that terminals (iTerm2, Windows Terminal, Terminal.app, GNOME Terminal) intercept Ctrl+←/→ for word-jump, so the documented tab-switching gesture never reaches qwen-code — confirmed by user testing in the issue, and the earlier partial fix #8074 (Ctrl+Tab alternative) itself acknowledged the original binding was unreachable. The PR's claim that Ctrl+Tab is also commonly intercepted is plausible, and bare arrows are the gesture the original #7302 feature description promised users. Direction: aligned — this repairs an unreachable gesture in a shipped feature (the category tabs from #7302), which is exactly what the issue's own triage concluded ("Next: rebind tab switching to a non-conflicting key combo"). Claude Code's CHANGELOG has no direct reference to this interaction, but the area is clearly relevant — it's qwen-code's own completion UI. Size: Approach: scope feels right. Independently I'd have done exactly this: rebind Risk: no elevated risk signals — none of the changed files match the high-risk path patterns. Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ 问题:已观测到的 bug,证据充分。#8069 记录了终端(iTerm2、Windows Terminal、Terminal.app、GNOME Terminal)会拦截 Ctrl+←/→ 用于按词跳转,导致文档中承诺的切换手势根本到不了 qwen-code——issue 中已经过用户实测确认,此前的部分修复 #8074(Ctrl+Tab 备选)也等于承认原绑定不可达。PR 中"Ctrl+Tab 同样常被拦截"的说法可信,而裸方向键正是 #7302 原始功能描述向用户承诺的手势。 方向:对齐——这是修复已上线功能(#7302 的分类标签)中不可达的操作方式,也正是该 issue 自身 triage 的结论("下一步:将标签切换重绑定到不冲突的按键")。Claude Code 的 CHANGELOG 中没有对此交互的直接引用,但该领域显然相关——这是 qwen-code 自己的补全 UI。 规模: 方案:范围合理。我独立推导的方案与此完全一致:把 风险:无升级风险信号——改动文件均未命中高风险路径模式。 进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code reviewRead the diff against an independent proposal I wrote first from the title and motivation — I'd have done it exactly this way. The integration points that matter for this change all check out:
One non-blocking observation: attachment chips are also navigated with bare ←/→, and that branch runs after the completion branch — so while the tab bar is up, arrows switch categories rather than move between chips. It's bounded (Esc restores chip navigation) and consistent with the documented tradeoff, just worth knowing. TestingCI evidence read from the GitHub API for the reviewed commit (per policy, the PR's code is never executed by triage):
Final CI results for
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。 Sandboxed verification would settle the remaining gap: 中文说明代码审查先根据标题和动机独立推导方案,再对照 diff——我的独立方案与本 PR 完全一致。关键集成点均已核实:
一个非阻塞观察:附件 chip 也用裸 ←/→ 导航,且该分支在补全分支之后——因此标签栏显示期间方向键切换分类而非移动 chip。影响有限(Esc 可恢复 chip 导航),与已记录的取舍一致,知晓即可。 测试CI 证据通过 GitHub API 读取(triage 按规约不执行 PR 代码):
沙箱验证可补齐剩余缺口: — Qwen Code · qwen3.8-max Reviewed at |
|
Confidence: 4/5 — clean, focused fix that matches my independent proposal exactly; one point withheld only for the still-running unit suite and a minor edge case named below. Stepping back: this is the second attempt at #8069, and it lands on the right answer. The first fix (#8074) bolted on Ctrl+Tab as an alternative after terminals swallowed Ctrl+←/→; this PR correctly recognizes that anything Ctrl-based in this slot is fighting terminal emulators, and moves to the one gesture nothing intercepts — the bare arrows the original #7302 feature promised in the first place. The problem was observed and confirmed (issue triage, user testing), not theoretical, and the fix is the minimal version of itself: one rebind with modifiers pinned off, the same The tradeoff — arrows switch categories instead of moving the caret while the tab bar is up — is stated plainly in the PR, gated so it can only fire when the tab bar is actually rendered, and always escapable via Esc / Ctrl+B / Ctrl+F. If I'm maintaining this in six months, the comment in Reservations, none blocking: the ubuntu unit job is still in flight (the deferred approval below is pinned to it landing green); the attachment-chip navigation edge from my Stage 2 note; and real-terminal behaviour rests on the author's local macOS run plus mock-stdin unit tests — the Approval deferred until CI lands green on 中文说明信心:4/5 —— 干净、聚焦的修复,与我独立推导的方案完全一致;仅因单测仍在运行及下文提到的一个小边界情况扣一分。 整体来看:这是 #8069 的第二次修复尝试,且落在了正确的答案上。第一次修复(#8074)在终端吞掉 Ctrl+←/→ 后补上了 Ctrl+Tab 备选;本 PR 正确认识到这个键位上任何带 Ctrl 的组合都在与终端模拟器对抗,转而采用不会被任何一方拦截的手势——也正是 #7302 原始功能当初承诺的裸方向键。问题是观测到并确认过的(issue triage、用户实测),不是理论推测;修复本身也是最简形态:一次重绑定并显式关闭所有修饰键、沿用原有的"超过 2 个分类"可见性门控、同步更新提示与文档、测试矩阵精确钉住新契约。 取舍——标签栏显示期间方向键切换分类而非移动光标——在 PR 中坦诚说明,门控保证它只在标签栏真实渲染时生效,且随时可通过 Esc / Ctrl+B / Ctrl+F 脱身。六个月后维护这段代码时, 保留意见(均不阻塞):ubuntu 单测任务仍在运行(下面的延迟批准以其全绿为前提);Stage 2 中提到的附件 chip 导航边界情况;真实终端行为目前只有作者的 macOS 本地运行加 mock stdin 单测支撑——Stage 2 中的 批准延迟至 CI 在 — Qwen Code · qwen3.8-max Reviewed at |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship — CI landed green after the review. ✅
|
tmux real-user testing: timeout - workflow run The tmux test did not complete before the time limit. This is not a pass/fail result for the affected flow; see the workflow run and artifacts for details. Full tmux transcript— Qwen Code · tmux real-user testing |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| // more than two tabs (at least 3 entries including 'all'). While this | ||
| // menu is open the arrows therefore do NOT move the caret — Esc first to | ||
| // dismiss it. Outside this branch ←/→ keep their normal caret behaviour. | ||
| if ((completion.availableCategories?.length ?? 0) > 2) { |
There was a problem hiding this comment.
[Critical] This branch consumes bare ←/→ for category switching but is gated only on showCompletionSuggestions && availableCategories > 2 — it does not exclude command-search mode or attachment mode, states in which the tab bar is not rendered (SuggestionsDisplay receives availableCategories={undefined} during search). The comment's "gated on the menu being visible" claim does not hold there. — Failure scenario: (1) type @ses (>2 categories), press Ctrl+R: the completion reset effect only checks reverseSearchActive, so showSuggestions stays true while no tab bar renders; bare ←/→ fall through the search block and call switchCategory(±1) on a hidden menu — the caret cannot move inside the search query (zero feedback), and after Esc the menu silently reopens on a different tab. (2) With attachments present and the menu open, switch to a tab holding exactly one suggestion (one bare → now suffices) and press Up at row 0: the attachment-mode entry has no !showCompletionSuggestions gate, so ←/→ then hit this branch before the attachment chip-navigation branch and chips can no longer be arrow-moved until Esc.
| if ((completion.availableCategories?.length ?? 0) > 2) { | |
| if (!commandSearchActive && !reverseSearchActive && !isAttachmentMode && (completion.availableCategories?.length ?? 0) > 2) { |
A deeper fix is one shared "tab bar visible" predicate consumed by both this branch and showTabBar in SuggestionsDisplay, so key capture and rendering cannot diverge again.
中文说明
[Critical] 该分支会消费裸 ←/→ 来切换分类,但仅以 showCompletionSuggestions && availableCategories > 2 为门控——没有排除命令搜索模式和附件模式,而这两种状态下标签栏并不渲染(搜索期间 SuggestionsDisplay 收到的是 availableCategories={undefined})。注释中"以菜单可见为前提"的说法在这些状态下不成立。— 故障场景:(1) 输入 @ses(>2 个分类)后按 Ctrl+R:补全重置副作用只检查 reverseSearchActive,因此 showSuggestions 保持为 true 而标签栏并未渲染;裸 ←/→ 穿过搜索分支后落到这里,对一个不可见的菜单调用 switchCategory(±1)——搜索框中的光标无法移动(毫无反馈),按 Esc 后重新打开的菜单会悄悄落在另一个分类上。(2) 存在附件且菜单打开时,切换到只有一个建议的分类(现在只需按一下裸 →),在第 0 行按 Up:进入附件模式的分支没有 !showCompletionSuggestions 门控,于是 ←/→ 会先命中本分支而不是附件选择分支,在按 Esc 之前无法再用方向键选择附件。
更彻底的修复是让本分支与 SuggestionsDisplay 的 showTabBar 共用同一个"标签栏可见"谓词,使按键捕获与渲染不再脱节。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| [Command.COMPLETION_TAB_RIGHT]: [ | ||
| { key: 'right', shift: false, ctrl: true, command: false }, | ||
| { key: 'tab', shift: false, ctrl: true, command: false }, | ||
| { key: 'right', shift: false, ctrl: false, command: false, meta: false }, |
There was a problem hiding this comment.
[Critical] With Vim keybindings enabled the new bare-arrow bindings are unreachable: vimHandleInput runs before the completion branch, and Vim INSERT mode consumes bare left/right as caret movement (NORMAL mode consumes everything). This PR simultaneously deletes the Ctrl+Tab / Ctrl+Shift+Tab alternatives that DID pass through Vim insert mode, and Ctrl+Tab now matches the unpinned ACCEPT_SUGGESTION tab entry — so Vim users lose every keyboard path to switch categories while SuggestionsDisplay still renders (←/→ to switch). — Failure scenario: a Vim user in INSERT mode types @ with >2 categories; ←/→ never reach COMPLETION_TAB_*, the old Ctrl+Tab path is gone, and Ctrl+Tab now inserts the highlighted suggestion instead of switching — no keyboard path remains while the hint claims one works.
| { key: 'right', shift: false, ctrl: false, command: false, meta: false }, | |
| { key: 'right', shift: false, ctrl: false, command: false, meta: false }, | |
| // Ctrl+Tab passes through Vim insert mode; bare arrows do not. | |
| { key: 'tab', shift: false, ctrl: true, command: false }, |
(Add the COMPLETION_TAB_LEFT counterpart { key: 'tab', shift: true, ctrl: true, command: false } to restore the pre-PR behaviour; alternatives are suppressing the tab-switch hint when Vim mode is active, or documenting the limitation.)
中文说明
[Critical] 启用 Vim 键位时,新的裸方向键绑定无法触达:vimHandleInput 在补全分支之前运行,且 Vim INSERT 模式会把裸 left/right 作为光标移动消费掉(NORMAL 模式消费所有按键)。本 PR 同时删除了能够穿透 Vim insert 模式的 Ctrl+Tab / Ctrl+Shift+Tab 备选绑定,而 Ctrl+Tab 现在会命中未固定修饰键的 ACCEPT_SUGGESTION tab 条目——于是 Vim 用户失去了所有切换分类的键盘路径,而 SuggestionsDisplay 仍然显示 (←/→ to switch)。— 故障场景:Vim 用户在 INSERT 模式输入 @(>2 个分类);←/→ 到不了 COMPLETION_TAB_*,旧的 Ctrl+Tab 路径已被删除,Ctrl+Tab 现在会插入高亮建议而不是切换分类——提示文字声称可用的路径已不复存在。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| // for Mission Control, so the documented gesture was unreachable for most | ||
| // users (#8069). |
There was a problem hiding this comment.
[Suggestion] The PR says "Closes #8069", but #8069 was already closed on 2026-07-31 by merged PR #8074 (the Ctrl+Tab alternative this PR removes). The live gap this PR actually fixes — Ctrl+Tab being intercepted by Warp — is tracked in still-OPEN issue #8330, and the complementary mouse-click fix is open PR #8395; neither is referenced. — Concrete cost: after merge, #8330 stays open with no cross-reference, "Closes #8069" is a no-op link, and two open PRs touching the same picker files (#8395, #8576) are not connected anywhere, risking conflicting review/merge decisions. Suggested fix: update the PR description, e.g. "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074".
中文说明
[Suggestion] 本 PR 声明 "Closes #8069",但 #8069 已于 2026-07-31 被合并的 PR #8074(即本 PR 所移除的 Ctrl+Tab 备选方案)关闭。本 PR 实际修复的活跃缺口——Ctrl+Tab 被 Warp 拦截——记录在仍开放的 issue #8330 中;互补的鼠标点击修复是开放 PR #8395;二者均未被引用。— 具体代价:合并后 #8330 仍然开放且没有交叉引用;"Closes #8069" 成为无效链接;两个触及同一补全选择器文件的开放 PR(#8395、#8576)互不关联,可能导致冲突的评审/合并决定。建议更新 PR 描述,例如 "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074"。
— qwen3.8-max via Qwen Code /review (v0.21.5)
There was a problem hiding this comment.
Accurate — #8069 was already closed by merged PR #8074, and the live gap this PR actually closes is #8330 (Ctrl+Tab intercepted by Warp), with the complementary mouse-click fix in #8395. Updating the PR description is a GitHub write that this automated round cannot perform (it has no GitHub credentials; the workflow owns all GitHub writes), so it must be applied by a maintainer rather than in code. Suggested description wording: "Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074." No code change is required for this finding.
中文说明
确实如此—— #8069 已被合并的 PR #8074 关闭,本 PR 实际修复的活跃缺口是 #8330(Ctrl+Tab 被 Warp 拦截),互补的鼠标点击修复是 #8395。更新 PR 描述属于 GitHub 写操作,本轮自动化无法执行(没有 GitHub 凭据,所有 GitHub 写操作由工作流负责),因此需要由维护者应用而非通过代码完成。建议的描述措辞为:"Addresses the remaining gap from #8330; complements the mouse-click fix in #8395; #8069 was already closed by #8074"。该发现无需代码改动。
| // gestures, leaving the documented shortcut unreachable (#8069). The caret | ||
| // tradeoff while the menu is open is gated in InputPrompt, not here. | ||
| describe('Completion tab-switching bare arrow bindings (#8069)', () => { | ||
| it('should match bare ← / → as COMPLETION_TAB_LEFT / RIGHT', () => { |
There was a problem hiding this comment.
[Suggestion] The standalone #8069 describe block re-asserts the bare-arrow positives and ctrl/meta negatives that the parameterized comparison block in this same file already makes for COMPLETION_TAB_LEFT/COMPLETION_TAB_RIGHT (asserted against both the keyMatchers and the originalMatchers oracle, lines ~243-265). This PR itself had to perform the double edit. Only the Ctrl+Tab / Ctrl+Shift+Tab / bare-Tab negatives in the fourth it are unique. — Concrete cost: every future binding change must be edited in two places in the same file, and this PR is proof the drift is easy. Suggested fix: delete the three duplicated its ('should match bare ← / →…', 'should not consume Ctrl+arrows…', 'should not consume Alt/Option+arrows…') and keep only 'should no longer bind Ctrl+Tab / Ctrl+Shift+Tab', or fold the tab negatives into the parameterized negative lists and drop the whole describe.
中文说明
[Suggestion] 独立的 #8069 describe 块重复断言了同文件参数化比较块(约 243-265 行,同时对 keyMatchers 与 originalMatchers 预言机断言)已覆盖的裸方向键正向用例和 ctrl/meta 反向用例。本 PR 自己就不得不进行双份编辑。只有第四个 it 中的 Ctrl+Tab / Ctrl+Shift+Tab / 裸 Tab 反向用例是独有的。— 具体代价:以后每次绑定变更都要在同一文件里编辑两处,本 PR 正是这种漂移易发生的证明。建议删除三个重复的 it('should match bare ← / →…'、'should not consume Ctrl+arrows…'、'should not consume Alt/Option+arrows…'),只保留 'should no longer bind Ctrl+Tab / Ctrl+Shift+Tab';或把 tab 反向用例并入参数化反向列表后整体删除该 describe。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| [Command.COMPLETION_TAB_LEFT]: [ | ||
| { key: 'left', shift: false, ctrl: true, command: false }, | ||
| { key: 'tab', shift: true, ctrl: true, command: false }, | ||
| { key: 'left', shift: false, ctrl: false, command: false, meta: false }, | ||
| ], |
There was a problem hiding this comment.
[Suggestion] Removing the Ctrl+Tab / Ctrl+Shift+Tab bindings also deleted the only thing shadowing the modifier-agnostic ACCEPT_SUGGESTION { key: 'tab' } binding. On terminals that deliver Ctrl+Tab distinctly (kitty CSI-u ESC[9;5u, xterm modifyOtherKeys ESC[27;5;9~ — both decoded by KeypressContext into {name:'tab', ctrl:true}), pressing Ctrl+Tab with the tabbed menu open now inserts the highlighted suggestion instead of switching the tab — an unexpected buffer mutation on exactly the UI where the previous release's hint advertised that gesture as navigation. Pre-PR this fallthrough only occurred with ≤2 categories; the diff extends it to the tabbed UI. — Suggested fix: if "no longer bind" is meant literally, pin ctrl: false on ACCEPT_SUGGESTION's tab entry ({ key: 'tab', ctrl: false }), keeping bare Tab and Shift+Tab accepting; alternatively state in the PR/tests that Ctrl+Tab ≡ Tab is intentional.
中文说明
[Suggestion] 删除 Ctrl+Tab / Ctrl+Shift+Tab 绑定的同时,也移除了唯一遮蔽不区分修饰键的 ACCEPT_SUGGESTION { key: 'tab' } 绑定的东西。在能区分递送 Ctrl+Tab 的终端上(kitty CSI-u ESC[9;5u、xterm modifyOtherKeys ESC[27;5;9~——KeypressContext 均解码为 {name:'tab', ctrl:true}),在显示分类标签的菜单打开时按 Ctrl+Tab,现在会插入高亮建议而不是切换分类——恰恰在上一版本提示文字把该手势标注为导航的界面上,产生了意外的输入框变更。PR 之前该穿透只发生在 ≤2 个分类时;本 diff 把它扩展到了带标签栏的界面。— 建议:若"不再绑定"是字面意图,给 ACCEPT_SUGGESTION 的 tab 条目固定 ctrl: false({ key: 'tab', ctrl: false }),保留裸 Tab 与 Shift+Tab 的接受行为;或在 PR/测试中明确 Ctrl+Tab ≡ Tab 是有意为之。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| <Text color={theme.text.secondary}> | ||
| {t('(Ctrl+Tab / Ctrl+Shift+Tab or Ctrl+←/→ to switch)')} | ||
| </Text> | ||
| <Text color={theme.text.secondary}>{t('(←/→ to switch)')}</Text> |
There was a problem hiding this comment.
[Suggestion] [test] The test-efficacy probe (harness validated) reverted this hunk alone and every affected test stayed green — no test asserts the rendered (←/→ to switch) hint; the PR's test changes exercise only key escape sequences. — Failure scenario: if a future change reverts or mangles the hint — e.g. restores the old (Ctrl+Tab / Ctrl+Shift+Tab or Ctrl+←/→ to switch) — CI stays green and users again see shortcut hints pointing at bindings that terminals / macOS Mission Control intercept: exactly the unreachable-binding confusion (#8069) this PR removes. Suggested fix: add a render assertion (SuggestionsDisplay or InputPrompt test) that with availableCategories > 2 the rendered output contains (←/→ to switch).
中文说明
[Suggestion] [test] 测试有效性探针(harness 已验证)单独回退该 hunk 后,所有受影响测试仍为绿色——没有任何测试断言渲染出的 (←/→ to switch) 提示;本 PR 的测试改动只覆盖了按键转义序列。— 故障场景:若未来某个改动回退或篡改该提示——例如恢复旧的 (Ctrl+Tab / Ctrl+Shift+Tab or Ctrl+←/→ to switch)——CI 依然全绿,而用户会再次看到指向被终端 / macOS 调度中心拦截的绑定的快捷键提示:正是本 PR 要消除的"绑定不可达"困惑(#8069)。建议:增加渲染断言(SuggestionsDisplay 或 InputPrompt 测试),验证 availableCategories > 2 时渲染输出包含 (←/→ to switch)。
— qwen3.8-max via Qwen Code /review (v0.21.5)
|
@qwen-code /takeover |
|
🤝 Takeover engaged: the autofix loop now manages this PR — it will address new review feedback and resolve base conflicts until the label is removed or the round cap is reached. This is a fork PR, so the first round comes from the next scheduled scan (usually within minutes). Remove the 中文说明🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
…enLM#8576) The category tab-switching branch consumed bare ←/→ whenever more than two categories existed, even in states where the tab bar is not rendered. During command/reverse search the menu shows without categories (availableCategories is passed as undefined to SuggestionsDisplay), and in attachment mode the arrows navigate the attachment chips — in both states the arrows switched a hidden menu instead of keeping their normal behaviour. Gate the branch on the tab bar actually being visible: not in command search, not in reverse search, and not in attachment mode. This PR had also removed the Ctrl+Tab / Ctrl+Shift+Tab alternatives. With Vim keybindings enabled, vimHandleInput consumes bare arrows before completion handling runs, so Vim users lost every keyboard path to switch categories while the hint still advertised ←/→. Restore the Ctrl+Tab / Ctrl+Shift+Tab alternatives (tab keys pass through Vim insert mode) and re-document them. Tests cover the two gated states, assert the Ctrl+Tab fallback end-to-end via its kitty CSI-u encoding, pin the rendered "(←/→ to switch)" hint, fold the former standalone QwenLM#8069 matcher block into the parameterized comparison, and pin Ctrl+Tab falling through to ACCEPT_SUGGESTION when the tab bar is hidden. Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Autofix review round — PR #8576Addressed both Critical findings and three Suggestions from the automated Feedback points
Also noted: the issue-level tmux real-user testing comment reports a timeout — VerificationCommands actually run (working tree = PR head + this round's commit):
中文说明Autofix 评审轮次 — PR #8576本轮处理了自动评审的两个 Critical 发现和三个 Suggestion;另一个 Suggestion(PR 描述引用问题)需要 GitHub 写操作,已在其评论线程中回复。提交为 反馈要点
另外:issue 级别的 tmux 真实用户测试评论报告的是超时——明确说明这不算通过/失败结果,故未对其采取行动。 验证实际执行的命令(工作树 = PR head + 本轮提交):
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: No changes neededThe only feedback item this round is a failed check; there are no review comments, inline comments, or issue-level comments to address. Failed check:
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| unmount(); | ||
| }); | ||
|
|
||
| it('should switch category on Ctrl+Tab / Ctrl+Shift+Tab when availableCategories > 2', async () => { |
There was a problem hiding this comment.
[Suggestion] The PR description contradicts the code under review. The Evidence section still claims the tests assert "that Ctrl+Tab is no longer bound" ("Ctrl+Tab 已解除绑定"), and Risk & Scope says "Breaking changes / migration notes: Ctrl+arrow and Ctrl+Tab no longer switch categories" — but this commit keeps the Ctrl+Tab / Ctrl+Shift+Tab alternative bindings (keyBindings.ts COMPLETION_TAB_*) and adds the test anchored here asserting they DO switch categories. The docs (keyboard-shortcuts.md) already advertise them as alternatives.
Failure scenario: the round-1 autofix restored the Ctrl+Tab alternatives without updating the PR body, so a maintainer assessing breaking-change risk, a reviewer checking the claimed coverage, or anyone writing release notes from the description concludes Ctrl+Tab does nothing after merge — the opposite of what ships (it switches categories while the tab bar is rendered, and accepts the suggestion when it is hidden).
Suggested fix: update the PR description — Evidence: "Ctrl+Tab / Ctrl+Shift+Tab still switch as alternatives"; Risk & Scope: "Ctrl+arrow no longer switches categories; Ctrl+Tab / Ctrl+Shift+Tab remain as alternatives" (maintainer/author description edit, same channel as the issue-reference wording fix).
中文说明
PR 描述与被审查的代码相矛盾。Evidence 一节仍声称测试断言"Ctrl+Tab 已解除绑定",Risk & Scope 仍写着"破坏性改动或迁移说明:Ctrl+方向键与 Ctrl+Tab 不再切换分类"——但本提交保留了 Ctrl+Tab / Ctrl+Shift+Tab 备选绑定(keyBindings.ts 的 COMPLETION_TAB_*),并新增了此处锚定的测试来断言它们确实可以切换分类。文档(keyboard-shortcuts.md)也已将其宣传为备选方式。
故障场景:第 1 轮 autofix 恢复了 Ctrl+Tab 备选绑定却没有更新 PR 描述,于是评估破坏性改动风险的维护者、核对所声称测试覆盖的评审者、或任何依据描述撰写发布说明的人,都会得出"合并后 Ctrl+Tab 不再有任何作用"的结论——与实际交付的行为恰恰相反(标签栏显示时它切换分类,标签栏隐藏时它接受建议)。
建议修复:更新 PR 描述——Evidence 改为"Ctrl+Tab / Ctrl+Shift+Tab 仍作为备选方式切换分类";Risk & Scope 改为"Ctrl+方向键不再切换分类;Ctrl+Tab / Ctrl+Shift+Tab 保留为备选"(与修正 issue 引用的措辞一样,属于维护者/作者的描述编辑)。
— qwen3.8-max via Qwen Code /review (v0.21.5)
There was a problem hiding this comment.
Not resolved in code this round: this finding targets the PR description on GitHub, which the autofix fix job cannot edit (it has no GitHub write access; the workflow owns network writes). The code already matches the finding's account — COMPLETION_TAB_* keeps the Ctrl+Tab / Ctrl+Shift+Tab alternatives and the tests anchored here assert they DO switch categories — so only the description needs to catch up.
Proposed description edits for the maintainer/author channel:
- Evidence: replace the "Ctrl+Tab is no longer bound" claim with: "Ctrl+Tab / Ctrl+Shift+Tab still switch categories as alternatives; they accept the suggestion only while the tab bar is hidden."
- Risk & Scope (breaking changes / migration notes): replace "Ctrl+arrow and Ctrl+Tab no longer switch categories" with: "Ctrl+arrow no longer switches categories; Ctrl+Tab / Ctrl+Shift+Tab remain as alternatives."
中文说明
本轮未在代码中解决:该发现针对的是 GitHub 上的 PR 描述,而 autofix 修复任务无法编辑它(修复任务没有 GitHub 写权限,网络写操作由工作流负责)。代码本身已与该发现所述一致——COMPLETION_TAB_* 保留了 Ctrl+Tab / Ctrl+Shift+Tab 备选绑定,且此处锚定的测试断言它们确实可以切换分类——因此只需要让 PR 描述跟上代码。
给维护者/作者渠道的描述修改建议:
- Evidence:将"Ctrl+Tab 已解除绑定"的说法替换为:"Ctrl+Tab / Ctrl+Shift+Tab 仍作为备选方式切换分类;仅在标签栏隐藏时接受建议。"
- Risk & Scope(破坏性改动或迁移说明):将"Ctrl+方向键与 Ctrl+Tab 不再切换分类"替换为:"Ctrl+方向键不再切换分类;Ctrl+Tab / Ctrl+Shift+Tab 保留为备选。"
| !commandSearchActive && | ||
| !reverseSearchActive && | ||
| !isAttachmentMode && | ||
| (completion.availableCategories?.length ?? 0) > 2 |
There was a problem hiding this comment.
[Suggestion] "Is the category tab bar visible/interactive" is now hand-maintained in three places that already diverge: this key gate (lacks suggestionsFromExport), the render-side prop suppression ~lines 2280-2291 (availableCategories passed as undefined when suggestionsFromExport || commandSearchActive || reverseSearchActive, no isAttachmentMode check), and showTabBar in SuggestionsDisplay.tsx. The autofix round declined the shared-predicate fix claiming "the explicit gate already mirrors the render condition" — but that is false for this commit's final state (the render side also omits isAttachmentMode, which this gate has).
Failure scenario: the suggestionsFromExport gap is unreachable today only because export suggestions never carry >2 categories — an implicit cross-module invariant stated nowhere near this code. Any future render-side suppression (a settings flag, compact mode, slash suggestions gaining categories) that misses this gate silently re-creates the round-1 Critical: bare ←/→ consumed while the tab bar is hidden, freezing caret movement with no visible UI to explain it.
Suggested fix: derive one boolean next to the render-prop computation, e.g. const categoryTabsVisible = !suggestionsFromExport && !commandSearchActive && !reverseSearchActive && (completion.availableCategories?.length ?? 0) > 2;, pass availableCategories={categoryTabsVisible ? completion.availableCategories : undefined}, and gate this branch on categoryTabsVisible && !isAttachmentMode (the attachment exclusion stays explicit — it is arrow-ownership, not visibility).
中文说明
"分类标签栏是否可见/可交互"目前由三处手工维护且已经出现分歧:本按键门控(缺少 suggestionsFromExport)、渲染端约 2280-2291 行的属性抑制(在 suggestionsFromExport || commandSearchActive || reverseSearchActive 时把 availableCategories 传为 undefined,但不检查 isAttachmentMode)、以及 SuggestionsDisplay.tsx 中的 showTabBar。autofix 轮次曾以"显式门控已与渲染条件一致"为由拒绝了共享谓词的深层修复——但就本提交的最终状态而言该说法不成立(渲染端同样缺少本门控所拥有的 isAttachmentMode)。
故障场景:suggestionsFromExport 缺口目前不可达,仅仅因为导出建议永远不会携带超过 2 个分类——这是一个未在此代码附近任何地方说明的跨模块隐式不变量。未来任何渲染端的抑制条件(设置开关、紧凑模式、斜杠建议获得分类)若遗漏本门控,都会悄悄复现第 1 轮的 Critical:标签栏隐藏时裸 ←/→ 被消费,光标移动被冻结且没有任何可见界面解释原因。
建议修复:在渲染属性计算旁边派生一个布尔量,例如 const categoryTabsVisible = !suggestionsFromExport && !commandSearchActive && !reverseSearchActive && (completion.availableCategories?.length ?? 0) > 2;,传 availableCategories={categoryTabsVisible ? completion.availableCategories : undefined},并把本分支的门控改为 categoryTabsVisible && !isAttachmentMode(附件模式排除保持显式——那是方向键归属问题,不是可见性问题)。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| // the tab bar is actually rendered: more than two tabs (at least 3 | ||
| // entries including 'all') and no history search active (search shows | ||
| // the menu without categories). In attachment mode the arrows belong | ||
| // to chip navigation below. While this menu is open the arrows |
There was a problem hiding this comment.
[Suggestion] In attachment mode the !isAttachmentMode gate blocks this whole branch — including the Ctrl+Tab / Ctrl+Shift+Tab fallback it was added alongside. With the menu still open (single-suggestion tab, Up at row 0 — the exact state the new attachment-mode test constructs), the tab bar keeps advertising "(←/→ to switch)" while ←/→ navigate chips, and Ctrl+Tab falls through to ACCEPT_SUGGESTION's modifier-agnostic { key: 'tab' } and silently accepts the single suggestion instead of switching categories. Pre-PR, Ctrl+Tab switched categories in this state.
Failure scenario (probe-confirmed at this commit): image attachment + @ menu with 3 categories + one suggestion on the active tab; Up at row 0 enters attachment mode with the menu open; Kitty Ctrl+Tab (ESC[9;5u) then produced handleAutocomplete(0) (buffer mutated) with zero switchCategory calls. No keyboard path switches categories until Esc. Reachability is narrow (needs the single-suggestion tab), hence Suggestion.
Suggested fix: gate only the bare-arrow matchers on !isAttachmentMode (e.g. apply the attachment exclusion only when key.name === 'left' || key.name === 'right'), keeping Ctrl+Tab / Ctrl+Shift+Tab active whenever the tab bar is rendered.
中文说明
附件模式下 !isAttachmentMode 门控阻断了整个分支——连同与之一起加入的 Ctrl+Tab / Ctrl+Shift+Tab 备选绑定。菜单仍然打开时(只有一个建议的分类、在第 0 行按 Up——正是新增附件模式测试所构造的状态),标签栏继续显示"(←/→ to switch)",而 ←/→ 实际在导航附件芯片,Ctrl+Tab 则穿透到不区分修饰键的 ACCEPT_SUGGESTION { key: 'tab' } 绑定,悄悄接受那唯一的建议而不是切换分类。PR 之前,Ctrl+Tab 在此状态下可以切换分类。
故障场景(已在本提交上以探针确认):图片附件 + 3 个分类的 @ 菜单 + 激活分类只有一个建议;在第 0 行按 Up 进入附件模式且菜单保持打开;此时按 Kitty Ctrl+Tab(ESC[9;5u)触发了 handleAutocomplete(0)(输入缓冲区被改写),switchCategory 调用为零。在按 Esc 之前没有任何键盘路径可以切换分类。可达性较窄(需要单建议分类),故定为 Suggestion。
建议修复:仅对裸方向键匹配器施加 !isAttachmentMode 门控(例如仅在 key.name === 'left' || key.name === 'right' 时应用附件模式排除),让 Ctrl+Tab / Ctrl+Shift+Tab 在标签栏渲染时始终可用。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| // a keyboard path to switch categories for Vim users. | ||
| [Command.COMPLETION_TAB_LEFT]: [ | ||
| { key: 'left', shift: false, ctrl: true, command: false }, | ||
| { key: 'left', shift: false, ctrl: false, command: false, meta: false }, |
There was a problem hiding this comment.
[Suggestion] command: false and meta: false compile to the identical check: in matchKeyBinding (keyMatchers.ts) both fields test key.meta — the Key type has no command field, and KeypressContext documents that a binding's command maps to key.meta. So one of the two fields here is a no-op duplicate that reads as two distinct physical modifiers being pinned. Siblings pin once each (SUBMIT/QUEUE_MESSAGE use command: false, VOICE_PUSH_TO_TALK uses meta: false).
Concrete cost: the comment above says "Modifiers are pinned false", which reads as Cmd and Alt independently pinned when only the single key.meta state exists (pinned twice); a future change that ever distinguishes command from meta in the compiler silently changes this binding's meaning.
Suggested fix (both COMPLETION_TAB_LEFT and COMPLETION_TAB_RIGHT bare-arrow entries):
{ key: 'left', shift: false, ctrl: false, meta: false },
{ key: 'right', shift: false, ctrl: false, meta: false },中文说明
command: false 与 meta: false 编译出完全相同的检查:matchKeyBinding(keyMatchers.ts)中两个字段都测试 key.meta——Key 类型没有 command 字段,KeypressContext 中也注明绑定的 command 映射到 key.meta。因此两者之一是无效重复,读起来却像分别固定了两个不同的物理修饰键。同级的绑定各只固定一次(SUBMIT/QUEUE_MESSAGE 用 command: false,VOICE_PUSH_TO_TALK 用 meta: false)。
具体代价:上方注释写着"Modifiers are pinned false",读起来像 Cmd 与 Alt 被各自独立固定,而实际只存在单一的 key.meta 状态(被固定了两次);未来若有改动在编译器中区分 command 与 meta,此绑定的含义会被悄悄改变。
建议修复(COMPLETION_TAB_LEFT 与 COMPLETION_TAB_RIGHT 的裸方向键条目):
{ key: 'left', shift: false, ctrl: false, meta: false },
{ key: 'right', shift: false, ctrl: false, meta: false },— qwen3.8-max via Qwen Code /review (v0.21.5)
| // Kitty CSI-u encodings of Ctrl+Tab / Ctrl+Shift+Tab — the fallback that | ||
| // stays reachable with Vim keybindings, where bare arrows are consumed | ||
| // before completion handling runs (#8069). |
There was a problem hiding this comment.
[Suggestion] This test's comment claims to pin "the fallback that stays reachable with Vim keybindings", but the chain is never exercised: the test renders with vimHandleInput undefined (the gate at InputPrompt.tsx:1083 is a no-op in every category-switching test), and no test anywhere covers vim.ts's insert-mode tab pass-through (vim.test.ts has no such case). The split at vim.ts:912 (tab keys return false / pass through; bare arrows are consumed) plus the gate ordering are the exact mechanism the round-1 Vim Critical's fix rests on — pinned by comments alone.
Failure scenario: if a future change makes Vim insert mode consume tab (e.g. indent-on-tab) or reorders the vim gate, Vim users silently lose the only remaining keyboard path to switch categories — the exact regression this commit claims to fix — and the whole suite stays green.
Suggested fix: add a case with props.vimHandleInput = vi.fn((key) => key.name === 'left' || key.name === 'right') (arrows consumed, tab passes) and assert bare arrows do NOT switch while ESC[9;5u / ESC[9;6u DO switch; optionally pin the pass-through in vim.test.ts too.
中文说明
该测试的注释声称固定了"在 Vim 键位下仍然可达的备选路径",但这条链路从未被真正执行:测试渲染时 vimHandleInput 为 undefined(InputPrompt.tsx:1083 的门控在所有分类切换测试中都是空操作),且任何测试都没有覆盖 vim.ts 插入模式的 tab 穿透(vim.test.ts 中没有此类用例)。vim.ts:912 处的分叉(tab 键返回 false / 穿透,裸方向键被消费)加上门控顺序,正是第 1 轮 Vim Critical 修复所依赖的确切机制——目前只靠注释固定。
故障场景:若未来改动让 Vim 插入模式消费 tab(例如 tab 缩进)或调整 vim 门控顺序,Vim 用户会悄悄失去切换分类的唯一键盘路径——正是本提交声称修复的回归——而整个测试套件仍然全绿。
建议修复:新增一个用例,设 props.vimHandleInput = vi.fn((key) => key.name === 'left' || key.name === 'right')(方向键被消费、tab 穿透),断言裸方向键不切换分类而 ESC[9;5u / ESC[9;6u 可以切换;也可在 vim.test.ts 中一并固定该穿透行为。
— qwen3.8-max via Qwen Code /review (v0.21.5)
| stdin.write('\x1b[D'); // plain left arrow | ||
| await wait(); | ||
|
|
||
| expect(switchCategory).toHaveBeenCalledWith(-1); |
There was a problem hiding this comment.
[Suggestion] These positive tests assert the category switched but not that the arrow was consumed — half of the PR's stated behaviour ("the arrows no longer move the caret") is unverified.
Failure scenario (mutation proven at this commit): deleting both return true; after completion.switchCategory(±1) (InputPrompt.tsx:1428/1433) keeps every PR test green while the arrow also reaches BaseTextInput — the caret drifts one character on every category switch. The probe assertion below fails under the mutation and passes on the original code, so it distinguishes correct from buggy.
Suggested fix: after each arrow write in the > 2 test, also assert the key did not reach the buffer:
expect(mockBuffer.move).not.toHaveBeenCalled();
expect(mockBuffer.handleInput).not.toHaveBeenCalled();中文说明
这些正向测试断言了分类发生切换,却没有断言方向键被消费——PR 所声明行为的一半("方向键不再移动光标")未被验证。
故障场景(已在本提交上做变异验证):删除 completion.switchCategory(±1) 之后的两处 return true;(InputPrompt.tsx:1428/1433),PR 的所有测试仍然全绿,而方向键同时会到达 BaseTextInput——每次切换分类光标都会漂移一个字符。下面的探针断言在该变异下失败、在原始代码上通过,因此能区分正确与错误实现。
建议修复:在 > 2 测试中每次写入方向键后,追加断言按键没有到达输入缓冲区:
expect(mockBuffer.move).not.toHaveBeenCalled();
expect(mockBuffer.handleInput).not.toHaveBeenCalled();— qwen3.8-max via Qwen Code /review (v0.21.5)
| // entries including 'all') and no history search active (search shows | ||
| // the menu without categories). In attachment mode the arrows belong |
There was a problem hiding this comment.
[Suggestion] While command search (Ctrl+R, non-shell) is active, this gate rejects Ctrl+Tab / Ctrl+Shift+Tab from COMPLETION_TAB_* — and the keystroke then falls through to ACCEPT_SUGGESTION, whose { key: 'tab' } binding pins no modifiers, silently accepting the still-open @ completion into the search-query buffer. Completion stays "open" under command search because the reset effect only reacts to reverseSearchActive, and with a no-match query the search block renders nothing and consumes no Tab.
Failure scenario (probe-confirmed at this commit): buffer @ with ≥3 categories → Ctrl+R → no-match query → Kitty Ctrl+Tab (ESC[9;5u) fired handleAutocomplete(0) and mutated the search query with no completion UI visible; flipping the gate to pre-PR makes the same keystroke switch categories without touching the buffer. Pre-PR the old gate had no search condition, so COMPLETION_TAB_RIGHT consumed the keystroke. (Shell reverse search is unaffected — completion resets there.)
Suggested fix: skip this completion branch while commandSearchActive (probe-verified with the suite green). Do not globally exclude Ctrl+Tab from ACCEPT_SUGGESTION — this PR documents that it accepts when the tab bar is hidden.
中文说明
命令搜索(Ctrl+R,非 shell)激活时,本门控拒绝了 COMPLETION_TAB_* 的 Ctrl+Tab / Ctrl+Shift+Tab——按键随后落入 ACCEPT_SUGGESTION(其 { key: 'tab' } 绑定不固定任何修饰键),把仍然"打开"的 @ 补全悄悄接受进搜索查询缓冲区。补全在命令搜索期间保持"打开",因为重置副作用只响应 reverseSearchActive;而当查询无匹配时,搜索分支不渲染任何内容也不消费 Tab。
故障场景(已在本提交上以探针确认):缓冲区输入 @(≥3 个分类)→ 按 Ctrl+R → 输入无匹配的查询 → 按 Kitty Ctrl+Tab(ESC[9;5u)触发了 handleAutocomplete(0),在没有任何补全界面可见的情况下改写了搜索查询;把门控翻回 PR 之前的形态,同一按键则切换分类且不触碰缓冲区。PR 之前旧门控没有搜索条件,COMPLETION_TAB_RIGHT 会消费该按键。(shell 反向搜索不受影响——补全会在那里被重置。)
建议修复:commandSearchActive 期间跳过本补全分支(已用探针验证,套件全绿)。不要全局地把 Ctrl+Tab 从 ACCEPT_SUGGESTION 中排除——本 PR 明确记录了标签栏隐藏时 Ctrl+Tab 接受建议的行为。
— qwen3.8-max via Qwen Code /review (v0.21.5)
|
🔀 Base updated: red check(s) [Test (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run. 中文说明🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. Suggestions are inline.
中文说明
已审查——无阻断问题。 建议见行内评论。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| if ( | ||
| !commandSearchActive && | ||
| !reverseSearchActive && |
There was a problem hiding this comment.
[Suggestion] R2-2: "Is the category tab bar visible/interactive" is still hand-maintained in three places that already diverge: this key gate (lacks suggestionsFromExport), the render-side prop suppression (~lines 2280-2292, which nulls availableCategories for suggestionsFromExport || commandSearchActive || reverseSearchActive), and showTabBar inside SuggestionsDisplay.tsx (length > 2). The suggestionsFromExport omission is unreachable today (export suggestions are single-category), but nothing encodes that invariant. — Failure scenario: a future change to tab-bar visibility (a new hide condition or threshold in SuggestionsDisplay, or a new category producer during export cycling) must be manually mirrored into this keypress gate; forgetting it silently consumes bare ←/→ to switch an invisible tab bar — the exact regression class this PR's own new command-search/attachment-mode tests exist to catch — turning caret movement into a no-op.
Fix (spans multiple locations, so no one-click suggestion): compute the predicate once in InputPrompt and share it:
const showCategoryTabs =
!suggestionsFromExport &&
!commandSearchActive &&
!reverseSearchActive &&
(completion.availableCategories?.length ?? 0) > 2;
// gate the key branch on: showCategoryTabs && !isAttachmentMode
// and pass showCategoryTabs / the same inputs to SuggestionsDisplay
// instead of letting it re-derive showTabBar independently.中文说明
[Suggestion] R2-2:“分类标签栏是否可见/可交互”目前仍由三处手工维护且已经出现分歧:本按键门控(缺少 suggestionsFromExport)、渲染侧的 prop 抑制(约 2280-2292 行,在 suggestionsFromExport || commandSearchActive || reverseSearchActive 时把 availableCategories 置为 undefined)、以及 SuggestionsDisplay.tsx 内的 showTabBar(length > 2)。缺少 suggestionsFromExport 目前不可达(导出建议是单分类),但这一不变式没有任何代码保证。— 故障场景:未来任何对标签栏可见性的修改(SuggestionsDisplay 新增隐藏条件或阈值变化、导出循环中新增分类来源)都必须手工同步到这个按键门控;一旦遗漏,裸 ←/→ 就会悄悄切换一个不可见的标签栏——这正是本 PR 新增的命令搜索/附件模式测试所要防范的回归类型——方向键移动光标变成无响应。
修复建议(跨多处,无法一键应用):在 InputPrompt 中只计算一次谓词并共享,见上方代码块;按键分支以 showCategoryTabs && !isAttachmentMode 为门控,并让 SuggestionsDisplay 使用同一来源而不是自行推导 showTabBar。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| if ( | ||
| !commandSearchActive && | ||
| !reverseSearchActive && | ||
| !isAttachmentMode && |
There was a problem hiding this comment.
[Suggestion] R2-3: The !isAttachmentMode gate term also disables the Ctrl+Tab / Ctrl+Shift+Tab alternatives in attachment mode. Probe-confirmed at this commit: with the menu open in attachment mode (image attachment, ≥3 categories, Up at row 0 — the exact state this PR's new test constructs), Ctrl+Tab falls through to ACCEPT_SUGGESTION (the { key: 'tab' } entry pins no modifiers, ~line 1457) and silently autocompletes the highlighted suggestion into the buffer, while the tab bar and its (←/→ to switch) hint remain rendered. Pre-PR (gate only > 2) Ctrl+Tab switched categories here — a probe flip reproduced the old behaviour. — Failure scenario: paste a clipboard image, type @ with ≥3 categories, press Up at row 0 to enter attachment mode, then press Ctrl+Tab — the binding the keyBindings comment and docs advertise as the category-switch alternative: the highlighted suggestion is autocompleted into the buffer instead of the category switching, while the hint still shows (←/→ to switch).
Fix idea: exclude only the bare arrows in attachment mode — e.g. check !isAttachmentMode inside the two arrow-matcher arms and keep the tab entries live, or accept the behaviour and say so in the keyBindings comment.
中文说明
[Suggestion] R2-3:!isAttachmentMode 门控项同时禁用了附件模式下的 Ctrl+Tab / Ctrl+Shift+Tab 备选绑定。已在当前提交上用探针证实:附件模式下菜单打开时(粘贴图片附件、≥3 个分类、在第 0 行按 Up——正是本 PR 新测试构造的状态),Ctrl+Tab 会落到 ACCEPT_SUGGESTION({ key: 'tab' } 条目未固定任何修饰键,约 1457 行),把高亮建议静默自动补全进输入缓冲区,而此时标签栏及其 (←/→ to switch) 提示仍在渲染。PR 之前(门控仅为 > 2)Ctrl+Tab 在此状态下可以切换分类——探针翻转后复现了旧行为。— 故障场景:粘贴图片附件、输入 @(≥3 个分类)、在第 0 行按 Up 进入附件模式,然后按 Ctrl+Tab——即 keyBindings 注释和文档所宣传的分类切换备选键:结果高亮建议被自动补全进缓冲区而不是切换分类,提示文字却仍显示 (←/→ to switch)。
修复思路:仅在附件模式下排除裸方向键——例如把 !isAttachmentMode 放进两个方向键匹配分支内部、保留 tab 条目生效;或接受该行为并在 keyBindings 注释中说明。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| stdin.write('\x1b[C'); // plain right arrow | ||
| await wait(); |
There was a problem hiding this comment.
[Suggestion] R2-6: The positive category-switching tests assert switchCategory was called but never assert the arrow was consumed — half of the PR's stated behaviour ("the arrows no longer move the caret") is unpinned. Probe-confirmed at this commit: removing the return true after switchCategory(1) makes the arrow both switch the category AND fall through to caret movement, and this test still passes. — Failure scenario: a future dispatch mutation that switches and then falls through leaves every test in this PR green while the headline tradeoff ("arrows no longer move the caret while the tab bar is visible") silently regresses.
Fix idea: after the arrow, also assert the buffer state — e.g. cursor position / buffer.text unchanged, or spy that buffer.handleInput was not called with the arrow key.
中文说明
[Suggestion] R2-6:正向的分类切换测试只断言了 switchCategory 被调用,从未断言方向键被消费——PR 声明行为的一半(“方向键不再移动光标”)没有被测试固定。已在当前提交上用探针证实:删除 switchCategory(1) 后的 return true,方向键会既切换分类又继续移动光标,而本测试仍然通过。— 故障场景:未来某次派发修改让方向键先切换再穿透,本 PR 的所有测试依旧全绿,而核心取舍(“标签栏可见时方向键不再移动光标”)已悄悄回归。
修复建议:在方向键之后追加缓冲区状态断言——例如光标位置/buffer.text 不变,或用 spy 断言 buffer.handleInput 没有收到该方向键。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| // to chip navigation below. While this menu is open the arrows | ||
| // therefore do NOT move the caret — Esc first to dismiss it. | ||
| if ( | ||
| !commandSearchActive && |
There was a problem hiding this comment.
[Suggestion] R2-7: Probe-confirmed at this commit: while command search (Ctrl+R, non-shell) is active, completion stays open (the reset effect only reacts to reverseSearchActive), this gate rejects Ctrl+Tab / Ctrl+Shift+Tab from COMPLETION_TAB_*, and the keystroke then falls through to ACCEPT_SUGGESTION ({ key: 'tab' } pins no modifiers) — handleAutocomplete fires on the invisible @ completion, mutating the search query. Pre-PR the same keystroke switched categories on the hidden tab bar (a probe on the base gate reproduced it), so this diff changes the outcome rather than merely removing a dead path. — Failure scenario: type @ses (menu open, >2 categories), press Ctrl+R to search command history, press Ctrl+Tab expecting nothing (tab bar hidden) — the highlighted @ suggestion is inserted into the search query, mutating it unexpectedly.
Fix idea: inside the showCompletionSuggestions branch, swallow COMPLETION_TAB_* matches while commandSearchActive || reverseSearchActive (return true without switching), or pin the chosen fallthrough behaviour with an explicit test.
中文说明
[Suggestion] R2-7:已在当前提交上用探针证实:命令搜索(Ctrl+R,非 shell)激活期间,补全保持打开(重置副作用只响应 reverseSearchActive),本门控拒绝 Ctrl+Tab / Ctrl+Shift+Tab 进入 COMPLETION_TAB_*,按键随后落到 ACCEPT_SUGGESTION({ key: 'tab' } 未固定修饰键)——handleAutocomplete 作用在不可见的 @ 补全上,把搜索词改掉。PR 之前同一按键会在隐藏的标签栏上切换分类(对基线门控的探针复现了该行为),因此本 diff 改变了结果,而不仅仅是移除了一条死路径。— 故障场景:输入 @ses(菜单打开、>2 个分类),按 Ctrl+R 进入命令搜索,按 Ctrl+Tab(标签栏已隐藏,预期无反应)——高亮的 @ 建议被插入搜索词,查询被意外修改。
修复建议:在 showCompletionSuggestions 分支内,当 commandSearchActive || reverseSearchActive 时吞掉 COMPLETION_TAB_* 匹配(直接 return true 不切换);或用显式测试固定所选的穿透行为。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| > alternatives when an editor mode (for example Vim keybindings) consumes the | ||
| > bare arrows. |
There was a problem hiding this comment.
[Suggestion] R3-2: This note is false for Vim NORMAL mode. Verified at the reviewed commit: (1) Esc from Vim insert mode is consumed by vimHandleInput (vim.ts ~904-908 returns true) before the menu-dismissing Esc branch runs, so the menu stays open while Vim enters normal mode; (2) in normal mode bare arrows are consumed as vim movement (vim.ts ~1826-1861) AND Ctrl+Tab falls into the default catch-all (~1863: CLEAR_PENDING_STATES; return true) — so while the menu is open there is NO keyboard path to switch categories; only a second Esc dismisses it. — Failure scenario: with Vim keybindings on and the @ menu open (insert mode), the user presses Esc; Vim consumes it to enter normal mode without dismissing the menu — afterwards arrows move the vim cursor and Ctrl+Tab is silently swallowed, so no keyboard path switches categories while both this note and the in-app hint advertise reachable gestures.
Fix idea: qualify the note to Vim insert mode (and mention Esc dismissing the menu), or make Vim normal mode's default branch pass tab keys through the way insert mode does.
中文说明
[Suggestion] R3-2:该注释对 Vim NORMAL(普通)模式不成立。已在被审提交上核实:(1) Vim INSERT 模式下按 Esc 会被 vimHandleInput 消费(vim.ts 约 904-908 行返回 true),先于关闭菜单的 Esc 分支执行,因此 Vim 进入普通模式时菜单仍然打开;(2) 普通模式下裸方向键被作为 vim 光标移动消费(vim.ts 约 1826-1861 行),且 Ctrl+Tab 落入默认兜底分支(约 1863 行:CLEAR_PENDING_STATES;return true)——于是菜单打开期间没有任何键盘路径可以切换分类;只有再按一次 Esc 才能关闭菜单。— 故障场景:启用 Vim 键位且 @ 菜单打开(INSERT 模式)时按 Esc,Vim 消费该键进入普通模式但不关闭菜单——之后方向键移动 vim 光标、Ctrl+Tab 被静默吞掉,注释和界面提示所宣传的手势全部不可达。
修复建议:把注释限定为 Vim INSERT 模式(并提及 Esc 可关闭菜单),或让 Vim 普通模式的默认分支像 INSERT 模式一样放行 tab 键。
— qwen3.8-max via Qwen Code /review (v0.21.6)
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Autofix round-2 review response (PR #8576)Addressed 6 of the 7 round-2 findings from the automated reviewer (all Findings and dispositions
Mutation verificationEach behavioral fix was proven load-bearing by temporarily reintroducing the defect and confirming the new/extended tests fail:
Changed files
Verification
中文说明Autofix 第 2 轮审查响应(PR #8576)在一次提交中处理了自动评审者第 2 轮 7 条发现中的 6 条(均为 发现与处置
变异验证通过临时重新引入每个缺陷、确认新增/扩展的测试会失败,证明各行为修复确实是承重的:
变更文件
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Autofix round-3 review response (PR #8576)The round-3 review was submitted against the pre-fix merge commit Feedback points
VerificationCommands actually run this round (Node v22, on the working tree that became commit
中文说明Autofix 第 3 轮评审响应(PR #8576)第 3 轮评审提交时针对的是修复前的合并提交( 反馈要点
验证本轮实际执行的命令(Node v22,工作树即提交
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally. Not reviewed: verification and reverse audit — each ran and opened its brief, but neither was launched with the prompt the CLI built — the launches were written by hand, so the posted findings cannot be counted as verified, and what the agents were actually asked is not what this skill certifies.
中文说明
已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。 未审查:验证与反向审计——两者都运行并打开了各自的 brief,但都不是用 CLI 构建的 prompt 启动的——启动 prompt 是手写的,发布的发现不能算作已验证,agent 实际被要求做的也不是本 skill 所认证的内容。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| if (normalizedKey.name === 'tab' && normalizedKey.ctrl) { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
[Suggestion] The new NORMAL-mode Ctrl+Tab pass-through returns before the switch's default: catch-all, which pre-PR dispatched CLEAR_PENDING_STATES (zeroes count, pendingOperator, pendingCharRead) and consumed the key — so a pending Vim operator/count now silently survives a category switch. The adjacent ? pass-through in this same function gates on s.pendingOperator === null && s.count === 0, i.e. the house pattern passes keys through only from a clean state. — Failure scenario: with the @ menu open in NORMAL mode (Esc from INSERT is consumed by vim, so the menu stays open), press d then Ctrl+Tab: pre-PR the pending state was cleared; now d stays armed and the next motion (w) deletes a word the user's Ctrl+Tab gesture indicated they had abandoned. A partially typed count (3, Ctrl+Tab, motion) leaks the same way. Probe-confirmed at this commit: d → Ctrl+Tab → h executes the pending delete, and dispatching CLEAR_PENDING_STATES before return false flips the probe.
| if (normalizedKey.name === 'tab' && normalizedKey.ctrl) { | |
| return false; | |
| } | |
| if (normalizedKey.name === 'tab' && normalizedKey.ctrl) { | |
| dispatch({ type: 'CLEAR_PENDING_STATES' }); | |
| return false; | |
| } |
中文说明
[Suggestion] 新增的 NORMAL 模式 Ctrl+Tab 放行分支在 switch 的 default: 兜底之前直接返回,而 PR 之前按键会落入该兜底并触发 CLEAR_PENDING_STATES(清零 count、pendingOperator、pendingCharRead)——于是待定的 Vim 操作符/计数会在切换分类后悄悄保留。同一函数中相邻的 ? 放行分支显式以 s.pendingOperator === null && s.count === 0 为门控,即此处的惯例是仅在干净状态下放行按键。— 故障场景:NORMAL 模式下 @ 菜单打开时(INSERT 下按 Esc 被 vim 消费、菜单仍打开),先按 d 再按 Ctrl+Tab:PR 之前待定状态会被清除;现在 d 仍处于待发状态,下一个移动键(w)会删除用户已用 Ctrl+Tab 手势放弃的那个词。半截计数(3、Ctrl+Tab、移动键)同样泄漏。已在当前提交上用探针确认:d → Ctrl+Tab → h 会执行待定删除;在 return false 前派发 CLEAR_PENDING_STATES 后探针结果翻转。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| const categoryTabsVisible = | ||
| !suggestionsFromExport && | ||
| !commandSearchActive && | ||
| !reverseSearchActive && | ||
| (completion.availableCategories?.length ?? 0) > 2; |
There was a problem hiding this comment.
[Suggestion] The > 2 tab-bar threshold is now duplicated across two files that must agree by convention: categoryTabsVisible re-implements the exact render-side rule const showTabBar = (availableCategories?.length ?? 0) > 2; in SuggestionsDisplay.tsx (~line 121) — the component whose rendering the added comment documents this boolean as tracking. This PR promoted the previously-inline check into a named single sync point while leaving the render-side original in place, and raised the cost of a desync: it now eats the bare arrows (every ←/→ during @ completion) rather than the rarely-pressed Ctrl+arrows. Distinct from the already-fixed R2-2 gate/prop divergence — this is the threshold constant's second home. — Failure scenario: a future change to the render threshold in SuggestionsDisplay (show tabs with 2 entries, or require 4) without a matching edit here leaves categoryTabsVisible true while the bar is hidden; handleInput then consumes every bare ←/→ with return true — caret movement silently freezes with no visible tab bar to explain it, the exact failure the added comment warns about. — Suggested fix: export the rule from SuggestionsDisplay.tsx (e.g. export function hasCategoryTabs(categories) { return (categories?.length ?? 0) > 2; }), use it for showTabBar there, and compose categoryTabsVisible from it here.
中文说明
[Suggestion] > 2 的标签栏阈值现在重复存在于两个必须靠约定保持一致的文件中:categoryTabsVisible 重新实现了 SuggestionsDisplay.tsx(约 121 行)里渲染侧的规则 const showTabBar = (availableCategories?.length ?? 0) > 2;——而新增注释明确声称该布尔量就是用来跟踪这个组件的渲染结果的。本 PR 把原先内联的检查提升成了命名的单一同步点,却保留了渲染侧的原实现,且提高了失同步的代价:现在失同步会吞掉裸方向键(@ 补全期间的每次 ←/→),而不是过去很少按下的 Ctrl+方向键。这与已修复的 R2-2(门控/prop 分歧)不同——这里指的是阈值常量的第二处宿主。— 故障场景:未来修改 SuggestionsDisplay 的渲染阈值(2 项也显示标签,或要求 4 项)而忘了同步这里时,categoryTabsVisible 为 true 而标签栏实际隐藏;handleInput 会以 return true 吞掉所有裸 ←/→——光标移动在无可见标签栏可解释的情况下静默失效,正是新增注释所警告的失败。— 修复建议:从 SuggestionsDisplay.tsx 导出该规则(如 export function hasCategoryTabs(categories) { return (categories?.length ?? 0) > 2; }),在那里用于 showTabBar,并在此处用它组合出 categoryTabsVisible。
— qwen3.8-max via Qwen Code /review (v0.21.6)
| // more than two tabs (at least 3 entries including 'all'). Plain ←/→ are | ||
| // never consumed here, so they always move the caret in the editable buffer. | ||
| if ((completion.availableCategories?.length ?? 0) > 2) { | ||
| if (showCompletionSuggestions && !commandSearchActive) { |
There was a problem hiding this comment.
[Suggestion] Command-search suppression is bolted onto consumers even though the producer (useCommandCompletion) already owns a generalized suppression mechanism and is simply never told about command search: its reset effect keys off reverseSearchActive || !active (useCommandCompletion.tsx ~276-284), and the call site (~line 371) passes the shell reverse-search state but not commandSearchActive, so showSuggestions stays true for the whole command-search session — a fact this PR's own test comment records. Six compensation sites verified: getActiveCompletion(), the activeCategory render gate, the mode render gate, shouldUseExportSuggestions, plus this PR's handleInput gate and categoryTabsVisible. InputPrompt is the hook's only production caller, so closing this at the producer is contained; the current code is correct — this is root-cause ownership. — Failure scenario: the next consumer of completion.showSuggestions / completion.suggestions must rediscover the guard or it acts on the stale still-open completion during search — the exact class of bug this hunk fixes (Tab/Ctrl+Tab silently accepting a suggestion into the search query). — Suggested fix: pass reverseSearchActive || commandSearchActive at the useCommandCompletion(...) call so completion resets on entering command search exactly as it already does for shell reverse search; the consumer-side !commandSearchActive gates then become redundant. Tradeoff: category/scroll state would be recomputed rather than preserved when search exits — the same behavior shell reverse search already has.
中文说明
[Suggestion] 命令搜索期间的补全抑制被拼接在各个消费方,而生产方(useCommandCompletion)本就有一个通用的抑制机制,只是从未被告知命令搜索的存在:其重置副作用以 reverseSearchActive || !active 为条件(useCommandCompletion.tsx 约 276-284 行),调用处(约 371 行)传入了 shell 反向搜索状态却没有传入 commandSearchActive,因此整个命令搜索期间 showSuggestions 一直保持 true——本 PR 自己的测试注释也记录了这一点。已核实六处补偿点:getActiveCompletion()、activeCategory 渲染门控、mode 渲染门控、shouldUseExportSuggestions,外加本 PR 新增的 handleInput 门控与 categoryTabsVisible。InputPrompt 是该 hook 唯一的生产调用方,在生产方收口是可控的;当前代码是正确的——这是根因归属问题。— 故障场景:下一个读取 completion.showSuggestions / completion.suggestions 的消费方必须重新发现该守卫,否则会在搜索期间对仍然“打开”的陈旧补全做出反应——正是本 hunk 所修复的 bug 类别(Tab/Ctrl+Tab 悄悄把建议接受进搜索词)。— 修复建议:在 useCommandCompletion(...) 调用处传入 reverseSearchActive || commandSearchActive,使进入命令搜索时补全像 shell 反向搜索一样被重置;消费侧的 !commandSearchActive 门控随之变为冗余。权衡:退出搜索时分类/滚动状态会被重算而非保留——与 shell 反向搜索的既有行为一致。
— qwen3.8-max via Qwen Code /review (v0.21.6)
There was a problem hiding this comment.
Declined for this PR — recording the reason here so it isn't silently dropped. The finding itself notes the current code is correct; closing this at the producer is a root-cause refactor of the pre-existing command-search suppression rather than a defect fix in this keybinding change. Two concrete problems: (1) passing reverseSearchActive || commandSearchActive into useCommandCompletion changes pre-existing command-search behavior — completion state (category, scroll, active index, dismissed flag) is currently preserved across a command-search session and restored on exit, while the producer reset would zero it on entry and recompute on exit (the tradeoff the finding itself acknowledges); that behavior call shouldn't ride along in this PR. (2) It would not consolidate all six compensation sites anyway: shouldUseExportSuggestions also gates the separate useExportCompletion hook, and getActiveCompletion() selects between completion and commandSearchCompletion for rendering, so those consumer gates must stay regardless; only the two small gates this PR added (both commented and test-covered) could be removed. Happy to revisit in a follow-up focused on command-search suppression ownership if maintainers want that behavior change.
中文说明
本 PR 拒绝此项——在此记录理由,避免被静默丢弃。该发现本身承认当前代码是正确的;在生产方收口属于对既有命令搜索抑制机制的根因重构,而非本键位改动中的缺陷修复。具体有两点:(1) 向 useCommandCompletion 传入 reverseSearchActive || commandSearchActive 会改变命令搜索的既有行为——补全状态(分类、滚动、选中项、dismissed 标志)目前在命令搜索期间被保留、退出后恢复,而生产方重置会在进入时清零、退出时重算(即该发现自己承认的权衡);这个行为决策不应顺带塞进本 PR。(2) 即便如此也无法把六处补偿点全部收口:shouldUseExportSuggestions 同时还门控着独立的 useExportCompletion hook,getActiveCompletion() 也要在 completion 与 commandSearchCompletion 之间做渲染选择,这些消费方门控无论如何都必须保留;能移除的只有本 PR 新增的两处小门控(均有注释且被测试覆盖)。如果维护者希望做出该行为变更,欢迎在专注于命令搜索抑制归属的后续 PR 中重新讨论。
| > alternatives when an editor mode (for example Vim keybindings) consumes the | ||
| > bare arrows. |
There was a problem hiding this comment.
[Suggestion] This note promises Ctrl+Tab / Ctrl+Shift+Tab as the Vim-user fallback unqualified, but in terminals without the Kitty keyboard protocol Ctrl+Tab arrives as an indistinguishable bare Tab: the app negotiates the protocol itself (detectAndEnableKittyProtocol, with a kittyProtocolEnabled=false fallback), and the bare Tab fails the ctrl: true COMPLETION_TAB_* matchers while matching the modifier-agnostic ACCEPT_SUGGESTION { key: 'tab' } binding. Ctrl+Shift+Tab fares no better (arrives as ESC[Z → {tab, shift, no ctrl}). — Failure scenario: Vim keybindings on, tab bar showing, terminal without Kitty support: the user presses Ctrl+Tab expecting a category switch; the highlighted suggestion is inserted into the prompt instead — and since Vim modes also consume the bare arrows, there is no working keyboard path while this note asserts one. — Suggested fix: qualify the note (and the keyBindings.ts comment) that the alternatives require a terminal that reports modifiers on Tab (Kitty keyboard protocol), otherwise Ctrl+Tab behaves as plain Tab.
| > alternatives when an editor mode (for example Vim keybindings) consumes the | |
| > bare arrows. | |
| > alternatives when an editor mode (for example Vim keybindings) consumes the | |
| > bare arrows. (On terminals without the Kitty keyboard protocol, `Ctrl+Tab` | |
| > arrives as a plain `Tab` and accepts the highlighted suggestion instead.) |
中文说明
[Suggestion] 该注释无条件地承诺 Ctrl+Tab / Ctrl+Shift+Tab 是 Vim 用户的备选路径,但在不支持 Kitty 键盘协议的终端里,Ctrl+Tab 到达时与裸 Tab 无法区分:应用自身会协商该协议(detectAndEnableKittyProtocol,回退为 kittyProtocolEnabled=false),而裸 Tab 不满足 COMPLETION_TAB_* 的 ctrl: true 匹配条件,却会命中不区分修饰键的 ACCEPT_SUGGESTION 的 { key: 'tab' } 绑定。Ctrl+Shift+Tab 同样不行(以 ESC[Z 到达 → {tab, shift, 无 ctrl})。— 故障场景:启用 Vim 键位、标签栏可见、终端不支持 Kitty:用户按 Ctrl+Tab 期望切换分类,结果高亮建议被插入输入框——而且 Vim 各模式同时消费裸方向键,该注释声称可用的键盘路径实际完全不存在。— 修复建议:在注释(以及 keyBindings.ts 的注释)中加上限定——备选键需要能报告 Tab 修饰键的终端(Kitty 键盘协议),否则 Ctrl+Tab 等同于普通 Tab。
— qwen3.8-max via Qwen Code /review (v0.21.6)
Clear the pending Vim operator/count when Ctrl+Tab passes through NORMAL mode (the pre-existing default catch-all cleared them), share the tab-bar visibility threshold between SuggestionsDisplay and InputPrompt via hasCategoryTabs, and document that the Ctrl+Tab alternatives need a terminal with Kitty keyboard protocol support.
|
🤖 Addressed the latest review feedback (round 4/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 4/100 轮)。改动内容与我反驳保留之处如下: Autofix review round — PR #8576Commit: Feedback dispositions
VerificationCommands actually run this round (all from a clean
中文说明Autofix 审查轮次 — PR #8576提交: 反馈处理结论
验证本轮实际执行的命令(runner 为干净的
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 Re-review when you have a moment. After round 100 this bot stops and leaves the PR for a human. · 有空请复审;第 100 轮后本 bot 停止并将 PR 交给人工。 🧠 Handled by Qwen Code · model/模型 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max via Qwen Code /review (v0.21.6)
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round — no action required (PR #8576)No actionable feedback arrived this round:
No code changes were made and no commit was created. The PR head remains at Note: the reviewer disclosed that the "build-and-test — Integration Tests (CLI, No Sandbox)" suite was skipped in CI and not run locally by the reviewer. That is a disclosure of the reviewer's own coverage, not a change request, and the workflow's independent CI remains the final verification gate. 中文说明Autofix 审查轮次 — 无需处理(PR #8576)本轮没有收到可操作的反馈:
未做任何代码改动,也未创建提交。PR head 仍为 备注:审查器披露 "build-and-test — Integration Tests (CLI, No Sandbox)" 套件在 CI 中被跳过、审查器本地也未运行。这是审查器自身覆盖范围的披露,不是变更请求,工作流的独立 CI 仍是最终验证关卡。 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
🤖 Addressed the latest review feedback (round 5/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 5/100 轮)。改动内容与我反驳保留之处如下: Autofix review round summary — PR #8576Feedback triageNo actionable feedback this round: Base-conflict resolution (
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed. Suggestions are inline. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
已审查。 建议见行内评论。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max via Qwen Code /review (v0.21.7)
| // With only 2 entries (all + one real category) the tab bar is hidden, | ||
| // so Ctrl+arrows must not trigger category switching. | ||
| // so the arrows must not trigger category switching. | ||
| expect(switchCategory).not.toHaveBeenCalled(); |
There was a problem hiding this comment.
[Suggestion] The hidden-tab-bar tests assert only that switching does not happen (switchCategory not called); the other half of the arrow contract — that the arrows still reach the input buffer when the tab bar is hidden — is never asserted. Probe-confirmed at this commit: inserting if (!categoryTabsVisible && (key.name === 'left' || key.name === 'right')) return true; into the completion branch survives the full suite (207/207 pass), silently freezing caret movement in the common ≤2-category case. Note the fall-through goes via BaseTextInput.tsx:305 → buffer.handleInput(key), and mockBuffer.move receives zero calls in this state, so the pinning assertion must target mockBuffer.handleInput. — Failure scenario: a future regression that eats bare arrows while the tab bar is hidden ships green — half of the PR's stated behaviour ("arrows keep their usual caret behaviour everywhere else") is unpinned.
| // With only 2 entries (all + one real category) the tab bar is hidden, | |
| // so Ctrl+arrows must not trigger category switching. | |
| // so the arrows must not trigger category switching. | |
| expect(switchCategory).not.toHaveBeenCalled(); | |
| // With only 2 entries (all + one real category) the tab bar is hidden, | |
| // so the arrows must not trigger category switching. | |
| expect(switchCategory).not.toHaveBeenCalled(); | |
| // The arrows must still reach the buffer so the caret stays movable. | |
| expect(mockBuffer.handleInput).toHaveBeenCalledWith( | |
| expect.objectContaining({ name: 'right' }), | |
| ); | |
| expect(mockBuffer.handleInput).toHaveBeenCalledWith( | |
| expect.objectContaining({ name: 'left' }), | |
| ); |
中文说明
[Suggestion] 标签栏隐藏的测试只断言了切换不会发生(switchCategory 未被调用);方向键契约的另一半——标签栏隐藏时方向键仍能到达输入缓冲区——从未被断言。已在本提交通过变异探针确认:在补全分支中插入 if (!categoryTabsVisible && (key.name === 'left' || key.name === 'right')) return true; 后整个测试套件仍然全部通过(207/207),会在常见的 ≤2 分类场景中悄悄冻结光标移动。注意穿透路径经由 BaseTextInput.tsx:305 → buffer.handleInput(key),此状态下 mockBuffer.move 的调用次数为零,因此固定该行为的断言必须以 mockBuffer.handleInput 为目标。— 故障场景:未来某个在标签栏隐藏时吞掉裸方向键的回归将一路绿灯地合入——PR 所声明行为的一半("其他场景下方向键保持正常的光标行为")没有任何测试固定。
— qwen3.8-max via Qwen Code /review (v0.21.7)
|
🤖 Addressed the latest review feedback (round 6/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 6/100 轮)。改动内容与我反驳保留之处如下: Autofix review response — PR #8576Feedback points and dispositions1. Inline [Suggestion] by @qwen-code-ci-bot —
|
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Reviewed — no blockers. 1 Suggestion-level finding(s) could not be anchored to a changed line and were dropped; nothing further to act on here.
中文说明
已审查——无阻断问题。 1 条建议级发现无法锚定到改动行,已丢弃;此处无需进一步处理。
— qwen3.8-max via Qwen Code /review (v0.21.7)
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix review round — no action needed (PR #8576)The latest feedback on this PR contains no actionable items:
No code changes were made this round. The branch remains at its current head commit; no merge of 中文说明Autofix 审查轮次 — 无需处理(PR #8576)本 PR 的最新反馈中没有任何可操作的事项:
本轮未做任何代码改动。分支保持在当前头部提交;由于未报告冲突,也未执行 Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
45463b0 to
e39832c
Compare
|
Please do not rebase or force-push to an active PR as it invalidates existing review comments. Note for future reference, the bots always squash all changes into a single commit automatically as part of the integration. 中文请勿对活跃的 PR 执行 rebase 或 force-push,因为这会使已有的评审评论失效。另外,供日后参考:作为集成流程的一部分,机器人始终会自动将所有改动压缩(squash)为单个提交。 |
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.
中文说明
未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally。
— qwen3.8-max via Qwen Code /review (v0.21.8)
| // The visible category tabs own the bare arrows, including in Vim mode. | ||
| // All other states fall through to their existing input owner. | ||
| if (showCompletionSuggestions && categoryTabsVisible) { |
There was a problem hiding this comment.
[Critical] Consuming bare Left/Right here — before vimHandleInput — bypasses vim's arrow-motion branch (vim.ts:1825-1860), which pre-PR applied/cancelled the pending operator and cleared the repeat count. With Vim mode on and the category tabs visible, a pending d/c/y/</> operator or a typed digit count survives a category switch and fires on the user's next motion key. This is distinct from the R3-2 thread (the missing Esc dismiss path): pre-PR the bare arrow always reached vim's arrow branch, which cleaned this state up; post-PR vim never sees the key at all. — Failure scenario: Vim mode enabled; type @ with ≥3 categories (tab bar visible); press Esc — vim consumes it (menu persists, NORMAL mode); press d (pendingOperator set); press Right Arrow (category switches, vim never sees the key); press l → the stale delete executes, removing text the user did not ask to delete. Probe-verified at this commit: with the PR's routing, d → intercepted Right → l performs the delete; with pre-PR routing the same sequence cancels the operator. Counts and c leak the same way.
Suggested fix direction: when this block consumes an arrow while vim is active, clear vim's pending operator/count first (expose a reset from useVim), or let vim observe the key for state purposes before consuming it.
中文说明
[Critical] 在此处——vimHandleInput 之前——消费裸 Left/Right 会绕过 vim 的方向键分支(vim.ts:1825-1860);该分支在本 PR 之前会应用/取消待命操作符并清空重复计数。启用 Vim 模式且分类标签可见时,待命的 d/c/y/</> 操作符或已输入的次数会越过分类切换存活下来,并在用户的下一个移动键上触发。这与 R3-2 线程(缺少 Esc 关闭菜单路径)是不同的问题:本 PR 之前裸方向键总会到达 vim 的方向键分支并清理这些状态;本 PR 之后 vim 完全看不到该按键。— 故障场景:启用 Vim 模式;输入 @ 且 ≥3 个分类(标签栏可见);按 Esc——被 vim 消费(菜单仍在,进入 NORMAL 模式);按 d(设置 pendingOperator);按 Right Arrow(切换分类,vim 看不到该键);按 l → 悬置的删除被执行,删掉用户并未要求删除的文本。已在本提交通过探针验证:按本 PR 的路由,d → 被拦截的 Right → l 会执行删除;按 PR 之前的路由,同样的按键序列会取消操作符。计数与 c 以同样方式泄漏。
修复方向建议:当此分支在 vim 激活时消费方向键时,先清除 vim 的待命操作符/计数(从 useVim 暴露一个重置方法),或在消费按键前先让 vim 观察该键以维护状态。
— qwen3.8-max via Qwen Code /review (v0.21.8)
| availableCategories={ | ||
| suggestionsFromExport || | ||
| commandSearchActive || | ||
| reverseSearchActive | ||
| ? undefined | ||
| : completion.availableCategories | ||
| categoryTabsVisible ? completion.availableCategories : undefined | ||
| } |
There was a problem hiding this comment.
[Suggestion] availableCategories now derives from categoryTabsVisible (which includes !isAttachmentMode), but the sibling activeCategory prop on this same call is still gated only by suggestionsFromExport. useCompletion filters the suggestion list by activeCategory upstream (useCompletion.ts:91-99) and nothing resets the category when entering attachment mode — so hiding the tab bar leaves an invisible filter narrowing the list, and the arrows are now owned by attachment navigation, so the user cannot switch back to 'All' until leaving attachment mode. Pre-PR the tab bar stayed rendered in attachment mode, so the filter state was always visible. — Failure scenario: type @foo with ≥3 categories, switch to a category tab holding ≤1 match, paste an image and press Up at row 0 → attachment mode hides the tab bar while the list stays filtered to the now-invisible category; the user cannot see or change the filter until leaving attachment mode or dismissing the menu. Probe-confirmed at this commit (with flip check).
Suggested fix: reset the completion category to 'all' when entering attachment mode (e.g. near setIsAttachmentMode(true)); gating the activeCategory prop alone is not enough because the filtering happens upstream in useCompletion.
中文说明
[Suggestion] availableCategories 现在由 categoryTabsVisible(包含 !isAttachmentMode)派生,但同一处的 activeCategory 属性仍只以 suggestionsFromExport 为门控。useCompletion 在上游按 activeCategory 过滤建议列表(useCompletion.ts:91-99),且进入附件模式时没有任何逻辑重置分类——于是标签栏被隐藏后,一个不可见的过滤器仍在收窄列表,而方向键已归附件导航所有,用户在离开附件模式之前无法切回 'All'。本 PR 之前标签栏在附件模式下仍然渲染,过滤状态始终可见。— 故障场景:输入 @foo(≥3 个分类),切换到只有 ≤1 条匹配的分类标签,粘贴图片后在第 0 行按 Up → 附件模式隐藏标签栏,而列表仍被过滤到这个已不可见的分类;用户在离开附件模式或关闭菜单之前无法看到或更改该过滤。已在本提交通过探针验证(含翻转检查)。
修复建议:进入附件模式时将补全分类重置为 'all'(例如在 setIsAttachmentMode(true) 附近);只门控 activeCategory 属性是不够的,因为过滤发生在上游的 useCompletion。
— qwen3.8-max via Qwen Code /review (v0.21.8)
|
Released in v0.21.10. |
What this PR does
Makes bare Left and Right switch the visible category tabs in
@completion, replacing the Ctrl+arrow and Ctrl+Tab bindings. The arrows are consumed only when the tab bar is actually shown. Vim mode follows the same visible-tab contract, while command search, reverse search, export suggestions, attachment selection, and completion menus with at most two categories retain their existing arrow behavior.While the category tabs are visible, Ctrl+B and Ctrl+F remain available for character-wise caret movement. Esc dismisses the menu and restores normal bare-arrow caret movement. Alt/Option+arrow word movement and terminal-owned Ctrl+arrow behavior are unaffected.
The on-screen hint and keyboard shortcut reference are updated to match.
Why it's needed
The previous Ctrl+arrow gesture was commonly intercepted for word movement or by macOS Mission Control, and Ctrl+Tab is also intercepted by many terminals. Bare arrows restore the interaction originally promised by the category-tab UI while limiting the ownership change to the state where users can see those tabs.
Reviewer Test Plan
How to verify
@completion with at least three available categories and confirm the hint reads(←/→ to switch).Evidence (Before & After)
Before: the tab bar advertised Ctrl+arrow and Ctrl+Tab gestures that were commonly intercepted before reaching the application. After: it advertises
(←/→ to switch), and bare arrows switch only while the category tabs are visible. Automated interaction tests cover Vim precedence, hidden search tabs, attachment ownership, two-category behavior, removed modified-arrow bindings, and the visible hint. No screenshot or recording was captured.Tested on
Environment (optional)
Node.js 22 in a local macOS worktree.
Risk & Scope
Linked Issues
Related to #8069 and #8330. Follow-up to #7302 and complementary to #8395.
中文说明
本 PR 做了什么
让
@补全在分类标签可见时使用裸 Left 和 Right 切换分类,替换 Ctrl+方向键与 Ctrl+Tab 绑定。只有标签栏真实显示时才会消费方向键。Vim 模式遵循同一可见标签合同;命令搜索、反向搜索、导出补全、附件选择,以及最多只有两个分类的补全菜单都保留原有方向键行为。分类标签可见期间,Ctrl+B 和 Ctrl+F 仍可逐字符移动光标;按 Esc 关闭菜单后,裸方向键恢复常规光标移动。Alt/Option+方向键的按词移动以及终端自身处理的 Ctrl+方向键不受影响。
界面提示与键盘快捷键文档同步更新。
为什么需要
此前的 Ctrl+方向键通常会被终端用于按词移动,或被 macOS 调度中心拦截;Ctrl+Tab 也经常被终端占用。裸方向键恢复分类标签界面最初承诺的交互,并把按键所有权变化严格限制在用户能看到这些标签的状态。
Reviewer 测试计划
如何验证
@补全,确认提示为(←/→ to switch)。证据(Before & After)
Before:标签栏提示 Ctrl+方向键与 Ctrl+Tab,但这些按键通常在抵达应用前就被拦截。After:提示为
(←/→ to switch),且裸方向键只在分类标签可见时切换分类。自动交互测试覆盖 Vim 优先级、搜索状态的隐藏标签、附件按键所有权、双分类行为、已移除的修饰键绑定和可见提示。本次没有录制截图或视频。测试平台
环境(可选)
Node.js 22,本地 macOS worktree。
风险与范围
关联 Issue
关联 #8069 与 #8330;为 #7302 的后续修复,并与 #8395 互补。