Skip to content

docs(goal): retire the Stop-hook era prose after the implementation was removed - #11922

Merged
qqqys merged 5 commits into
QwenLM:mainfrom
qqqys:docs/goal-retire-stop-hook-prose
Sep 15, 2026
Merged

qqqys merged 5 commits into
QwenLM:mainfrom
qqqys:docs/goal-retire-stop-hook-prose

Conversation

@qqqys

@qqqys qqqys commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Removes stale Goal Stop-hook descriptions after the old hook, judge and in-memory store were retired. The settings reference, schema descriptions, hook browser documentation and bridge/Web Shell comments now describe runtime-scheduled Goal turns and persisted state. An unused locale key and the obsolete Stop-hook design are removed. The still-live reasons for prioritizing Goal controls and not restoring already executed commands are preserved next to their implementations.

Why it's needed

#11900 removed the old Goal hook, judge and store, but documentation still directed readers to that retired execution path. Goal turns and blocking Stop-hook continuations each reset the tool-call budget through separate paths. The workspace Goal list also includes paused and blocked goals from resident sessions; it is not a list of memory-only running goals.

#10795 remains open: compatibility projections and history parsing are still used. This documentation cleanup does not claim to finish their removal or change the existing wire format.

Reviewer Test Plan

How to verify

Check that Goal turns are described separately from blocking Stop-hook continuations and that all three settings-description copies agree. The hook browser documentation should no longer list /goal as a Session hook source. Goal list comments should match persisted snapshots and canonical turn counts, including paused and blocked goals. Queue comments should explain why controls bypass ordinary input and why executed commands are not restored after cancelled steering preparation.

Evidence (Before & After)

N/A for runtime behavior: only documentation, comments, a setting description and an unused locale key change. Local checks passed: build, typecheck, focused CLI tests (333), core loop-detector tests (145), ESLint, Prettier, i18n and git diff --check. The four edited TypeScript files produce identical JavaScript with comments removed.

Tested on

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

Environment (optional)

Node.js 22.17.0. No runtime behavior or UI layout changes.

Risk & Scope

  • Main risk or tradeoff: documentation accuracy; runtime logic is unchanged.
  • Not validated / out of scope: removing live compatibility projections or history parsing, rewriting other historical design documents, and changing Goal input behavior. Hosted CI is separate from local validation.
  • Breaking changes / migration notes: none.

Linked Issues

Part of #10795
Part of #4228

中文说明

这个 PR 做了什么

在旧 Goal hook、judge 与内存 store 退役之后,移除陈旧的 Stop-hook 描述。设置参考、schema 描述、hook 浏览器文档以及 bridge/Web Shell 注释现在描述由运行时调度的 Goal 轮和持久化状态。删除未使用的 locale key 与过时的 Stop-hook 设计文档,并在实现位置保留仍然有效的设计理由:Goal 控制命令为何优先处理,以及已执行命令为何不参与恢复。

为什么需要

#11900 删除了旧 Goal hook、judge 与 store,但文档仍把读者引向已退役的执行路径。Goal 轮与阻塞式 Stop-hook 续跑通过各自独立的路径重置工具调用预算。工作区 Goal 列表还包含驻留会话中已暂停、阻塞的 Goal,并非仅存于内存的运行中 Goal 列表。

#10795 保持开放:兼容投影和历史解析仍被使用。本次文档清理不宣称完成它们的移除,也不改变现有传输格式。

评审验证方式

如何验证

确认 Goal 轮与阻塞式 Stop-hook 续跑被分别描述,且三处设置描述保持一致。hook 浏览器文档不应再把 /goal 列为 Session hook 来源。Goal 列表注释应符合持久化快照与实际 Goal 轮数,包括已暂停和阻塞的 Goal。队列注释应解释控制命令为何绕过普通输入,以及 steering 准备取消后为何不恢复已经执行的命令。

证据(前后对比)

运行行为不适用:仅修改文档、注释、一条设置描述和未使用的 locale key。本地检查通过:构建、类型检查、CLI 定向测试(333 个)、core 循环检测测试(145 个)、ESLint、Prettier、i18n 与 git diff --check。四个修改的 TypeScript 文件去除注释后生成的 JavaScript 完全一致。

测试平台

OS 状态
🍏 macOS
🪟 Windows ⚠️
🐧 Linux ⚠️

环境(可选)

Node.js 22.17.0。没有运行行为或 UI 布局变更。

风险与范围

  • 主要风险或权衡:文档准确性;运行逻辑不变。
  • 未验证 / 不在范围内:移除仍在使用的兼容投影或历史解析、重写其他历史设计文档,以及改变 Goal 输入行为。远端 CI 与本地验证分别记录。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

Part of #10795
Part of #4228

…as removed

The first-generation Goal implementation was deleted in QwenLM#11900, but a
design doc, an i18n key, two comments and a setting description still
describe it: the input-control design for a Goal loop that ran as a
blocking Stop hook, the `/goal` description string nothing reads any
more, an ACP control comment naming an in-memory store and a type that
no longer exist, a "judge's verdict" field comment, and the per-turn
tool-call cap saying Goal iterations are Stop-hook continuations.
@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Sep 15, 2026
@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Deferred approval withheld — 1 PR CI workflow run(s) on d5e34b1 did not finish green; see the updated table in the Stage 2 comment. Re-run @qwen-code /triage after fixes. finalize run

⚠️ 延迟审批已搁置 —— d5e34b1 有 1 个 PR CI workflow 未以绿色完成,详见 Stage 2 评论中已更新的表格。修复后可重新运行 @qwen-code /triage查看 finalize 运行

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for the PR!

