feat(web-shell): add async submit preparation - #9802
Conversation
|
Thanks for the PR — re-run after three follow-up commits (head is now
Moving on to code review. 🔍 中文说明感谢贡献——这是在三个后续提交之后的重新运行(当前 head 为
进入代码审查 🔍 — Qwen Code · qwen3.8-max Reviewed at |
Code review (re-run on
|
| Check | Conclusion |
|---|---|
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 |
route |
✅ success |
Secret scan (TruffleHog) |
✅ success |
Test (ubuntu-latest, Node 22.x) |
✅ success |
web-shell E2E Smoke (ubuntu-latest, Node 22.x) |
✅ success |
One row per check name (latest run); skipped checks omitted; failures sort first. / 每个检查名一行(取最新一次运行),省略 skipped,失败项排在最前。
All substantive checks are green on the reviewed commit; the macOS/Windows test legs report skipped as their check conclusion (the fork-PR CI pattern, not failures).
中文说明
代码审查(在 2f17b33 上重新运行)
自上一轮之后又落了三个后续提交;本轮对当前 head 做了端到端审查,重点看变化部分。无 Critical 或阻塞项。
对照现有提交管线逐条核对:
- 第一轮 Critical 已修复,且修法最小。 失败 prompt 重试现在传
skipPrepareSubmit: true而非retry: true。核对过 base:该调用点此前只有optimisticUserMessage: false+inputAnnotations,因此对 daemon 可见的行为与 main 完全一致——新选项只用于跳过新回调。第一轮提出的丢失 transcript 场景(daemon 重试语义跳过从未创建的用户消息记录)已关闭,测试does not resend a failed-prompt retry with daemon retry semantics用retry: undefined断言钉住了这一点。Ctrl+Y turn-error 路径保持原有的retry: true不变。 - slash 准备后的解除(本轮新增)。 准备后以
/开头的提交永不重试,因此新分支清空全部 8 个重试相关 ref(最近 prompt/图片/文件/annotations 以及 4 个 turn-error ref)。核对过守卫:对重试(!opts?.retry)和失败重试(optimisticUserMessage !== false)均不触发,重试不会解除自身。共 3 个测试覆盖——基础场景、附件变体、暂存 turn-error 变体。 - 过期防护。 直接路径三次复查
admissionSourceIsCurrent()——宿主准备之后、整个 preflight 之后、会话分配之后;排队路径在每次宿主 await 之后、入队之前复查submissionOwnerIsCurrent()。prepare 之后的中断路径——上一轮我标记的缺口——现在直接和排队路径都有测试,断言下游均不执行且草稿/准备状态被恢复。 - 契约测试钉住五项声明。 顺序(
prepare→ 门禁 → 传输)、入队前冻结的排队快照、重试复用首次准备后的 payload 且不再重新准备、空准备结果取消且保留草稿、本地 slash/shell 命令绕过准备——每项都有在 base 分支上不可能通过的测试(base 上该 prop 不存在)。resolvePreparedSubmit在宿主返回 void/畸形值时回退到原 payload,是合理的边界默认。 - 剩余事项即 /review 线程中已跟踪的 S 级探针(例如
/language ui这类内部 daemon 同步会经过宿主的prepareSubmit,宿主可以观察甚至改写它;排队路径门禁对准备后 prompt 的断言)。这些是已记录的后续项,不是合入阻塞——language 同步发送的其他行为与 main 完全一致。
测试证据说明
本次为无人值守 CI 运行,未构建或执行 PR 代码(此路径没有 tmux 真实场景部分);证据来自 PR 自身 CI 在受审提交上的结果(经 API 获取)。新增单测仍是承重证据:没有此 diff 不可能通过,因此下面变绿的 Test (ubuntu-latest) 运行证实了五项契约声明。一位维护者还在同一提交上做了本地验证并在线程中报告全绿——那是维护者自己的结果,此处仅作背景引用,不作为本审查的证据。有一层按构造就超出仓库内所有通道:真实嵌入宿主中的行为——仓库内不存在下游宿主。一个受赞助的沙箱验证任务已作为本次 triage 重跑的一部分在运行,报告会另行发布。
CI 表格(英文部分):受审提交上所有实质检查均为绿色;macOS/Windows 测试项的检查结论为 skipped(fork PR 的 CI 模式,非失败)。
— Qwen Code · qwen3.8-max
Reviewed at 2f17b3335d5b41101e96651a9017b05ed3b51ec1 · re-run with @qwen-code /triage
|
Confidence: 4/5 — the follow-up commits resolved the only Critical cleanly and pinned every fix with real oracles; the remaining reservations are recorded non-blocking follow-ups, not doubts about the code. Reflecting on the whole picture: this re-run is mostly about whether the autofix loop's three commits fixed what round 1 found without growing the PR past its goal — and they did. The Reservations, plainly, none blocking: no in-repo consumer of This approval supersedes the round-1 changes-requested review on the earlier commit — the finding it cited is fixed and test-pinned on the current head. 中文说明置信度:4/5 —— 后续提交干净地解决了唯一的 Critical,并为每个修复钉上了真实的验证用例;剩余保留意见是已记录的非阻塞后续项,而非对代码的疑虑。 整体反思:这次重跑主要是检验 autofix 循环的三个提交是否在不让 PR 超出目标的前提下修复了第一轮发现的问题——答案是肯定的。 保留意见,直说,均不阻塞: 本次批准取代第一轮在早期提交上的 changes-requested 审查——该审查引用的问题已在当前 head 上修复并有测试钉住。 — Qwen Code · qwen3.8-max Reviewed at |
🖼️ web-shell visual previewRendered against a mock daemon (no real backend): the PR base vs this PR head Screenshots · before / afterFull-resolution recordings (.webm) are attached to the workflow run. — Qwen Code · web-shell visuals |
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. ✅
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
Not explored to full depth (tool budget reached): chunk 2: none — but I did not run the web-shell test suite or a live daemon to dynamically confirm the transcript-loss scenario in the Critical finding; it rests on the ….
Test Plan (not a blocker): 525 passed — this review observed 4164 passed.
中文说明
未探索到全部深度(达到工具调用预算):chunk 2:none — but I did not run the web-shell test suite or a live daemon to dynamically confirm the transcript-loss scenario in the Critical finding; it rests on the …。
Test Plan(非阻断):525 passed — this review observed 4164 passed。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
@qwen-code takeover |
|
@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,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 |
The failed-prompt retry reused `retry: true` only to skip prepareSubmit, but the flag is also forwarded to the daemon, whose retry branch skips recording the user message — the retried turn ran and was answered, yet its user prompt never reached the transcript. Skip preparation with an internal skipPrepareSubmit flag instead; the Ctrl+Y turn-error retry keeps `retry: true`, where the daemon semantics are correct. Also classify the retry payload from the prepared prompt so a host rewrite that changes slash-ness still arms the turn-error retry. Adds focused coverage for the resolvePreparedSubmit fallbacks, preparation-time staleness cancellation on both paths, prepareSubmit rejection, and the queued empty-prepared guard.
|
🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下: Autofix review round — PR #9802Round outcome: changed — 1 Critical fixed, 5 Suggestions addressed (2 of them with code fixes; every point witnessed by new tests). One additive commit: Feedback points and dispositions
Review body and issue-level comments
Mutation probes (every round-added guard/branch witnessed)
The source was verified byte-identical to the fixed state after the probe run, and the full suite re-ran green afterwards. Conflict notesNone — invoked with Verification
中文说明Autofix 审查轮次 — PR #9802本轮结果:有改动 — 修复 1 个 Critical,处理 5 个 Suggestion(其中 2 个涉及代码修复;每个问题均有新增测试作为见证)。一个增量提交: 反馈点与处置
审查主体与 issue 级评论
变异探针(本轮新增的每个守卫/分支均有见证)
探针运行结束后已验证源码与修复状态逐字节一致,且全量测试重新跑绿。 冲突说明无——调用参数为 验证
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.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Test Plan (not a blocker): 525 passed — this review observed 4173 passed.
Deferred under the convergence posture (round 2, not a blocker) — recorded, not requested in this round:
packages/web-shell/client/App.tsx:6698 — [probe] The queued admission gate receiving the prepared prompt is untested with both prepareSubmit and onSubmitBefore presentpackages/web-shell/client/App.test.tsx:12149 — [probe] The ordering test never records the 'submit' session-change event, so its position relative to the gate is unpinned
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:反向审计——在 5 轮的反审轮数上限内未收敛。
Test Plan(非阻断):525 passed — this review observed 4173 passed。
收敛姿态下延后(第 2 轮,非阻断)——已记录,本轮不要求修改:共 2 条(原文未翻译,列表见上方英文部分)。
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🤖 Addressed the latest review feedback (round 2/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 2/100 轮)。改动内容与我反驳保留之处如下: Autofix round summary — PR #9802Round: address-review on Feedback triage
The two review-body items deferred under the convergence posture (the queued admission gate receiving the prepared prompt with both callbacks present, and the ordering test not recording the ChangesR2-1 — disarm stale retry state on slash submits (App.tsx)Reproduced first: the new test records a first prompt ("hello"), then submits a second prompt that The recording gate now has a scoped else-branch: when a non-retry submit resolves to a slash command (raw or prepared), it disarms Scoping note: the suggestion's R2-2 / R2-3 — retry-classification test (App.test.tsx)
R2-4 — queued snapshot pin (App.test.tsx)
R2-5 — queued pre-gate staleness pin (App.test.tsx)
Mutation probes (witness verification)Each new guard was probed by temporarily mutating the source, confirming the witness test FAILS, then restoring and re-running to green:
Additionally, the R2-1 test fails on the pre-round code (defect reproduction), satisfying the pre-round red requirement for a code-level defect fix. Conflict resolutionNone — Verification
中文说明Autofix 轮次总结 — PR #9802轮次:在 反馈分类
审查正文中按收敛姿态延后的两条(同时传入两个回调时排队准入门禁接收 prepared prompt 的场景、以及排序测试未记录 变更内容R2-1 — 斜杠提交时解除旧重试状态的 armed 状态(App.tsx)先复现:新测试先记录第一条 prompt("hello"),再提交一条被 记录门禁现在新增了一个限定作用域的 else 分支:当一个非重试提交 resolve 为斜杠命令(无论是原始输入还是准备阶段改写)时,解除 作用范围说明:建议中的 R2-2 / R2-3 — 重试分类测试(App.test.tsx)
R2-4 — 排队快照钉住(App.test.tsx)
R2-5 — 排队门禁前过期检查钉住(App.test.tsx)
变异探针(见证验证)每个新增守卫都通过临时变异源码做了探针验证:确认见证测试失败,然后恢复并重跑至绿:
此外,R2-1 的测试在轮次前的代码上失败(缺陷复现),满足代码级缺陷修复对"轮前必须红"的要求。 冲突处理无—— 验证
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.
Partially reviewed — gaps disclosed. Suggestions are inline.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Test Plan (not a blocker): 525 passed — this review observed 4174 passed.
Deferred under the convergence posture (round 3, not a blocker) — recorded, not requested in this round:
packages/web-shell/client/App.tsx:6064 — [probe] Empty-prepared bail-out runs after submitBefore, so the host gate observes a submission that is then discardedpackages/web-shell/client/App.tsx:9311 — [probe] Deferred composer commit is unpinned when prepareSubmit is the only async preflight; mutant keeps the suite greenpackages/web-shell/client/App.tsx:6714 — [probe] No test verifies the queued path's onSubmitBefore gate receives the prepared prompt (recorded in round 2 as a deferred finding at App.tsx:6698)packages/web-shell/client/App.tsx:9363 — [probe] Uncertain-admission recovery payload is unpinned to the prepared prompt/annotationspackages/web-shell/client/App.tsx:571 — [probe] Explicit inputAnnotations: [] stripping non-empty originals is unpinned in resolvePreparedSubmitpackages/web-shell/client/App.tsx:5980 — [probe] App-internal control sends (/language ui, /model --fast) are routed through the host's prepareSubmit and can be rewrittenpackages/web-shell/client/App.tsx:6680 — [probe] Queued-path async preflight has no re-entrancy guard; a repeated Enter while prepareSubmit is pending enqueues the draft twice
Convergence: round 3 posted 2 inline comment(s), 2 of them reported for the first time; the previous round posted 5 (5 new). Findings keep coming back to the same files: packages/web-shell/client/App.test.tsx (findings in round 2; 1 more now); packages/web-shell/client/App.tsx (findings in round 2; 1 more now). A cluster that keeps producing siblings usually means the fixes are treating instances of a shared root cause — triaging that cause before the next round, or splitting an independent cluster into its own pull request, tends to end the loop faster than fixing them one at a time. (Observation only — nothing was withheld from this review because of this observation.)
中文说明
仅完成部分审查,审查缺口已披露。 建议见行内评论。
未审查:reverse audit — did not converge within the reverse-audit round cap of 5。
Test Plan(非阻断):525 passed — this review observed 4174 passed。
收敛姿态下延后(第 3 轮,非阻断)——已记录,本轮不要求修改:共 7 条(原文未翻译,列表见上方英文部分)。
收敛情况:第 3 轮发布了 2 条行内评论,其中 2 条是首次提出;上一轮发布了 5 条(其中 5 条首次提出)。发现反复回到同一批文件:packages/web-shell/client/App.test.tsx(第 2 轮已出过发现,本轮又有 1 条);packages/web-shell/client/App.tsx(第 2 轮已出过发现,本轮又有 1 条)。一个不断再生兄弟发现的簇,通常意味着逐条修复只在处理同一根因的实例——先定位并处理该根因,或把独立的簇拆成单独的 PR,通常比逐条修复更快结束循环。(仅为观察——本轮评审未因此扣留任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.0)
… stash variants Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
|
🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下: Address-review summary — PR #9802 (round 3)Feedback dispositions
Changes
Mutation probes (witness verification)Each new guard line was proven load-bearing by temporarily deleting it and re-running the focused tests:
Conflict notes
Verification
中文说明处理评审摘要 — PR #9802(第 3 轮)反馈处理结论
变更内容
变异探针(见证验证)通过临时删除每条新增守卫对应行并重跑聚焦测试,逐条证明其必要性:
冲突说明
验证
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.
Partially reviewed — gaps disclosed.
Not reviewed: reverse audit — did not converge within the reverse-audit round cap of 5.
Not explored to full depth (tool budget reached): "agent reverse-audit (round 3)": Enter-key submit path gating on isPreparing inside useComposerCore not verified — only the send-button path (ChatEditor composerPreparing ) was confirmed, ….
Test Plan (not a blocker): 525 passed — this review observed 4176 passed.
Deferred under the convergence posture (round 4, not a blocker) — recorded, not requested in this round:
packages/web-shell/client/App.tsx:9363 — [probe] Unknown-admission prepared-payload record unpinnedpackages/web-shell/client/App.tsx:6716 — [probe] Queued-path host gate prepared-prompt assertion missingpackages/web-shell/client/App.tsx:5980 — [probe] Internal daemon syncs routed through host prepareSubmit (settings/editor language, /model --fast)packages/web-shell/client/App.test.tsx:5537 — [review] /help local handling lacks a positive test assertionpackages/web-shell/client/App.test.tsx:12152 — [review] Empty-prepared cancel test omits the isPreparing reset assertionpackages/web-shell/client/App.tsx:567 — [review] Malformed prepareSubmit result discarded without console.warnpackages/web-shell/client/App.tsx:9541 — [review] Language-sync deferComposerCommit extension unpinnedpackages/web-shell/client/App.test.tsx:12240 — [probe] Mid-flight isPreparing === true never pinnedpackages/web-shell/client/App.test.tsx:5537 — [probe] Local slash-branch return-true composer-commit contract unpinnedpackages/web-shell/client/App.tsx:6135 — [probe] Internal syncs disarm a pending turn-error retry even on successpackages/web-shell/client/App.test.tsx:12198 — [probe] Annotation-strip (empty array) branch unpinnedpackages/web-shell/client/App.test.tsx:12169 — [probe] Malformed-result fallback behaviour unpinnedpackages/web-shell/client/App.tsx:6064 — [probe] Empty-prepared bail-out runs after the host gate
Mechanism health: this round did not close cleanly, so it withholds the incremental anchor — and the round it recovered had none either, so the next review re-reads the whole diff and will keep doing so until a round's marker carries an anchor again. (Stated, not acted on — this changes nothing about what the round posts.)
中文说明
仅完成部分审查,审查缺口已披露。
未审查:reverse audit — did not converge within the reverse-audit round cap of 5。
未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 3)":Enter-key submit path gating on isPreparing inside useComposerCore not verified — only the send-button path (ChatEditor composerPreparing ) was confirmed, …。
Test Plan(非阻断):525 passed — this review observed 4176 passed。
收敛姿态下延后(第 4 轮,非阻断)——已记录,本轮不要求修改:共 13 条(原文未翻译,列表见上方英文部分)。
机制健康:本轮未能干净收尾,因而扣留了增量锚点,而它恢复到的那一轮也没有锚点,因此下一次评审将重读整个 diff——并会一直如此,直到某一轮的标记重新带上锚点。(仅陈述,不据此行动——这不改变本轮发布的任何内容。)
— qwen3.8-max via Qwen Code /review (v0.22.0)
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: Autofix round: no action (growth-audit round, verdict
|
Local verification report for PR #9802Status: ✅ Verified — feature works as intended, tests/typecheck/lint/build all green. Environment
What was verifiedPR #9802 introduces an optional asynchronous Verification steps
Key test coverage observedThe new tests exercise the contract described in the PR body:
Implementation notes from code review
Caveats / non-blocking observations
RecommendationApprove for merge after the usual rebase/CI cycle. The change is minimal, well-tested, and keeps the existing 📝 中文验证报告(点击展开)PR #9802 本地验证报告状态: ✅ 验证通过 — 功能按预期工作,测试 / 类型检查 / Lint / 构建全部通过。 验证环境
验证内容PR #9802 为 验证步骤
核心测试覆盖新增测试完整覆盖了 PR 描述中的行为约定:
代码审阅要点
注意事项 / 非阻塞观察
建议按常规 rebase / CI 流程后即可批准合并。该改动范围小、测试充分,且在保持现有 |
|
@qwen-code /triage |
|
Sandboxed verification: ❌ not passed — findings reported (agent verdict) - workflow run Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check. Scripted assertions: 5294 passed · 1 failed · 5295 total Flakiness gate: ✅ 1 changed test file(s) x 5 identical rounds, no divergence 中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查。 脚本断言:5294 通过 · 1 失败 · 5295 总计 抖动门:✅ 1 changed test file(s) x 5 identical rounds, no divergence Verification reportPR 9802 Deep Verification — feat(web-shell): add async submit preparationVerdict: 中文摘要结论:
Central claim and A/B proofCentral claim (from PR body): A/B design. The PR's own head
The 16 base failures are exactly the prepareSubmit behavior tests Two new tests pass on base by design and are labeled accordingly: Reviewer Test Plan, per step
CorrectionsNone needed — no prior review round or bot comment on record required one. Findings1. Suggestion —
|
| mutant | change | result | killed by |
|---|---|---|---|
| M0 control | none | 18/18 green | (harness-live positive control) |
| M1 | transport sends raw text instead of preparedPrompt |
KILLED, 7 red | prepares direct transport…; inherits…; classifies the retry payload…; 3× slash-disarm tests; reuses the first prepared payload… |
| M2 | drop post-prepare staleness check (direct path) | KILLED, 1 red | cancels a direct submission when the session owner changes while preparing |
| M3 | drop empty-prepared cancellation guard | KILLED, 1 red | keeps the draft when preparation removes all prompt content |
| M4 | drop slash-prepared retry-disarm branch | KILLED, 3 red | the disarm test + attachment variant + stashed-retry variant |
| M5 | failed-prompt retry forwards daemon retry: true instead of skipPrepareSubmit |
KILLED, 1 red | does not resend a failed-prompt retry with daemon retry semantics |
| M6 | drop typeof prepared.prompt !== 'string' from resolve guard |
SURVIVED | — (adjudicated above as coverage gap) |
| M7 | drop submissionOwnerIsCurrent recheck on queued path |
KILLED, 1 red | cancels a queued submission when the owner changes while preparing |
The positive control is in the same file as every mutant (all tests live in
App.test.tsx): M1 turning 7 tests red proves the runner collects and fails
this file; M0 proves the scenario set is green unmutated. Every kill is
attributed to the test named for the guard it pins — no unrelated test went
red for any mutant.
Commit 2's daemon-semantics claim was verified against the daemon's own
contract, not the PR's prose: packages/webui/src/daemon/session/types.ts
documents retry?: boolean as "strips orphaned user entries … and skips
recording a duplicate user message in the JSONL transcript. Used by Ctrl+Y
retry", and actions.ts forwards it on the wire (promptRequest['retry'] = true). At head, the turn-error retry keeps retry: true (App.tsx:10606)
while the failed-prompt retry uses skipPrepareSubmit (App.tsx:6516) — the
split the commit describes, pinned by M5's dedicated test.
Targeted gates
| gate | command | result |
|---|---|---|
| web-shell full suite (head) | npx vitest run --config vitest.config.ts in packages/web-shell |
199 files, 4207/4207 passed (logs/full-suite-head.log) |
| typecheck | npm run typecheck (tsc -p tsconfig.json --noEmit) |
exit 0 (logs/typecheck.log) |
| lint (workspace) | npm run lint in packages/web-shell (eslint over the package) |
exit 0 |
| lint liveness | planted unused-var file in scratch tree |
reported (@typescript-eslint/no-unused-vars), then removed |
| built artifact ships the API | grep of dist/ |
prepareSubmit prop in dist/types/App.d.ts, types re-exported in dist/types/index.d.ts, minified bundle contains the skipPrepareSubmit selection logic |
Not covered
- Per-commit attribution. Shallow merge-ref checkout (depth 2): only the
merge commit, base tip, and PR head are reachable;git rev-listat a
shallow boundary returns 1, while the metadata snapshot lists 4 commits.
The aggregateHEAD^1..HEADdiff was verified; individual commits
(including commit 2's and 3's fix claims) only as part of the aggregate. - Live-daemon transcript behavior. The
retry: truetranscript-skip
consequence was verified from the daemon's type contract and wire code, not
by running a real daemon turn — the harness mocks@qwen-code/webuiat the
SDK seam. This reproduces the client-side wire shape, not a daemon-side
transcript write. - Windows / Linux rendering runs (author flagged
⚠️ ); no visual change is
claimed and the surface is logic-only, so nothing was attempted here. - Repo-wide gates. Only the affected workspace (
packages/web-shell) was
run — per the PR's own note the CLI baseline has unrelated aggregate-run
failures; the PR has no CLI diff and repo-widepreflightwas out of scope. - Queued slash-prepared submits and retry state. The disarm branch covers
the direct path only. A slash prompt that enters via the queue and later
turn-errors would still find the previous submit's retry state, because the
drain bypasses the wrapper entirely. This shape is pre-existing on base
(queue drain never went through the wrapper there either) and unchanged by
this PR — recorded as an observation for a possible follow-up, not as a
finding against this diff. verify-capture.mjscovered all three headline captures; no gaps.
Assertion ledger breakdown
| source | expected outcome | pass | fail |
|---|---|---|---|
| A/B head arm (537 vitest tests) | all green | 537 | 0 |
| A/B base arm (same 537 tests) | 521 green + 16 named reds | 537 | 0 |
| Matrix M0 control (18 new tests green) | green | 1 | 0 |
| Matrix M1–M5, M7 (guard deleted → named test red) | killed | 6 | 0 |
| Matrix M6 (typeof guard deleted → any new test red) | killed | 0 | 1 |
| Scratch non-string fixture on pristine head | green | 1 | 0 |
| Scratch non-string fixture on M6 mutant | red | 1 | 0 |
| Full web-shell suite (4207 tests) | all green | 4207 | 0 |
| typecheck / lint changed / lint workspace / lint liveness | exit 0 / clean / clean / caught | 4 | 0 |
| total | 5294 | 1 |
Methodology
Environment: CI verify container (node:22-bookworm class), Node v22.23.2,
merge-ref checkout at depth 2 (HEAD = merge commit 5442c29e, HEAD^1 =
base tip b2edb80a, HEAD^2 = verified head 2f17b333; the metadata
snapshot's baseRefOid 431a0bd9 is stale relative to the merge checkout —
HEAD^1 is authoritative per the CI contract). npm ci + npm run build
were pre-run at HEAD; no rebuild was needed because vitest transforms TS
source directly. A/B base cell: git worktree add tmp/base-tree HEAD^1 with
root and package-local node_modules symlinked (realpaths asserted:
@qwen-code/webui → head tree's packages/webui, @qwen-code/sdk →
packages/sdk-typescript; both fully vi.mock()ed by the harness, and the
PR touches no dependency manifest — clean control). The head test file was
copied into the base tree (sha256-verified identical) so the harness is a
constant. Mutation runs used a second scratch worktree at HEAD
(tmp/head-scratch), restoring the pristine App.tsx between mutants and
asserting each anchor replaced exactly once. Raw logs: logs/ab-head.log,
logs/ab-base.log, logs/mut-M*.log, logs/mutation-matrix.progress,
logs/full-suite-head.log, logs/typecheck.log, capture stderr in
logs/cap-*.stderr. Harness scripts: mutation-run.mjs in this directory.
Flakiness gate log
rounds=5 files=1 skipped=0
file packages/web-shell/client/App.test.tsx: (cd packages/web-shell) npx --no-install vitest run ./client/App.test.tsx
per-file results (P=pass F=fail I=infra-exit, one letter per run):
packages/web-shell/client/App.test.tsx: PPPPP
verdict: pass
summary: 1 changed test file(s) x 5 identical rounds, no divergence
--- per-invocation detail (full copy in the artifact) ---
round 1 · packages/web-shell/client/App.test.tsx: P (exit 0)
round 2 · packages/web-shell/client/App.test.tsx: P (exit 0)
round 3 · packages/web-shell/client/App.test.tsx: P (exit 0)
round 4 · packages/web-shell/client/App.test.tsx: P (exit 0)
round 5 · packages/web-shell/client/App.test.tsx: P (exit 0)
Evidence images
Harness scripts and raw logs are in the workflow run artifacts (7-day retention).
— Qwen Code · sandboxed verification
qwen-code-ci-bot
left a comment
There was a problem hiding this comment.
LGTM, looks ready to ship. ✅
|
🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下: All 13 open inline findings were already fixed by earlier commits on this branch; this round re-verified every one of them against the current head What landed when
Findings, point by point
Every probe was run against the real tree: apply the mutant, confirm the named witness fails, restore, confirm green. The post-restore tree is byte-identical to head Issue-level comments
Verification
中文说明全部 13 条仍开放的内联反馈均已由本分支早前的提交修复;本轮在当前 head 各项修复的落点
逐点说明
每个探针都在真实代码树上执行:应用变异体、确认指定见证测试失败、恢复、确认恢复后全绿。恢复后的代码树与 head Issue 级评论
验证
Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。 🧠 Handled by Qwen Code · model/模型 |
|
Released in v0.22.2. |



What this PR does
Adds an optional asynchronous
prepareSubmitcustomization for embedded Web Shell hosts. The callback can replace the outgoing prompt and input annotations after Web Shell has handled local commands, but before submission gates, session allocation, queue admission, and transport. Direct submissions and queued submissions use the prepared snapshot, while retries reuse the already prepared payload instead of resolving mutable host context again.Why it's needed
Embedded hosts sometimes need to resolve context only when a user actually submits, such as the editor or node active at that moment. Doing this in the host before rendering either makes the context stale or requires UI event listeners, while the existing pre-submit gate is intentionally a wait-only contract. A dedicated preparation stage lets hosts attach late-bound context without changing local slash/shell command behavior or weakening Web Shell's session ownership and queue guards.
Reviewer Test Plan
How to verify
WebShellwith aprepareSubmitcallback that appends text and replacesinputAnnotations, then submit a normal prompt. Confirm preparation runs beforeonSubmitBefore, and the prepared payload reaches both the daemon transport and the prompt-submitted event./help,/theme dark, and!echo hellolocally. ConfirmprepareSubmitis not called.prepareSubmitagain.Local verification:
npm run test --workspace=packages/web-shell -- App.test.tsx— 525 passed.npm run typecheck --workspace=packages/web-shell— passed.npm run lint --workspace=packages/web-shell— passed.npm run build --workspace=packages/web-shell— passed.npm run pre-commit— passed with the staged patch unchanged.npm run preflightcompleted formatting, lint, builds, typechecks, and the full Web Shell suite, but returned 1 because unrelated CLI baseline tests failed under the aggregate run. The two server files passed when rerun serially (1077 and 15 tests);AuthDialog.test.tsxremains a baseline-only keyboard-navigation failure, and this branch has no CLI diff.Evidence (Before & After)
N/A — this is a host integration API with no standalone Web Shell visual change.
Tested on
Environment (optional)
Node.js 22-compatible monorepo environment; embedded host behavior was also exercised against a locally built Web Shell artifact.
Risk & Scope
prepareSubmitis optional and omitted hosts preserve existing behavior.Linked Issues
N/A
中文说明
本 PR 做了什么
为嵌入 Web Shell 的宿主新增可选的异步
prepareSubmit定制能力。该回调会在 Web Shell 完成本地命令处理后、提交门禁、会话分配、队列准入和网络发送前执行,并可替换最终发送的 prompt 与 input annotations。直接发送和排队发送都会使用准备后的快照;重试则复用已经准备好的 payload,不会再次解析可能已变化的宿主上下文。为什么需要
嵌入式宿主有时需要只在用户真正发送时解析上下文,例如那一刻处于激活状态的编辑器或节点。如果宿主在渲染阶段提前处理,上下文可能过期,或者需要额外监听 UI 事件;现有的发送前门禁则明确只是等待型契约。独立的准备阶段允许宿主附加发送时才确定的上下文,同时不改变本地 slash/shell 命令行为,也不削弱 Web Shell 的会话归属和排队保护。
Reviewer Test Plan
如何验证
WebShell传入一个会追加文本并替换inputAnnotations的prepareSubmit回调,然后发送普通 prompt。确认准备阶段先于onSubmitBefore,并且准备后的 payload 同时进入 daemon transport 和 prompt-submitted 事件。/help、/theme dark和!echo hello,确认不会调用prepareSubmit。prepareSubmit。本地验证:
npm run test --workspace=packages/web-shell -- App.test.tsx— 525 个用例通过。npm run typecheck --workspace=packages/web-shell— 通过。npm run lint --workspace=packages/web-shell— 通过。npm run build --workspace=packages/web-shell— 通过。npm run pre-commit— 通过,暂存 patch 未发生变化。npm run preflight已完成格式化、lint、构建、类型检查和 Web Shell 全量测试,但因为聚合运行中的无关 CLI 基线测试失败而返回 1。两个服务端测试文件串行复跑均通过(1077 和 15 个用例);AuthDialog.test.tsx仍是基线已有的键盘导航失败,本分支没有任何 CLI diff。证据(修改前后)
N/A — 这是宿主集成 API,独立 Web Shell 没有可视变化。
已测试平台
环境(可选)
兼容 Node.js 22 的 monorepo 环境;同时使用本地构建的 Web Shell 产物验证了嵌入式宿主行为。
风险与范围
prepareSubmit是可选能力,未传入的宿主保持现有行为。关联 Issue
N/A