fix(cli): submit exact OpenTUI slash commands from live input - #10987
Conversation
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
Thanks for the PR! Template looks good ✓ — every section filled in, and the Chinese body is a real translation rather than a stub. Problem: observed, not theoretical. #10976 was auto-filed from a main-branch Direction: aligned. This is the OpenTUI counterpart of the Ink-side race already fixed in #10929, and it targets a lane that is red on Size: the core-module gate does not apply. Both files sit under Approach: the scope feels right, and it is close to the minimal version of this fix. Instead of adding a new mechanism it re-derives Risk: no elevated risk signals — neither file matches the revert-correlated path list. One thing to know up front, which I pick up again in the review: the Moving on to code review. 🔍 中文说明感谢贡献! 模板完整 ✓ —— 各小节都填写了,中文正文也是完整翻译而非占位。 问题: 是已观测到的 bug,不是理论性加固。#10976 由 main 分支 方向: 对齐。这是 #10929 已在 Ink 侧修复的补全竞态在 OpenTUI 的对应问题,且针对的是 main 上已经变红的 lane。修输入路径是正确的选择——如果改成在 E2E 里加等待或重试,job 会绿,但真实用户仍会碰到这个竞态。 规模: 核心模块门禁不适用。两个文件都在 方案: 范围合理,基本就是这个问题最小的修法。它没有引入新机制,而是在 Enter 处理里用 风险: 无升级风险信号——两个文件都不匹配与 revert 相关的路径清单。有一点先说明,我在 review 里还会再提: 进入代码审查 🔍 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
qwen-code-dev-bot
left a comment
There was a problem hiding this comment.
Reviewed at head 77b10bd2.
- The OpenTUI counterpart of #10929, and done the tighter way: instead of re-implementing the exact-match predicate, Enter re-runs the render path's own
detectCompletionTarget+parseSlashCommandQuery+isPerfectSlashMatchagainst the live editor (cursor-mapped, multi-line aware), so the live decision is equivalent-by-construction to what the next render would compute — no grammar copy to drift.slashStateRefsheds only itsperfectfield; suggestion acceptance still uses the effect-published range, and the mode check moved from the render'scompletionModeRefto the livetarget?.mode(undefined target → not perfect → accepts, same as before for AT mode and non-commands). No danglingperfectreferences at this head; cost is one synchronous parse per Enter. - Both race directions are pinned by new tests in the same frame the failure needs: final char + Enter in one React frame must submit
/quit, and a live partial under stale perfect-match must still autocomplete — exactly the Ink-side pair from #10929 adapted to OpenTUI. - No review history or threads to reconcile; nothing in my own pass found worth blocking.
- CI facts: 11 checks pass, zero assertion failures; Test/Lint/triage are still in flight (the authoritative run of this very spec, since local re-execution was not possible in my environment — the
@opentui/reactsnapshot here predates the packages). The lone red isDependency CVE audit, whose job died onnpm error audit endpoint returned an error(registry 503) with production showingfound 0 vulnerabilities— the same endpoint flake that reddened the identical check on #10979 this morning, and this PR touches no manifest or lockfile. Per the channel convention the call is on the review itself; a re-run clears that leg.
Code reviewRead statically against The core of the change is right. Both new tests are load-bearing rather than decorative — I checked each one against the pre-change logic:
They also reuse the existing harness helpers ( One divergence worth a look (non-blocking)The fix makes History recall is the reachable path. The
Net effect: the keystroke is swallowed. Before this PR the stale I want to be clear about the weight here: this needs an exact-match dropdown, a history recall, and both keys in one React frame, and the symptom is a no-op Enter that a second press recovers from — not corruption. It is also the mirror image of a case this PR genuinely improves (backspacing If you do want to close it, the shape is roughly one word — require a live target before taking the accept branch, since a null live target means the open dropdown is stale by definition and Enter should fall through to if (showing && target && (!isPerfectMatch || suggestionNavigatedRef.current)) {Not verified: I could not execute this to confirm the window is reachable in the real OpenTUI runtime — running PR code is out of bounds on this path, and the unit harness drives a fake editor rather than real frame timing. The NitThe comment above Test evidenceThis is an unattended CI run, so the evidence below is the PR's own CI read through the API — real check names and conclusions for
The two lanes that matter most for this diff were still running when I fetched, so I am reporting them as pending rather than guessing an outcome — the finalize job rewrites the table above once CI settles. For attribution: the description reports focused ESLint and Prettier passing locally, and says the focused Vitest file could not be collected in the author's isolated worktree because its dependency snapshot predates the OpenTUI packages. That is the author's account of their own environment, not evidence I can vouch for — which is precisely why the pending Sandboxed verification would settle what static review cannot. The gap worth naming explicitly: 中文说明代码审查以下是针对 改动的核心是对的。 两个新测试都是真正有承重作用的,我逐个对照改动前的逻辑验证过:
它们也复用了既有 harness helper( 一处值得看看的行为分歧(非阻塞)这个修复让 可达路径是历史回溯。
最终结果:这次按键被吞掉。改动前,过期的 我想把权重说清楚:这需要"完整匹配的下拉框 + 历史回溯 + 两个按键落在同一个 React frame",症状是一次空操作的 Enter,再按一次即可恢复——不是数据损坏。而且它是本 PR 确实改好的那个场景的镜像(从 如果确实想收掉它,大致只需一个词——在走 accept 分支前要求存在实时 target,因为实时 target 为 null 就意味着当前下拉框按定义已过期,Enter 应当落到 if (showing && target && (!isPerfectMatch || suggestionNavigatedRef.current)) {未验证:我无法执行代码来确认这个窗口在真实 OpenTUI 运行时中确实可达——在此路径上运行 PR 代码是被禁止的,而单元测试 harness 驱动的是 fake editor,不是真实帧时序。下面的 小问题
测试证据这是无人值守的 CI 运行,因此下面的证据是通过 API 读到的本 PR 自己的 CI——针对 对本 diff 最重要的两条 lane 在我获取时仍在运行,所以我如实标为 pending,而不是猜测结果——CI 落定后 finalize job 会重写上面的表格。 关于归属:PR 描述称本地聚焦的 ESLint 与 Prettier 通过,并说聚焦的 Vitest 文件在作者的隔离 worktree 中无法收集,因为其依赖快照早于 OpenTUI 包。这是作者对自己环境的陈述,不是我能背书的证据——这也正是上面那条 pending 的 沙箱化验证可以判定静态审查判定不了的部分。 需要点明的缺口: — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Confidence: 4/5 — correct diagnosis, minimal fix, and two regression tests that actually fail without it; the nits below are non-blocking. Stepping back: before I read the diff I wrote down what I would do from the title and the "why" alone — re-derive the submit-vs-complete decision from the live editor buffer inside the Enter handler, using the same parse the completion effect already uses, and delete the cached flag rather than trying to keep it fresh. That is what this PR does. The alternatives I came up with were all worse: calling the refresh synchronously from the key handler pushes state updates into an event and still leaves The part that convinced me this is a fix and not a plausible-looking edit is that the mechanism is checkable in the code without running anything. What holds it at 4 rather than 5 is that the fix is only half-applied to its own guard: On the evidence: I have to be straight about what is and is not verified. No check was red when I fetched, and the two OpenTUI-adjacent lanes that did finish ( Approving on the merits. CI is still running on this commit, so approval is deferred until it lands green on 中文说明Confidence: 4/5 —— 诊断准确、修法最小,且两个回归测试在没有该改动时确实会失败;下面几点均为非阻塞。 退一步看:在读 diff 之前,我只凭标题和"为什么需要"写下了自己会怎么做——在 Enter 处理里用补全 effect 本来就在用的同一套解析、从实时编辑器缓冲区重新推导"提交还是补全"的决策,并删掉缓存标志而不是设法让它保持新鲜。这正是本 PR 所做的。我想到的其他方案都更差:在按键处理里同步调用刷新,会把 state 更新塞进事件里,而且 真正让我确信这是修复而非"看起来合理"的改动,是它的成因无需运行任何代码就能在源码里核实。 之所以停在 4 分而不是 5 分,是因为这个修复对它自己的判断条件只做了一半: 关于证据:我必须把已验证与未验证分清楚。我获取时没有红色 check,已完成的两条与 OpenTUI 相关的 lane( 按实质表现批准。CI 仍在该 commit 上运行,因此批准将推迟到它在 — Qwen Code · qwen3.8-max-2026-09-02 Reviewed at |
|
Post-merge review (merged while this review was running; findings are for follow-up, not a merge gate) No blocking findings. Tier: Standard. FindingsMinor — stale The three-line comment immediately above What I checked
Cross-check with ci-bot stage-3:
Not covered: no local build / unit-test run (OpenTUI deps absent in this environment); E2E lane does not trigger on Reviewed with AI assistance. |
What this PR does
This change makes OpenTUI decide whether Enter should submit an exact slash command from the editor's current text instead of completion state produced by the previous React render. It also adds regression coverage for both stale-state directions: an exact
/quitmust submit, while a partial command must not be dispatched as an old exact match.Why it's needed
The main-branch OpenTUI E2E job repeatedly failed because the final
tin/quitand Enter can arrive in the same React frame. The editor already contains/quit, but the completion state can still describe/qui, so Enter accepts the stale suggestion instead of submitting the command. The input remains on screen and the process never reaches the already-correct interrupt and exit path. The stale Enter path originated in the OpenTUI input batch from #10368. The same job's bareexitcase succeeds because it does not enter slash completion. This is the OpenTUI counterpart of the stale completion race fixed for Ink by #10929.Failing job: https://github.com/QwenLM/qwen-code/actions/runs/33820259657/job/100861214441
Reviewer Test Plan
How to verify
/quit, and press Enter immediately after the final character. The active response should be interrupted and the CLI should exit with code 0 without waiting for the held stream.Evidence (Before & After)
Before: the linked main-branch job retried the
/quitcase three times; each attempt timed out after 30 seconds while the input and completion menu remained visible. The bareexitcase completed in 3.7 seconds.After: the regression test sends the final
tand Enter in one React frame and expects/quitto be submitted and the editor to clear. Standard PR CI remains the authoritative OpenTUI runtime verification because the isolated local worktree did not have the newly required@opentui/reactpackage installed.Tested on
Environment (optional)
Focused ESLint and Prettier checks passed. The focused Vitest file could not be collected in the isolated worktree because its dependency snapshot predates the OpenTUI packages; no full dependency installation or broad build was run locally.
Risk & Scope
Linked Issues
Fixes #10976
中文说明
本 PR 做了什么
本改动让 OpenTUI 在按下 Enter 时直接依据编辑器当前文本判断是否为完整 slash command,不再依赖上一次 React 渲染产生的补全状态。同时增加两个方向的回归覆盖:完整的
/quit必须提交,而部分命令不能因为旧状态曾经是完整匹配就被错误执行。为什么需要
main 分支的 OpenTUI E2E 反复失败,是因为
/quit最后的t和 Enter 可能在同一个 React frame 内到达。此时编辑器已经是/quit,但补全状态仍可能停留在/qui,Enter 因而接受旧补全项而不是提交命令。输入内容继续留在界面上,进程也没有进入本来已经正确的中断和退出路径。这个使用旧状态的 Enter 路径最初由 OpenTUI 输入批次 #10368 引入。同一 job 中裸exit能成功,是因为它不会进入 slash 补全。这是 #10929 已为 Ink 修复的补全状态竞态在 OpenTUI 中的对应问题。失败 job:https://github.com/QwenLM/qwen-code/actions/runs/33820259657/job/100861214441
Reviewer Test Plan
如何验证
/quit,并在最后一个字符后立即按 Enter。活动响应应被中断,CLI 应以退出码 0 结束,不等待被保持的响应流。前后证据
改动前:链接中的 main 分支 job 对
/quitcase 重试三次,每次都在 30 秒后超时,输入和补全菜单仍留在屏幕上;裸exitcase 在 3.7 秒内完成。改动后:回归测试在同一个 React frame 中发送最后的
t和 Enter,并断言/quit被提交且编辑器被清空。由于隔离的本地 worktree 没有安装新要求的@opentui/react包,标准 PR CI 是 OpenTUI 运行时的权威验证。测试平台
环境(可选)
聚焦的 ESLint 和 Prettier 检查已通过。隔离 worktree 的依赖快照早于 OpenTUI 包,因此无法收集聚焦的 Vitest 文件;本地没有执行全量依赖安装或整仓构建。
风险与范围
关联 Issue
Fixes #10976