Template looks good ✓ — every required heading is present, the Risk & Scope bullets are filled in, and the Chinese translation is complete rather than abbreviated.

Problem: observed, not theoretical, and I confirmed it independently rather than taking the description's word. At this PR's base (3a093ae), goalHook, goalJudge and activeGoalStore have no real references left in the tree — the only goalJudge hit is a simulated build-error string inside scripts/tests/qwen-autofix-workflow.test.js. ActiveGoalView appears exactly once, in the very comment this PR rewrites. So the prose really does describe machinery that is gone, which is what #10795 is about. That issue is still open, so nothing here is a duplicate of a landed fix.

Direction: aligned. Retiring prose that points at deleted code is documentation accuracy, not scope creep, and it is cheap to review. Upstream's CHANGELOG has no direct reference to this (it is qwen-code's own Goal v1 → v3 migration), but /goal is a live, actively developed surface upstream, which is exactly why its docs should not describe two systems.

Size: this does reach core paths — packages/cli/src/config/settingsSchema.ts matches packages/*/src/config/**, and the diff spans four packages. Worth stating plainly so nobody has to wonder: 74 lines total (9 added, 65 removed), 72 by the production-line count (settings.schema.json excluded as a schema file), 0 test lines. Of those 72, 50 are markdown and the remaining 22 are comments, one description string, and locale data. No executable logic changes anywhere in the diff. It touches 11 files, but 10 of them change one to four lines each — the low-risk-sweep shape, not a deep rewrite. Noted for awareness, not escalated: no size threshold is anywhere near.

Approach: the scope feels right, and I specifically like that it resisted the obvious over-reach. Deleting the design doc rather than rewriting it is correct — I checked, and it has no .zh-CN.md counterpart and no inbound reference from any file, including the design index, so deleting it orphans nothing and creates no bilingual obligation. Removing the locale key only from the four locales that carry it (ca, en, zh, zh-TW) leaves all nine consistent, since de, fr, ja, pt and ru never had it. Leaving the other Stop-hook-mentioning design docs alone is justified too: GOAL_PAUSE_REASON_STOP_HOOK_CAP is still live at packages/core/src/core/client.ts:3075, so 2026-09-02-goal-pause-reasons.md describes something that exists.

I have two completeness questions from the code review — both comment-only, neither a blocker — detailed below in Stage 2.

Risk: no elevated risk signals. None of the changed files match the high-risk paths from the revert-history analysis.

Moving on to code review. 🔍

中文说明

感谢贡献!

模板完整 ✓ —— 所有必需标题都在,Risk & Scope 三项都填了,中文说明也是完整翻译而非摘要。

问题: 是已观测到的问题,不是理论性加固。我没有只采信 PR 描述,而是独立核实过:在本 PR 的 base(3a093ae)上,goalHookgoalJudgeactiveGoalStore 在代码树里已经没有真实引用——唯一的 goalJudge 命中是 scripts/tests/qwen-autofix-workflow.test.js 里一段模拟的构建报错字符串。ActiveGoalView 只出现一次,就在本 PR 改写的那条注释里。所以这些文字描述的确实是已经不存在的机制,这正是 #10795 要解决的。该 issue 仍处于 open 状态,因此本 PR 不是对已合并修复的重复。

方向: 对齐。清理指向已删除代码的文字属于文档准确性,不是范围扩张,而且 review 成本很低。上游 CHANGELOG 没有直接对应的条目(这是 qwen-code 自己的 Goal v1 → v3 迁移),但 /goal 在上游是活跃维护的功能面,这恰恰是它的文档不应该同时描述两套系统的理由。

规模: 本 PR 确实触及核心路径——packages/cli/src/config/settingsSchema.ts 匹配 packages/*/src/config/**,且 diff 跨了四个 package。这里把数字说清楚,省得别人猜:总计 74 行(新增 9、删除 65),按生产行计为 72 行(settings.schema.json 作为 schema 文件排除),测试行 0。这 72 行里有 50 行是 markdown,剩下 22 行是注释、一条描述字符串和 locale 数据。整个 diff 没有任何可执行逻辑变更。 它涉及 11 个文件,但其中 10 个各只改了一到四行——这是低风险扫改的形态,不是深度重写。仅作知会,未升级处理:完全没有接近任何规模阈值。

方案: 范围合理,尤其欣赏它没有明显越界。删除而非重写那篇设计文档是对的——我查过,它没有 .zh-CN.md 对应版本,也没有任何文件(含设计文档索引)引用它,所以删除不会留下孤儿文档,也不产生双语义务。locale key 只从携带它的四个 locale(caenzhzh-TW)中移除,反而让九个 locale 保持一致,因为 defrjaptru 本来就没有这个 key。其他提到 Stop hook 的设计文档刻意保留也站得住:GOAL_PAUSE_REASON_STOP_HOOK_CAPpackages/core/src/core/client.ts:3075 仍然有效,所以 2026-09-02-goal-pause-reasons.md 描述的东西是存在的。

代码审查里我有两个完整性方面的疑问——都只是注释层面,都不是阻塞项——详见下方 Stage 2。

风险: 无升级风险信号。改动文件均未命中基于 revert 历史分析得出的高风险路径。

进入代码审查 🔍

Qwen Code · qwen3.8-max-2026-09-02

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

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Code review

I read the base tree at 3a093ae (this PR's exact base) and checked each claim against the code rather than against the description. Everything the PR asserts holds up:

The sessionGoalGet rewrite is accurate, not just vaguer. The old comment promised { active: ActiveGoalView | null } from an in-memory store. The real handler at packages/cli/src/acp-integration/acpAgent.ts:12794 returns { snapshot, active } sourced from getGoalRuntimeReady().getSnapshot() — which is exactly BridgeSessionGoal, and exactly what the new comment now says. The in-memory-store framing was doubly wrong: that path has a GoalPersistenceUnavailableError branch, so goal state is persisted, not memory-only. Calling active "a projection of it for clients that still read the older shape" also matches the interface's own docstring two lines above it.

The lastReason rewrite matches core's semantics. packages/core/src/goals/goal-protocol.ts documents the field as "The lastReason a Goal stops with when…" for the token, turn-count and active-time budgets, plus a checkpoint-stall reason at line 80. "Why the Goal last stopped, or the verifier's most recent reason" covers both halves; "the judge's verdict" covered neither.

The tool-call cap rewording is correct, and I checked the mechanism rather than trusting it. Goal turns reset the detector on their own path — packages/core/src/core/client.ts:3557 does if (messageType === SendMessageType.Goal) { this.loopDetector.reset(prompt_id); } — separately from the blocking Stop-hook continuation resets at client.ts:4529 and client.ts:4648. So "blocking Stop-hook continuations and runtime-scheduled Goal turns each start a fresh budget" is true of both, and the old "such as /goal iterations" wrongly filed Goal turns under the Stop hook. All three copies of the string carry the identical new clause — I compared them byte for byte at the head commit, which matters because .github/workflows/ci.yml:1201 fails the build when settings.schema.json drifts from settingsSchema.ts. That CI gate is the load-bearing downstream consumer here, and the PR updates both sides of it. No test or snapshot asserts this description anywhere.

The locale key removal is safe. goalCommand.description returns t('Set or control a session goal'), not the removed key, and that key is untouched. Grepping the whole tree, the removed string appears only in the four locale files that this PR edits — no reader, and no other locale left inconsistent.

The design doc deletion orphans nothing. No .zh-CN.md counterpart exists, and nothing in the repo references the file, including the design index.

Two things it left behind — non-blocking, but both are the same sentence it just fixed

Neither is a regression; the base is equally stale. But this PR's whole deliverable is comment accuracy, so they are worth a look before merge.

1. packages/core/src/services/loopDetectionService.ts:149 still reads:

// continuation (e.g. a /goal iteration) starts a fresh budget via
// loopDetector.reset() in client.ts, so the cap bounds each iteration
// rather than an entire goal chain.

That is the source of the wording this PR corrected in three user-facing copies. The description says the cap wording "was checked against the code rather than deleted" — this is the code comment it was checked against, and it still attributes /goal iterations to a blocking Stop-hook continuation. Per client.ts:3557 that reset happens on the SendMessageType.Goal path, not the Stop-hook path. Was leaving this deliberate, to keep the diff out of packages/core/src/services/**? If so that is a defensible call and I would not block on it — but then the PR is fixing the mirror and leaving the original, which is the odd way round. One line either way.

2. packages/web-shell/client/daemon/workspace/types.ts:371 is a context line inside the very hunk this PR edits, three lines above the change:

/** Judge turns completed; 0 before the first stop-hook evaluation. */
iterations: number;

The PR retires "the judge's verdict" on line 374 but leaves "Judge turns" and "the first stop-hook evaluation" on line 371 — both retired concepts, and iterations is fed from record.turnCount (packages/cli/src/serve/routes/goals.ts:154), which counts canonical Goal turns, not judge evaluations. The parallel field in packages/acp-bridge/src/bridgeTypes.ts:940 already says "Canonical Goal turns completed so far." Since the PR treats these two structs as copies that must agree, they now diverge on the adjacent field as well as the one it fixed.

No other blockers. No over-abstraction, no duplication introduced, no code in the wrong package — the diff is comments, one description string, markdown, and unused locale data.

Files changed (11)
File What changed
docs/design/goal-loop-input-control.md Deleted whole. Described /goal as a blocking Stop hook with queue draining; no counterpart translation, no inbound reference.
docs/users/configuration/settings.md The model.maxToolCallsPerTurn row, reworded to name Goal turns instead of implying they are Stop-hook continuations.
packages/acp-bridge/src/bridgeTypes.ts One comment on BridgeSessionGoal.active.lastReason.
packages/acp-bridge/src/status.ts The sessionGoalGet docstring, now naming the real return type and the persisted runtime instead of a nonexistent store and type.
packages/cli/src/config/settingsSchema.ts The description string that feeds the schema generator. The only core-path change, and it is data, not logic.
packages/cli/src/i18n/locales/ca.js Drops the unread /goal description key.
packages/cli/src/i18n/locales/en.js Same.
packages/cli/src/i18n/locales/zh-TW.js Same.
packages/cli/src/i18n/locales/zh.js Same.
packages/vscode-ide-companion/schemas/settings.schema.json Generated mirror of the description change. Required, since CI fails on drift.
packages/web-shell/client/daemon/workspace/types.ts One comment on DaemonGoal.lastReason. Its neighbour on line 371 is finding 2 above.

Testing

This is an unattended CI run, so per the gate's rules I did not build, run, or check out any of this PR's code — the evidence below is the PR's own CI read through the API, plus my static verification of each claim against the base tree.

The unit suite is red — and it is not this PR

Test (ubuntu-latest, Node 22.x)completed / failure. One test file, 13 tests:

Test Files  1 failed | 1053 passed (1054)
     Tests  13 failed | 31425 passed | 91 skipped (31529)

FAIL  src/serve/workspace-skills-status.test.ts
AssertionError: expected [ { kind: 'skill', …(10) }, …(16) ] to deeply equal ArrayContaining{…}
-     "name": "active-skill",
+     "name": "active:active-skill",

I am classifying this as pre-existing and not caused by this PR, and here is the evidence rather than the assertion:

  1. An unrelated PR fails identically. Run 34942912668 (fix/issue-10887-tool-error-l, a tool-error-handling branch with no overlap whatsoever with this diff) fails the same single file with the same counts: Test Files 1 failed | 1053 passed (1054), Tests 13 failed, all in src/serve/workspace-skills-status.test.ts. A failure that reproduces byte-for-byte on a PR that shares nothing with this one is not this one's failure.
  2. The mismatch is a naming-convention change, not a docs change. The test expects name: 'active-skill' and gets name: 'active:active-skill' — extension Skills are now namespaced extension:skill. Nothing in this PR touches Skill naming, packages/cli/src/serve/**, or extension loading.
  3. The received array also contains bundled Skills resolved from the runner's own checkout, e.g. installedPath: /home/github-runner/actions-runner-hk3-14/_work/qwen-code/qwen-code/packages/core/src/skills/bundled/batch/SKILL.md alongside the test's isolated fixture home /var/tmp/qwen-ci-nOGCy6/qwen-catalog-home-J4gmCg/.... That is the assertion comparing against a real working tree's bundled set.
  4. This PR's diff cannot reach that code path. Its 11 files are one deleted markdown design doc, one docs table row, three comment lines, one description string in three copies, and four locale-key deletions. There is no executable change at all.

I classified this from the diff and the cross-PR reproduction, not from anything the log text says about itself. For what it is worth, an open follow-up issue #11699 ("Follow-up work left by #10841 (skill naming)") looks like the right neighbourhood — that is a lead for whoever owns it, not something I verified.

What did pass, and what it proves

The two checks that could actually break here both ran and both passed. The i18n removal and the generated-schema coupling are the only ways this diff can fail mechanically, and they are gated by steps in the lint_and_static job that are conditional on a full CI profile — so a green job alone would not prove they executed. I read the step-level results rather than trusting the job conclusion:

completed  success  22  Run ESLint
completed  success  26  Run Prettier
completed  success  28  Run i18n check
completed  success  29  Generate settings schema
completed  success  30  Check settings schema is up-to-date

Step 28 is the author's npm run check-i18n, and step 30 is the drift gate on the description string. Both genuinely ran (the profile was full, not skipped) and both are green. That independently confirms the two claims I could not have settled by reading alone — I did not run check-i18n myself, and did not need to.

Everything else I verified by reading code at the base and head commits: the handler return shape, the lastReason semantics, both loop-detector reset paths, the three-copy string identity, the locale key having no reader, and the design doc having no inbound reference. All confirmed.

Also green on this head: Lint & Static, Real daemon E2E, TUI parity snapshots, Integration Tests (no-AK, No Sandbox), Desktop Shell on both platforms, Capture web-shell visuals, OpenTUI no-flicker gate. Still running at the time of writing: web-shell E2E Smoke (ubuntu-latest, Node 22.x), leaving 1 of this PR's pull_request workflow runs incomplete. I have not polled and will not guess its outcome.

Two entries that are not build signal: label shows completed / cancelled because there are two label check-runs from two different workflow runs (34943224255 cancelled, 34943225537 success) — a superseded bot orchestration job under a cancel-in-progress concurrency group, and the later run's label succeeded. review-pr is queued bot orchestration, not PR CI. Test (macos-latest), Test (windows-latest) and Integration Tests (CLI, No Sandbox) are skipped, which is this repo's normal routing rather than a gap this PR caused.

Final CI results for d5e34b1 (auto-updated by the triage finalize job after CI completed):

Check Conclusion
Test (ubuntu-latest, Node 22.x) ❌ failure
Capture web-shell visuals (ubuntu-latest, Node 22.x) ✅ success
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Integration Tests (no-AK, No Sandbox) ✅ success
Lint & Static (ubuntu-latest, Node 22.x) ✅ success
macos-latest / Java 21 ✅ success
OpenTUI no-flicker gate ✅ success
Real daemon E2E / Java 11 ✅ success
TUI parity snapshots (ink vs opentui) ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
windows-latest / Java 21 ✅ success

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

Real-scenario tmux testing: N/A. Nothing here is user-visible at runtime — the diff is comments, markdown, one settings description string, and a locale key with no reader. There is no TUI surface to drive, and no behavioural claim for a sandboxed lane to settle.

中文说明

代码审查

我在 3a093ae(本 PR 的确切 base)上阅读了代码树,逐条对照代码而非对照 PR 描述来核实。PR 的每一项断言都成立:

sessionGoalGet 的改写是准确的,不是含糊化。 旧注释承诺的是来自内存 store 的 { active: ActiveGoalView | null }。真实 handler 在 packages/cli/src/acp-integration/acpAgent.ts:12794,返回 { snapshot, active },数据来自 getGoalRuntimeReady().getSnapshot()——这恰好就是 BridgeSessionGoal,也恰好是新注释现在写的。"内存 store" 的说法错在两处:该路径有 GoalPersistenceUnavailableError 分支,说明 goal 状态是持久化的,不是只存在内存里。把 active 称为 "a projection of it for clients that still read the older shape" 也与它上方两行接口自己的 docstring 一致。

lastReason 的改写与 core 的语义相符。 packages/core/src/goals/goal-protocol.ts 把该字段描述为 "The lastReason a Goal stops with when…",覆盖 token、turn 数与活动时间三种预算,外加第 80 行的 checkpoint 停滞原因。"Why the Goal last stopped, or the verifier's most recent reason" 覆盖了两半;而 "the judge's verdict" 两半都没覆盖。

工具调用上限的改写是正确的,我核实的是机制而不是采信结论。 Goal 轮走自己独立的重置路径——packages/core/src/core/client.ts:3557if (messageType === SendMessageType.Goal) { this.loopDetector.reset(prompt_id); }——与 client.ts:4529client.ts:4648 处阻塞式 Stop-hook 续跑的重置是分开的。所以 "blocking Stop-hook continuations and runtime-scheduled Goal turns each start a fresh budget" 对两者都成立,而旧写法 "such as /goal iterations" 把 Goal 轮错误地归到了 Stop hook 之下。三处字符串现在携带完全相同的新子句——我在 head commit 上逐字节比对过,这一点很关键,因为当 settings.schema.jsonsettingsSchema.ts 漂移时,.github/workflows/ci.yml:1201 会让构建失败。这个 CI 门禁是此处的关键下游消费者,而 PR 把它的两侧都改了。代码树里没有任何测试或快照断言这条描述。

删除 locale key 是安全的。 goalCommand.description 返回的是 t('Set or control a session goal'),不是被删掉的 key,而那个 key 未被改动。全树 grep 下来,被删字符串只出现在本 PR 编辑的四个 locale 文件里——没有读取者,也没有让其他 locale 变得不一致。

删除设计文档不会留下孤儿。 它没有 .zh-CN.md 对应版本,仓库里(含设计文档索引)也没有任何文件引用它。

两处遗留 —— 非阻塞,但都是它刚刚修好的同一句话

两者都不是回归;base 上同样陈旧。但本 PR 的全部交付物就是注释准确性,所以合并前值得看一眼。

1. packages/core/src/services/loopDetectionService.ts:149 仍是:

// continuation (e.g. a /goal iteration) starts a fresh budget via
// loopDetector.reset() in client.ts, so the cap bounds each iteration
// rather than an entire goal chain.

这正是本 PR 在三处面向用户的副本里改正的那段措辞的源头。PR 描述说上限措辞是 "checked against the code rather than deleted"——而这就是它对照的那段代码注释,它仍然把 /goal 迭代归因于阻塞式 Stop-hook 续跑。按 client.ts:3557,该重置发生在 SendMessageType.Goal 路径上,不是 Stop-hook 路径。这是刻意保留、以避免 diff 进入 packages/core/src/services/** 吗?如果是,这个取舍说得过去,我不会因此阻塞——但那样就是改了镜像、留了原件,方向有点反。无论哪种,都只是一行。

2. packages/web-shell/client/daemon/workspace/types.ts:371 是本 PR 所编辑那个 hunk 内部的一行上下文,就在改动上方三行:

/** Judge turns completed; 0 before the first stop-hook evaluation. */
iterations: number;

PR 在第 374 行退掉了 "the judge's verdict",却在第 371 行留下了 "Judge turns" 和 "the first stop-hook evaluation"——两者都是已退役的概念,而 iterations 的数据来自 record.turnCountpackages/cli/src/serve/routes/goals.ts:154),它统计的是规范 Goal 轮数,不是 judge 评估次数。packages/acp-bridge/src/bridgeTypes.ts:940 里对应的字段已经写着 "Canonical Goal turns completed so far."。既然本 PR 把这两个结构体视为必须一致的副本,那它们现在除了被修好的那个字段之外,相邻字段也出现了分歧。

无其他阻塞项。没有过度抽象,没有引入重复,没有把代码放错 package——diff 全部是注释、一条描述字符串、markdown 和无人使用的 locale 数据。

测试

这是一次无人值守的 CI 运行,因此按 gate 规则,我没有构建、运行或 checkout 本 PR 的任何代码——下面的证据是通过 API 读到的本 PR 自己的 CI,加上我在 base 代码树上对每条断言的静态核实。

单元测试套件变红了——但不是本 PR 造成的

Test (ubuntu-latest, Node 22.x)completed / failure。一个测试文件,13 个测试:

Test Files  1 failed | 1053 passed (1054)
     Tests  13 failed | 31425 passed | 91 skipped (31529)

FAIL  src/serve/workspace-skills-status.test.ts
AssertionError: expected [ { kind: 'skill', …(10) }, …(16) ] to deeply equal ArrayContaining{…}
-     "name": "active-skill",
+     "name": "active:active-skill",

我将其判定为既有问题、非本 PR 造成,下面给的是证据而不是断言:

  1. 一个无关 PR 出现完全相同的失败。 run 34942912668fix/issue-10887-tool-error-l,一个处理 tool error 的分支,与本 diff 毫无交集)在同一个文件上以相同计数失败:Test Files 1 failed | 1053 passed (1054)Tests 13 failed,全部位于 src/serve/workspace-skills-status.test.ts。一个在与本 PR 毫无共通的 PR 上逐字重现的失败,不属于本 PR。
  2. 不一致点是命名约定变更,不是文档变更。 测试期望 name: 'active-skill',实际得到 name: 'active:active-skill'——扩展 Skill 现在按 extension:skill 命名。本 PR 完全没有触及 Skill 命名、packages/cli/src/serve/** 或扩展加载。
  3. 实际返回的数组里还包含从 runner 自身 checkout 解析出的 bundled Skill,例如 installedPath: /home/github-runner/actions-runner-hk3-14/_work/qwen-code/qwen-code/packages/core/src/skills/bundled/batch/SKILL.md,与测试隔离的 fixture home /var/tmp/qwen-ci-nOGCy6/qwen-catalog-home-J4gmCg/... 并存。也就是说断言在跟真实工作树的 bundled 集合做比较。
  4. 本 PR 的 diff 根本到不了那条代码路径。 它的 11 个文件是一篇被删除的 markdown 设计文档、一行文档表格、三行注释、一条出现在三处的描述字符串,以及四个 locale key 删除。完全没有任何可执行变更。

我这个判定来自 diff 与跨 PR 重现,而不是日志文本对自身性质的任何说法。顺带一提,open 状态的后续 issue #11699("Follow-up work left by #10841 (skill naming)")看起来属于同一片区域——这是给负责人的线索,不是我核实过的结论。

通过了什么,以及它能证明什么

真正可能让本 diff 机械性失败的两个检查都跑了,而且都通过了。 i18n 删除与生成 schema 的耦合是唯一的两种失败方式,它们由 lint_and_static job 里的步骤把关,而这些步骤以 full CI profile 为条件——所以单看 job 变绿并不能证明它们真的执行了。我读的是步骤级结果,而不是采信 job 结论:

completed  success  22  Run ESLint
completed  success  26  Run Prettier
completed  success  28  Run i18n check
completed  success  29  Generate settings schema
completed  success  30  Check settings schema is up-to-date

第 28 步就是作者的 npm run check-i18n,第 30 步是针对该描述字符串的漂移门禁。两者都确实运行了(profile 是 full,没有被跳过)且都为绿。这独立确认了我单靠阅读无法定论的两条断言——我没有自己运行 check-i18n,也不需要运行。

其余部分我都是通过在 base 与 head commit 上阅读代码来核实的:handler 返回结构、lastReason 语义、两条 loop-detector 重置路径、三处副本字符串一致、locale key 无读取者、设计文档无入站引用。全部确认。

本 head 上同时为绿的还有:Lint & StaticReal daemon E2ETUI parity snapshotsIntegration Tests (no-AK, No Sandbox)、两个平台的 Desktop ShellCapture web-shell visualsOpenTUI no-flicker gate。撰写时仍在运行:web-shell E2E Smoke (ubuntu-latest, Node 22.x),这使得本 PR 的 pull_request workflow run 中还有 1 个未完成。我没有轮询,也不会猜测它的结果。

有两项不属于构建信号:label 显示 completed / cancelled,是因为存在两个来自不同 workflow run 的 label check-run(34943224255 被取消,34943225537 成功)——在 cancel-in-progress 并发组下被取代的机器人编排任务,且较新那次 run 的 label 是成功的。review-pr 是排队中的机器人编排任务,不属于 PR CI。Test (macos-latest)Test (windows-latest)Integration Tests (CLI, No Sandbox)skipped,这是本仓库正常的路由形态,不是本 PR 造成的缺口。

(CI 表格见上方英文部分,未在中文里重复。)

真实场景 tmux 测试:N/A。这里没有任何运行时可见的改动——diff 全部是注释、markdown、一条设置描述字符串和一个无读取者的 locale key。没有可驱动的 TUI 界面,也没有需要沙箱通道来定论的行为性断言。

Qwen Code · qwen3.8-max-2026-09-02

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

@qwen-code-ci-bot

qwen-code-ci-bot commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

🖼️ web-shell visual preview

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

Screenshots · before / after

No screenshot changes against the PR base.

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

Qwen Code · web-shell visuals

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — every claim in this PR checks out against the code, and the only two things I would change are stale comments it did not touch. The one red check is not this PR's, and I have the evidence for that call below.

Stepping back: this is a PR whose entire product is accuracy, so the honest test is whether it leaves the tree more accurate or just differently inaccurate. It leaves it more accurate, and I could not find a single claim in the description that did not survive contact with the code. That is not nothing — the description asserts a specific mechanism for the tool-call cap ("each runtime-scheduled Goal turn is its own sendMessageStream call"), and that assertion is right for a reason the author did not even spell out: client.ts:3557 resets the detector on the SendMessageType.Goal branch specifically, separately from the two Stop-hook continuation resets. Whoever wrote this read the reset sites rather than pattern-matching the prose.

The scope discipline is the part I would hold up as an example. Deleting a design doc instead of updating it, and saying why; removing a locale key from four files and not the other five, because the other five never had it; naming three Stop-hook-mentioning design docs it deliberately left alone with a reason for each. That is a PR that knows exactly where its edges are. My one hesitation about the edges is finding 1 in Stage 2 — it fixed the three downstream copies of a sentence and left the code comment the sentence came from, in a core-path file. If that was a deliberate choice to keep the diff out of packages/core/src/services/**, fine; if it was an oversight, it is one line. Either answer is acceptable, which is why this is a question and not a blocker.

Six months from now this is a thank-you, not a curse: nobody will have to reverse-engineer why the docs describe a Stop hook that does not exist.

Two notes on process rather than content. The diff spans four packages and reaches a core path (packages/cli/src/config/settingsSchema.ts), so it passes the core-module gate rather than skipping it — it clears that gate because 22 of its 72 production lines are comments and strings and the rest is markdown, with no executable logic anywhere. And it touches the one downstream coupling that can actually break here: ci.yml:1201 fails the build when the generated VS Code schema drifts from the schema source. Both sides are updated and byte-identical, and I confirmed at step level that the drift gate and the i18n check really ran in this PR's CI rather than being skipped by the full-profile condition — both green. So that coupling is satisfied, not merely avoided.

The part a maintainer needs to decide, because I cannot

Verdict on the diff: approve. Verdict on the merge: blocked by something this PR did not cause.

Test (ubuntu-latest, Node 22.x) landed red — 13 tests in src/serve/workspace-skills-status.test.ts, expecting name: 'active-skill' and receiving name: 'active:active-skill'. An unrelated PR (fix/issue-10887-tool-error-l, run 34942912668) fails the same file with the same counts, and this diff touches no Skill, serve, or extension code — it has no executable change at all. Stage 2 carries the full evidence. So this is a pre-existing breakage in the extension-Skill naming path, and the author cannot fix it from here without taking on someone else's work.

That matters mechanically: the marker below asks the finalize job to approve once every check on this commit is green, and a red unit suite means it will withhold and flag rather than approve. I am deliberately not approving in this run — CI is also still incomplete (web-shell E2E Smoke running, 1 pull_request run open), and I will not attest to a result that does not exist yet. I am equally not requesting changes, because there is nothing in this diff to change.

So this needs a human call, and there are exactly two sane ways out: land the workspace-skills-status expectation fix on main and re-run CI here, or approve this commit manually on the strength of the pre-existing-failure evidence. What should not happen is this PR sitting red until the author guesses that the failure is not theirs.

中文说明

Confidence: 4/5 —— 本 PR 的每一条断言都经得住与代码对照;我唯一想改的两处,是它没有触碰的陈旧注释。唯一变红的那项检查不属于本 PR,下面给出我这个判断的证据。

退一步看:这是一个全部产出即"准确性"的 PR,所以诚实的判据是它让代码树更准确,还是只是换了一种不准确。它让代码树更准确,而且我在描述里找不到任何一条断言在与代码接触后不成立。这一点并非无关紧要——描述对工具调用上限断言了一个具体机制("each runtime-scheduled Goal turn is its own sendMessageStream call"),而这个断言之所以正确,还有一个作者甚至没有点明的原因:client.ts:3557 专门在 SendMessageType.Goal 分支上重置检测器,与两处 Stop-hook 续跑的重置是分开的。写这段的人读过重置点,而不是照着文字做模式匹配。

范围克制是我最愿意拿来当范例的部分。删除而不是更新一篇设计文档,并说明原因;只从四个文件里删 locale key 而不动另外五个,因为另外五个本来就没有;点名三篇它刻意保留的、提到 Stop hook 的设计文档并各给一条理由。这是一个清楚知道自己边界在哪里的 PR。我对边界唯一的犹豫是 Stage 2 里的第 1 条发现——它改了三处下游副本,却留下了那句话的源头代码注释,而那个文件在核心路径上。如果这是为了把 diff 挡在 packages/core/src/services/** 之外的刻意选择,可以;如果是疏漏,那也就一行。两种回答都能接受,所以这是一个疑问,不是阻塞项。

六个月后回看,这是让人道谢而不是让人咒骂的改动:不会再有人需要反推为什么文档描述了一个并不存在的 Stop hook。

两点关于流程而非内容的说明。diff 跨了四个 package 并触及核心路径(packages/cli/src/config/settingsSchema.ts),所以它是通过了核心模块门禁,而不是绕过了它——它之所以能通过,是因为 72 行生产行里有 22 行是注释和字符串,其余是 markdown,全篇没有任何可执行逻辑。它也碰到了这里唯一真正会出问题的下游耦合:当生成的 VS Code schema 与 schema 源文件漂移时,ci.yml:1201 会让构建失败。两侧都已更新且逐字节一致,而且我在步骤级别确认了漂移门禁与 i18n 检查在本 PR 的 CI 中确实运行了,而不是被 full profile 条件跳过——两者都是绿的。所以这个耦合是被满足了,而不只是被避开了。

需要维护者定夺的部分,因为我定不了

对 diff 的结论:approve。对合并的结论:被一件本 PR 没有造成的事情阻塞。

Test (ubuntu-latest, Node 22.x) 变红——src/serve/workspace-skills-status.test.ts 里 13 个测试,期望 name: 'active-skill',实际得到 name: 'active:active-skill'。一个无关 PR(fix/issue-10887-tool-error-l,run 34942912668)在同一个文件上以相同计数失败,而本 diff 完全没有触及 Skill、serve 或扩展相关代码——它根本没有任何可执行变更。完整证据在 Stage 2。所以这是扩展 Skill 命名路径上的既有破损,作者无法从这里修好它,除非去接别人的活。

这在机制上很关键:下面这个 marker 请求 finalize 任务在本 commit 的所有检查变绿后批准,而单元测试变红意味着它会选择不予批准并标记状态,而不是批准。我这次刻意没有批准——CI 也仍未跑完(web-shell E2E Smoke 在运行,还有 1 个 pull_request run 未结束),我不会为一个尚不存在的结果背书。我同样不会 request changes,因为这个 diff 里没有需要改的东西。

所以这里需要人来定夺,而且只有两条合理出路:把 workspace-skills-status 的期望值修复落到 main,然后在这里重跑 CI;或者依据"既有失败"的证据手动批准这个 commit。不该发生的是这个 PR 一直红着,直到作者自己猜出这个失败与他无关。

Qwen Code · qwen3.8-max-2026-09-02

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

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline.

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

  • T1-1 loopDetectionService.ts:148-151 stale cap comment — already reported (comment 5677033129)
  • T1-2 web-shell types.ts:371 stale iterations comment — already reported (comment 5677033129)

Not explored to full depth (tool budget reached): "agent 1a": I did not run the web-shell or cli unit suites; my verification of the touched files was static (Prettier, node --check , and cross-reading the implementations….

中文说明

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

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

未探索到全部深度(达到工具调用预算):"agent 1a"I did not run the web-shell or cli unit suites; my verification of the touched files was static (Prettier, node --check , and cross-reading the implementations…

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

Comment thread docs/users/configuration/settings.md
Comment thread docs/users/configuration/settings.md
Comment thread packages/web-shell/client/daemon/workspace/types.ts
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Historical-head review — head moved to 5350335e18b5086f5a153fcff93645503b470d62 while this review was in flight (past the salvage threshold), so the run finished and posted against the head it reviewed: dfd98a6ba84ca1cd850c5536108c8da967476d0e (#10110). The next automatic review covers the delta from that anchor. Full log in the workflow run.

中文说明

历史 head 评审 —— 本次评审进行中 head 移动到了 5350335e18b5086f5a153fcff93645503b470d62(已过 salvage 阈值),因此评审跑完并针对其实际评审的 head dfd98a6ba84ca1cd850c5536108c8da967476d0e 发布(#10110)。下一次自动评审将从该锚点起评审增量。完整日志见 workflow 运行

@github-actions github-actions Bot removed the review/self-reported The linked issue was opened by the PR author (self-reported) label Sep 15, 2026

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No blocking findings. Pure documentation/comment cleanup following the Goal Stop-hook removal in #11900. No runtime behavior changes; the four edited TypeScript files produce identical JavaScript with comments removed.

Reviewed with AI assistance.

@qqqys
qqqys enabled auto-merge September 15, 2026 12:55

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM at 2461042 — no blocking issues. Docs-only retirement of the Stop-hook era prose following #11900's implementation removal; the bot's three threads were all Suggestion-level (two scope observations, one accuracy note), and the head commit addresses the accuracy gap directly. CI green.

@qqqys
qqqys added this pull request to the merge queue Sep 15, 2026
Merged via the queue into QwenLM:main with commit 06d77ad Sep 15, 2026
113 of 114 checks passed

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE

核对基线:head 24610428b89e9777b523defaee16f8211d882b38(base 12b8cbc4,15 个文件 +20/-73)。该 head 上 22 项非跳过检查全部 success、无 pending、无失败(Test (ubuntu-latest, Node 22.x)Lint & StaticIntegration Tests (no-AK, No Sandbox)web-shell E2E SmokeTUI parity snapshotsOpenTUI no-flicker gateReal daemon E2E / Java 11Capture web-shell visuals、Java/Desktop 各档)。3 条线程全部关闭、全是 [Suggestion]、无 Critical。说明:本 PR 已于 2026-09-15T12:56:11Z 由作者本人合入,本票是补记,没有参与放行。

逐条按本 head 重判历史项:R1-1 说正文里的 Closes #10795 会把仍有活的追踪 issue 顺带关掉 —— 现已改为 Part of #10795,且 #10795 在本 head 时刻仍为 open,落实。R1-3 说 packages/acp-bridge/src/status.ts 改写后,同一条数据路径的姊妹文档 DaemonGoal 头注释还停留在「只活在子进程内存里」,两边互相打脸 —— 本 head 的 packages/web-shell/client/daemon/workspace/types.ts:360-363 已改成「读自持久化的 Goal 运行时,含 paused/blocked,不含未加载会话」,与 status.ts 一致,两个字段注释也一并跟上(iterations 改成「Canonical Goal turns completed so far」、lastReason 改成「Why the Goal last stopped, or the verifier's most recent reason」)。

独立扫描未发现 Critical:四个「代码」文件的改动全是注释或文案 —— use-llm-stream.tsuseMessageQueue.ts 各 +2 行注释、零逻辑改动;loopDetectionService.ts 是三条注释重写,我核了它的新说法:client.ts:4574 确实在 StopHookLoop 分支里 loopDetector.reset(prompt_id)client.ts:3272startsInteraction 处重置(运行时安排的 Goal turn 走这条),紧跟其后还有 takePendingGoalEvents(),所以「两类续跑各自开新预算」站得住。model.maxToolCallsPerTurn 的同一句话在三处同步改了(docs/users/configuration/settings.mdsettingsSchema.tspackages/vscode-ide-companion/schemas/settings.schema.json),没有留下互相矛盾的副本;hooks.md 里去掉的 /goal 注册来源与 #11900 删掉实现一致。删掉的 4 份语言包里的 'Set a goal — keep working until the condition is met' 在本 head 全仓引用数为 0(base 上也只有语言包自己,没有生产者),所以是清孤儿键,不会让任何提示退化成裸键名。

记录(不阻塞):R1-2 我核过仍然成立 —— 随 docs/design/goal-loop-input-control.md 一起删掉的,不只是 Stop-hook 那段:它的 Design 条目记录的「活跃 turn 期间队列只额外排空 /goal、clear 只生效不再喂模型、多个 Goal 命令只发最后一个指令」这套行为在本 head 依然是活代码(GoalQueueControlMode 三种模式与 popNextSubmission 仍在 useMessageQueue.ts,本 PR 只给那里加了注释),而 docs/ 下已无任何文件提到这两个名字,树里对这块的唯一定档就没了。建议后续单独补一份只写现存行为的设计说明(或把该段挪进 Goal v3 的设计文档),不影响本次合入。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants