Skip to content

feat(cli): expose reasoning effort through ACP - #8526

Merged
wenshao merged 8 commits into
QwenLM:mainfrom
zjunothing:fix/issue-8514-acp-reasoning-effort
Aug 11, 2026
Merged

feat(cli): expose reasoning effort through ACP#8526
wenshao merged 8 commits into
QwenLM:mainfrom
zjunothing:fix/issue-8514-acp-reasoning-effort

Conversation

@zjunothing

Copy link
Copy Markdown
Collaborator

What this PR does

This adds a standard ACP session selector for reasoning effort. ACP clients now receive a thought_level option with Default, Low, Medium, High, Extra high, and Max choices, and can apply or clear the requested tier through session/set_config_option.

The selector reuses Qwen Code's existing reasoning-effort ladder and runtime setter, so provider-specific mapping and clamping remain unchanged. Invalid values are rejected at the protocol boundary. The JetBrains integration guide now documents the new control.

Why it's needed

Qwen Code already exposes reasoning effort in the terminal, but ACP hosts such as JetBrains could only configure mode and model. Users therefore could not control thinking effort from the host's native agent controls even though the runtime already supported all five tiers.

Reviewer Test Plan

How to verify

Start Qwen Code as an ACP agent and create a session. Confirm that the returned configuration options include reasoning_effort in the standard thought_level category with Default plus all five tiers. Set the option to Extra high and confirm the returned current value becomes xhigh; set it back to Default and confirm it becomes default. An unsupported value such as ultra should return an invalid-params error without changing the current setting.

Evidence (Before & After)

Before: ACP session responses exposed only mode and model; reasoning_effort was absent and the setter rejected that config id.

After: a real bundled qwen --acp process advertises all six choices, preserves the option after a model switch, applies xhigh, and clears it back to default.

Tested on

OS Status
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

Environment (optional)

macOS arm64, Node.js 22.23.2. Verified with the production bundle over a real ACP stdio subprocess; no model request or external network call was required.

Risk & Scope

  • Main risk or tradeoff: ACP clients receive one additional standard session option; the selected value is session-scoped, matching the existing ACP mode and model selectors.
  • Not validated / out of scope: live rendering inside JetBrains, and provider-specific inference behavior, which continues through the existing mapping and clamping path.
  • Breaking changes / migration notes: None. Existing clients that ignore unknown configuration options continue to work.

Linked Issues

Closes #8514

中文说明

此 PR 做了什么

此 PR 为 reasoning effort 增加标准 ACP 会话选择器。ACP 客户端现在会收到一个 thought_level 选项,其中包含 Default、Low、Medium、High、Extra high 和 Max,并可通过 session/set_config_option 应用或清除所请求的档位。

该选择器复用 Qwen Code 现有的 reasoning-effort 档位列表与运行时 setter,因此各 provider 原有的映射和钳制行为保持不变。非法值会在协议边界被拒绝。JetBrains 集成文档也补充了这一控制项。

为什么需要

Qwen Code 已经在终端中暴露 reasoning effort,但 JetBrains 等 ACP 宿主此前只能配置 mode 和 model。虽然运行时已经支持全部五档,用户仍无法通过宿主原生的 agent 控件调整思考强度。

Reviewer 测试计划

如何验证

以 ACP agent 启动 Qwen Code 并创建会话。确认返回的配置选项包含标准 thought_level 类别下的 reasoning_effort,并提供 Default 与全部五个档位。将其设置为 Extra high,确认返回的当前值变为 xhigh;再设置回 Default,确认变为 default。传入 ultra 等不支持的值时,应返回 invalid-params 错误且不改变当前设置。

证据(修改前与修改后)

修改前:ACP 会话响应只暴露 mode 和 model;reasoning_effort 不存在,setter 会拒绝该 config id。

修改后:真实打包的 qwen --acp 进程会广播全部六个选项,在模型切换后继续保留该选项,能够应用 xhigh,并能清除回 default

测试平台

OS 状态
🍏 macOS
🪟 Windows N/A
🐧 Linux N/A

环境(可选)

macOS arm64,Node.js 22.23.2。通过生产 bundle 和真实 ACP stdio 子进程完成验证;无需发送模型请求,也无需外部网络调用。

风险与范围

  • 主要风险或权衡:ACP 客户端会收到一个新增的标准会话选项;选择值仅作用于当前会话,与现有 ACP mode 和 model 选择器的语义一致。
  • 未验证 / 范围外:JetBrains 内的实际渲染,以及各 provider 的真实推理行为;后者继续复用现有映射与钳制路径。
  • 破坏性变更 / 迁移说明:无。忽略未知配置选项的既有客户端可继续正常工作。

关联 Issue

Closes #8514

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Qwen Triage finishedview run. See the stage comments in this thread for the result.

Qwen Triage 已完成 —— 查看运行。结果见本线程中的各阶段评论。

@zjunothing

zjunothing commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator Author

Local verification report

Verified commits: 87570d58eecbdb620f50fae610f5111ba55e089e, review follow-up 0273f82cbd04e752048d488163449d3a39b70984

Failure-first regression

Before the original implementation, the focused ACP regression failed because session/new returned no reasoning_effort option. During review follow-up, the branch also reproduced the reported worktree-suite regression exactly: 3/3 tests failed because its core mock did not export REASONING_EFFORT_TIERS.

The follow-up additionally covers all five review findings:

  • HTTP ACP now advertises only the session options that transport can route (model and mode); stdio ACP continues to expose and set reasoning_effort.
  • The worktree test mock exports the new tier constant.
  • A disabled-thinking no-op is detected by reading the value back and returns Invalid params.
  • Every reasoning tier carries a provider-mapping description.
  • The real stdio ACP process rejects an invalid ultra tier with JSON-RPC code -32602.

Real production-process verification

Built the production bundle and ran the repository's ACP integration harness against a real node dist/cli.js --acp --no-chat-recording subprocess. The test performs the JSON-RPC handshake, creates a real session, applies and resets reasoning effort, and verifies the invalid-tier wire response. It does not mock the agent or call a model endpoint.

OPENAI_API_KEY=test-key QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli/acp-integration.test.ts -t "supports session/set_config_option for mode, model, and reasoning effort"
PASS 1/1 (10 skipped)

Regression and static validation

ACP agent + worktree + HTTP transport tests   PASS (652/652)
Real bundled stdio ACP integration            PASS (1/1; 10 skipped)
Full repository build                         PASS
Production bundle                             PASS
CLI TypeScript typecheck                      PASS
Changed-file ESLint                           PASS
Changed-file Prettier                         PASS
git diff --check                              PASS

Environment: macOS arm64, Node.js 22.23.2. No live model request or external model API call was made. A screenshot is not applicable to this protocol-only change; the HTTP wire payload and real stdio subprocess were tested directly.

中文验证报告

本地验证报告

验证提交:87570d58eecbdb620f50fae610f5111ba55e089e;审查跟进提交:0273f82cbd04e752048d488163449d3a39b70984

失败优先回归

原始实现之前,聚焦 ACP 回归测试会失败,因为 session/new 没有返回 reasoning_effort。本次审查跟进也精确复现了反馈中的 worktree 测试回归:由于 core mock 未导出 REASONING_EFFORT_TIERS,3/3 测试全部失败。

跟进修复覆盖了全部 5 条审查意见:

  • HTTP ACP 只广播该传输层真正能路由的 modelmode;stdio ACP 仍可广播和设置 reasoning_effort
  • worktree 测试 mock 补充了新的 tier 常量。
  • thinking 被禁用而导致 setter 无操作时,通过回读检测并返回 Invalid params
  • 5 个 reasoning tier 都包含 provider 映射说明。
  • 真实 stdio ACP 进程收到非法 ultra tier 时返回 JSON-RPC -32602

真实生产进程验证

先构建生产 bundle,再通过仓库 ACP 集成框架启动真实的 node dist/cli.js --acp --no-chat-recording 子进程。测试完成 JSON-RPC 握手、创建真实会话、设置和重置 reasoning effort,并验证非法 tier 的线协议错误;没有 mock agent,也没有调用模型端点。

OPENAI_API_KEY=test-key QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli/acp-integration.test.ts -t "supports session/set_config_option for mode, model, and reasoning effort"
通过 1/1(跳过 10)

回归与静态验证

ACP agent + worktree + HTTP transport 测试   通过(652/652)
真实 bundle 的 stdio ACP 集成测试            通过(1/1;跳过 10)
全仓构建                                      通过
生产 bundle                                   通过
CLI TypeScript 类型检查                       通过
变更文件 ESLint                               通过
变更文件 Prettier                             通过
git diff --check                              通过

环境:macOS arm64,Node.js 22.23.2。未发送真实模型请求,也未调用外部模型 API。本变更仅涉及协议行为,不适合提供 UI 截图;已直接验证 HTTP wire payload 和真实 stdio 子进程。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Re-run after two more autofix commits (54163b7b, 6c4a28e5) landed on top of the previously reviewed head; full gate re-evaluated on the current diff.

Template ✓ — all required sections present, bilingual body complete.

Problem: real, user-reported gap. Linked issue #8514 asks for the five reasoning-effort tiers to be reachable from ACP hosts (JetBrains), which today can only configure mode and model. Not theoretical — the runtime already supports all tiers, the ACP surface just never exposed them.

Direction: aligned. The terminal already exposes the full ladder via /effort; this is ACP parity for the same runtime capability, session-scoped like the existing mode/model selectors. claude-code's CHANGELOG treats reasoning-effort controls as an active area (effort xhigh/max vocabulary, effort carried on status payloads), supporting the direction.

Size: core paths are touched (packages/core/src/core/reasoning-effort.ts, cross-package core+cli) — 138 production lines, 315 test lines, 1 docs line. Well under every escalation threshold.

Approach: minimal and honest. One new reasoning_effort config option on the stdio agent reusing the existing tier ladder and runtime setter; one small core helper that collapses four duplicated set→read-back sites instead of adding a fifth; and the HTTP transport deliberately does not advertise the option because it cannot route the set — it filters to routable ids and fails loud with the supported list. No unrelated changes or drive-by edits in the diff.

Risk: Stage 1e matches the acp-integration paths (revert-correlated in this repo's history) — so no Stage 2 enrichment is skipped and CI evidence is required before approval. Flagged for reviewer focus: option advertisement vs. routability in acpAgent.ts and dispatch.ts.

Moving on to code review. 🔍

中文说明

在之前审查的 head 之上又落了两个 autofix 提交(54163b7b6c4a28e5)后的重跑;已对当前 diff 重新做完整门禁评估。

模板 ✓ —— 必填章节齐全,中英双语正文完整。

问题:真实、用户上报的缺口。关联 issue #8514 要求 ACP 宿主(JetBrains)能触达五档 reasoning effort,而目前宿主只能配置 mode 和 model。不是理论问题——运行时早已支持全部档位,只是 ACP 面从未暴露。

方向:对齐。终端已通过 /effort 暴露完整档位列表;这是同一运行时能力的 ACP 对等,且与现有 mode/model 选择器一样仅作用于会话。claude-code 的 CHANGELOG 显示 reasoning effort 控制是活跃方向(xhigh/max 档位词汇、状态上报中携带 effort),支持该方向。

规模:触及核心路径(packages/core/src/core/reasoning-effort.ts,跨 core+cli 包)——生产代码 138 行、测试 315 行、文档 1 行,远低于任何升级阈值。

方案:最小且诚实。stdio agent 新增一个复用现有档位列表与运行时 setter 的 reasoning_effort 配置项;一个小的 core 帮助函数把四处重复的 set→read-back 收敛为一处,而不是新增第五处;HTTP 传输层因无法路由该设置而刻意不广播该选项——过滤为可路由 id,并以支持列表明确报错。diff 中无无关改动或顺手修改。

风险:Stage 1e 命中 acp-integration 路径(与本仓库回滚历史相关)——因此不跳过任何 Stage 2 增强项,且批准前必须有 CI 证据。提醒审查者重点关注 acpAgent.tsdispatch.ts 中选项广播与可路由性的一致性。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

Independent proposal first (written before reading the diff): add a reasoning_effort selector under the standard thought_level category to the stdio ACP agent, validate values against the existing core tier ladder, route through the same runtime setter /effort uses, reject unknown values at the protocol boundary, and either mirror that in the HTTP transport or honestly gate it there. The PR matches this — and improves on it in two places:

  • applyReasoningEffort (core) — the set→read-back "did the config actually accept it" pattern existed four times (slash command, dialog hook, control plane ×2). This PR extracts one core helper and migrates all four sites; I verified against base that reporting semantics are preserved at each. My proposal would have repeated the pattern a fifth time — this is strictly better, and no hand-rolled read-back site remains after the migration.
  • HTTP transport honestyserve/acp-http cannot route an effort set to the child, so instead of advertising an option it cannot honor (the exact mismatch an earlier review round caught), it now filters configOptions to the routable ids (model, mode) via one shared constant and returns a clear "ConfigId not supported by this transport (supported: model, mode)" error. Raw-state surfaces intentionally keep the full set, and the doc comment draws that line precisely.

Verified against base code: Config.getReasoningEffort / setReasoningEffort exist as used, the new symbols flow through the core barrel export, default clears the override via setReasoningEffort(undefined), and the thinking-disabled path fails closed with a clear invalid-params error instead of reporting success.

No correctness, security, or regression findings. Non-blocking nits, carried from the prior round and unchanged: config.getReasoningEffort?.() uses optional chaining on a required method (cosmetic), and the stdio end-to-end flow is pinned only by the merge-queue integration test (skipped in PR CI by design — merge_group gate).

Files changed (13)
File What changed
packages/core/src/core/reasoning-effort.ts New applyReasoningEffort helper: set plus read-back, reports whether the tier landed
packages/core/src/core/reasoning-effort.test.ts Tests for the helper, including thinking-disabled and clear-override cases
packages/cli/src/acp-integration/acpAgent.ts Advertises the reasoning_effort option and routes set_config_option for it
packages/cli/src/acp-integration/acpAgent.test.ts New unit test covering option shape, apply, reset, and both rejection paths
packages/cli/src/acp-integration/acpAgent.worktree.test.ts Adds the new core exports to this harness module mock
packages/cli/src/serve/acp-http/dispatch.ts Filters advertised options to routable ids and improves the unsupported-id error
packages/cli/src/serve/acp-http/transport.test.ts Wire tests for filtered advertisement and unroutable-id rejection
packages/cli/src/nonInteractive/control/controllers/systemController.ts Migrates two set-then-read-back sites to the shared helper
packages/cli/src/ui/commands/effort-command.ts The /effort slash command uses the shared helper
packages/cli/src/ui/hooks/use-effort-command.ts The effort dialog uses the shared helper
packages/cli/src/ui/hooks/use-effort-command.test.ts Adds the read-back getter to the mocked config
integration-tests/cli/acp-integration.test.ts Extends the stdio end-to-end test with set, reset, persistence, and invalid-value cases
docs/users/integration-jetbrains.md Documents the new session option in the JetBrains guide

Test evidence — the PR's own CI on 6c4a28e5

Fetched via the checks API for the reviewed commit; nothing was re-run here. All 20 reporting checks are green — including the unit suite (Test (ubuntu-latest, Node 22.x)), Serve A/B, both Desktop Shell builds, web-shell E2E Smoke, the real-daemon E2E, and the full SDK Java matrix. The 55 skipped checks are the merge-queue-gated jobs (below) plus bot-orchestration jobs that don't apply to this event. No failures, nothing pending.

Check Conclusion
Test (ubuntu-latest, Node 22.x) success
Serve A/B (ubuntu-latest, Node 22.x) success
Desktop Shell (ubuntu-22.04) success
Desktop Shell (windows-2022) success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) success
Real daemon E2E / Java 11 success
ubuntu-latest / Java 11, 17, 21 · macos-latest / Java 21 · windows-latest / Java 21 (SDK Java) success
Integration Tests (CLI, No Sandbox) skipped — merge-queue-only
Test (macos-latest / windows-latest, Node 22.x) skipped — merge-queue-gated

Sandboxed verification, if a maintainer wants live-subprocess A/B proof before merge: @qwen-code /verify — the stdio end-to-end path (option advertised → set xhigh → read-back → clear → invalid value rejected) runs only in the merge queue, and the author's live evidence is macOS-only. That said, maintainer @wenshao has already independently deep-verified the live behavior and approved this head, which substantially narrows the gap this lane would close.

Not verified here: real rendering inside JetBrains (explicitly out of scope for the PR), and provider-specific inference behavior per tier (unchanged mapping/clamping path).

中文说明

代码审查:先独立给出方案再看 diff——我的方案是给 stdio ACP agent 增加标准 thought_level 类别下的 reasoning_effort 选项,值走现有核心档位列表校验,复用 /effort 的运行时 setter,非法值在协议边界拒绝,HTTP 侧要么镜像要么诚实门禁。PR 与此一致,且两处更好:一是新增 core 帮助函数 applyReasoningEffort,把四处重复的 set→read-back 收敛为一处并全部迁移(逐一对比基础代码确认上报语义不变,迁移后无遗留手写 read-back);二是 HTTP 传输层无法路由该设置,便不再广播无法兑现的选项(正是早前评审发现的不一致),改为按共享常量过滤为可路由 id,并给出明确的支持列表报错。另核实:default 通过 setReasoningEffort(undefined) 清除覆盖;thinking 关闭时以明确的 invalid-params 错误失败关闭而非假装成功。无正确性/安全/回归问题。非阻塞小问题(沿袭上一轮):对必选方法使用了可选链 ?.()(装饰性);stdio 端到端流程仅由合并队列集成测试固定(PR CI 按设计跳过)。测试证据:受审提交 6c4a28e5 的 CI 全绿(20 项通过,含单测套件、Serve A/B、Desktop Shell、web-shell E2E、真实 daemon E2E、SDK Java 全矩阵),55 项跳过为合并队列门禁与机器人编排任务;无失败、无进行中。若维护者合并前想要真实子进程 A/B 证据,可触发 @qwen-code /verify;不过 @wenshao 已对该 head 完成独立深度验证并批准。未验证:JetBrains 内真实渲染(PR 明示范围外)与各 provider 分档推理行为(映射/钳制路径未变)。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean, well-tested implementation of a real user-facing gap; the withheld point is a cosmetic ?.() nit and the stdio end-to-end flow being pinned by the merge-queue integration test rather than PR CI.

This re-run found the head unchanged (6c4a28e5) and re-verified the gate and the code review from scratch — every earlier finding still stands. One bookkeeping correction: the previous Stage 3 comment attested an older head (c8067c42), and the approval on the current head never landed (an interrupted run left only a pending review draft). This comment supersedes it, and the approval is pinned to the reviewed commit below.

Stepping back: the problem is real — issue #8514 is a concrete user report (JetBrains ACP users can configure mode and model but cannot reach the reasoning-effort tiers the runtime already supports), and the change is still the smallest thing that works: one reasoning_effort option under the standard thought_level category, one handler case validating against the existing core tier ladder, reuse of the same runtime setter /effort uses, and an HTTP transport that honestly declines to advertise what it cannot route. The set→read-back pattern that was duplicated in four places now lives in one core helper, and I re-checked each migrated call site against base — reporting semantics are preserved everywhere.

The evidence is stronger than on any earlier pass. PR CI is fully green on this commit — 20 successful checks including the unit suite (Test (ubuntu-latest, Node 22.x)), Serve A/B, both Desktop Shell builds, web-shell E2E Smoke, the real-daemon E2E, and the full SDK Java matrix; nothing pending — and a maintainer independently deep-verified this exact head with a mock-free A/B wire harness: 744 scripted assertions passed, the live stdio scenario flips cleanly against the base bundle (12/12 vs 8/8 control), and a six-mutation matrix killed all six, so the new tests are load-bearing and the previously merge-queue-only live-behaviour gap is closed by independent evidence.

Non-blocking reservations, unchanged: config.getReasoningEffort?.() uses optional chaining on a required method (cosmetic), and the extended stdio integration test runs only in the merge queue by design. Real in-IDE rendering in JetBrains remains out of scope, as the author stated.

Approving, pinned to the reviewed commit. For transparency: this becomes this account's latest review state and supersedes the changes-requested review the /review workflow left on an earlier head before the autofix round addressed its findings.

中文说明

置信度:4/5 —— 干净、测试充分的实现,解决真实用户缺口;保留的一分给装饰性的 ?.() 小问题,以及 stdio 端到端流程仅由合并队列集成测试固定、PR CI 未覆盖这一点。

本次重跑时 head 未变(6c4a28e5),我从头重新验证了门禁与代码审查——此前所有结论依然成立。订正一处记录:先前的 Stage 3 评论认证的是旧 head(c8067c42),而对当前 head 的批准从未落地(一次中断的运行只留下了 pending 评审草稿)。本评论取代它,并在下方把批准固定到受审提交。

退一步看:问题真实——issue #8514 是具体的用户报告(JetBrains ACP 用户能配置 mode 和 model,却触达不了运行时早已支持的 reasoning effort 档位),方案仍是可行的最小实现:标准 thought_level 类别下的一个 reasoning_effort 选项、一个对照现有核心档位列表校验的 handler case、复用 /effort 使用的同一运行时 setter,HTTP 传输则诚实地不广播其无法路由的设置。四处重复的 set→read-back 模式收敛进一个 core 帮助函数,并逐一对照基础代码复查了每个迁移点——上报语义全部保持不变。

证据比之前任何一轮都强。PR CI 在该提交上全绿——20 项检查通过,含单测套件(Test (ubuntu-latest, Node 22.x))、Serve A/B、两个 Desktop Shell 构建、web-shell E2E Smoke、真实 daemon E2E 与完整 SDK Java 矩阵;无进行中——且维护者已用无 mock 的 A/B wire 测试台对同一 head 完成独立深度验证:744 项脚本断言全部通过,真实 stdio 场景相对 base 产物干净翻转(12/12 对 8/8 控制),六项单点突变全部击杀——新增测试均为承重,且此前"仅由合并队列固定"的实时行为缺口已由独立证据闭合。

非阻塞保留项(不变):config.getReasoningEffort?.() 对必选方法使用可选链(装饰性);扩展后的 stdio 集成测试按设计只在合并队列运行。JetBrains IDE 内的真实渲染仍如作者所述在范围外。

批准将固定到受审提交。透明起见:这将成为本账号的最新评审状态,并取代 /review 工作流在 autofix 轮次处理其发现之前于更早 head 上留下的 changes-requested 评审。

Qwen Code · qwen3.8-max

Reviewed at 6c4a28e556fab7af9a67d3ee0da134d209ac69ee · 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.

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)

Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/acp-integration/acpAgent.test.ts
Comment thread packages/cli/src/acp-integration/acpAgent.ts Outdated
Comment thread packages/cli/src/acp-integration/acpAgent.ts
Comment thread packages/cli/src/acp-integration/acpAgent.test.ts Outdated
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

Built the PR base vs this PR head 6c4a28e, drove a fixed endpoint set against each, and diffed the JSON responses. Only fields that changed are shown.

No response changes against the PR base across 4 scenario(s).

Qwen Code · serve A/B

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Qwen Code review timed out. Qwen review timed out after 10800 seconds (of the 180-minute budget). For large PRs, retry with a longer timeout by commenting: @qwen-code /review --timeout=360. See workflow logs.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed. Suggestions are inline. Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI at the reviewed commit; a review agent ran the suite locally and it passed, but no CI job gates it.

中文说明

已审查。 建议见行内评论。 未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI at the reviewed commit; a review agent ran the suite locally and it passed, but no CI job gates it。

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

Comment on lines +5134 to +5138
session.getConfig().setReasoningEffort(effort);
if (
effort !== undefined &&
session.getConfig().getReasoningEffort() !== effort
) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-1: This adds a fourth copy of the "set reasoning effort, then read it back to detect the silent no-op when thinking is disabled" pattern. The same set→read-back comparison now lives in four call sites across three files: this ACP branch (throws -32602), packages/cli/src/ui/commands/effort-command.ts (returns an info message), and twice in packages/cli/src/nonInteractive/control/controllers/systemController.ts (handleInitialize warns via debugLogger, handleSetEffort returns an applied boolean). — Concrete cost: any change to the no-op semantics requires four coordinated edits, and the next surface to copy one variant without the read-back will report success while the requested tier is silently discarded.

Fix (spans multiple files, so no one-click suggestion): extract one shared helper beside packages/core/src/core/reasoning-effort.ts — e.g. applyReasoningEffort(config, effort): boolean that performs the set + read-back — and have each surface wrap it with its own reporting semantics.

中文说明

此处新增了"设置 reasoning effort 后读回,以检测 thinking 被禁用时的静默 no-op"这一模式的第四份拷贝。同样的 set→读回比较现在出现在三个文件的四处调用点:本 ACP 分支(抛出 -32602)、packages/cli/src/ui/commands/effort-command.ts(返回提示信息)、以及 packages/cli/src/nonInteractive/control/controllers/systemController.ts 中的两处(handleInitialize 打警告日志、handleSetEffort 返回 applied 布尔值)。——具体代价:no-op 语义的任何变化都需要四处同步修改;下一个照抄某个变体却漏掉读回检查的调用面,会在请求的档位被静默丢弃时向调用方报告成功。

修复(跨多文件,故不提供一键 suggestion):在 packages/core/src/core/reasoning-effort.ts 旁提取一个共享辅助函数——例如 applyReasoningEffort(config, effort): boolean,执行 set + 读回——各调用面再分别包装自己的上报语义。

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

Comment on lines +998 to +1002
? co.filter(
(option) =>
isObject(option) &&
(option['id'] === 'model' || option['id'] === 'mode'),
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-2: The HTTP transport's supported config-option ids are encoded twice independently — here as a hardcoded allowlist on the read path, and in the session/set_config_option routing chain (~line 2207: if (configId === 'model') … else if (configId === 'mode') … else Unknown configId) on the write path — with no shared constant or cross-reference. The follow-up that adds daemon routing for reasoning_effort (anticipated by the "currently model and mode" comment above) must keep both in sync by hand. — Failure scenario: that follow-up adds a bridge arm to the routing chain but forgets this filter → the option becomes settable over HTTP ACP yet stays silently absent from every session/new, session/load, and set_config_option response, so no client can discover it and no test fails. Reverse drift (relaxing the filter without the handler) advertises an option that then fails sets with Unknown configId.

Fix (spans multiple locations): hoist one shared constant in AcpDispatcher (e.g. const HTTP_ACP_CONFIG_OPTION_IDS: readonly string[] = ['model', 'mode'];) consumed by both this filter predicate and the routing chain's accepted-id check.

中文说明

HTTP 传输层支持的配置项 id 现在被独立编码了两次——读路径上是这里的硬编码允许列表,写路径上是 session/set_config_option 路由链(约第 2207 行:if (configId === 'model') … else if (configId === 'mode') … else Unknown configId)——两者之间没有共享常量或交叉引用。为 reasoning_effort 新增 daemon 路由的后续 PR(上方 "currently model and mode" 注释已预告)必须手工保持两处同步。——失败场景:该后续 PR 在路由链中加入了 bridge 分支却忘了这里的过滤器 → 该选项在 HTTP ACP 上可设置,却悄悄从所有 session/newsession/loadset_config_option 响应中消失,任何客户端都无法发现它,且没有测试会失败。反向漂移(放宽过滤器而未改处理器)则会广播一个设置时返回 Unknown configId 失败的选项。

修复(跨多处):在 AcpDispatcher 中提取一个共享常量(例如 const HTTP_ACP_CONFIG_OPTION_IDS: readonly string[] = ['model', 'mode'];),让此过滤谓词与路由链的可接受 id 检查共同消费。

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

Comment on lines +1293 to +1295
expect(frame.result.configOptions.map((option) => option.id)).toEqual([
'model',
]);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-3: This new test for the configOptionsFor allow-list never exercises the 'mode' half of the predicate — the FakeBridge fixture (lines 323–343) supplies only model and reasoning_effort options. A mutation check during this review confirmed the mutant survives: deleting the option['id'] === 'mode' disjunct leaves all 291 tests in this file green, while adding a mode option to the fixture and asserting both ids fails the mutant and passes the correct code. — Failure scenario: someone drops or inverts the mode disjunct → the real HTTP ACP surface silently stops returning the approval-mode selector in session/new and set_config_option responses, regressing mode switching for JetBrains/HTTP clients with no CI signal.

Fix (two edits, so no one-click suggestion): add a mode option to the FakeBridge fixture's configOptions (e.g. { id: 'mode', name: 'Mode', category: 'mode', type: 'select', currentValue: 'default', options: [] }), then tighten this assertion to cover both entries, e.g. toEqual(['model', 'mode']).

中文说明

这个针对 configOptionsFor 允许列表的新测试从未覆盖谓词中 'mode' 的那一半——FakeBridge 夹具(第 323–343 行)只提供了 modelreasoning_effort 两个选项。本次评审中的变异检查确认该变异体可以存活:删除 option['id'] === 'mode' 这一析取项后,本文件全部 291 个测试仍然全绿;而在夹具中加入 mode 选项并断言两个 id 后,变异体失败、正确代码通过。——失败场景:有人删掉或反转 mode 析取项 → 真实 HTTP ACP 面会在 session/newset_config_option 响应中悄悄不再返回审批模式选择器,使 JetBrains/HTTP 客户端的模式切换回归,且 CI 毫无信号。

修复(两处编辑,故不提供一键 suggestion):在 FakeBridge 夹具的 configOptions 中加入 mode 选项(例如 { id: 'mode', name: 'Mode', category: 'mode', type: 'select', currentValue: 'default', options: [] }),再收紧此断言以覆盖两个条目,例如 toEqual(['model', 'mode'])

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

Comment on lines +998 to +1002
? co.filter(
(option) =>
isObject(option) &&
(option['id'] === 'model' || option['id'] === 'mode'),
)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R2-4: The model/mode allow-list applies only to the standard ACP responses via configOptionsFor; the daemon's raw-state surfaces still pass the child's unfiltered configOptions — now including reasoning_effort: GET /session/:id/context (routes/session.ts:3000–3005), the _qwen/session/context vendor method (dispatch.ts:2347–2356), and REST POST /session/:id/load/resume state. The SDK documents DaemonSessionState.configOptions as "per-session toggles the client can flip", while the daemon's session/set_config_option rejects this id with Unknown configId. — Failure scenario: a generic daemon/SDK client rendering those surfaces shows a Reasoning-effort selector; the user flips it → INVALID_PARAMS: Unknown configId: reasoning_effort for a toggle the daemon itself advertised. Clean error, no wrong state; no in-repo consumer renders these surfaces today, so impact is limited to external clients (hence Suggestion, not Critical).

Fix (spans multiple locations): apply the same allow-list wherever the daemon re-emits the child's state.configOptions (context status and restore state), or document at buildSessionContextStatus/this filter that the diagnostic endpoints intentionally carry the full child set while the setter is gated.

中文说明

model/mode 允许列表只通过 configOptionsFor 作用于标准 ACP 响应;daemon 的原始状态面仍然透传子进程未过滤的 configOptions——现在其中包含 reasoning_effortGET /session/:id/context(routes/session.ts:3000–3005)、_qwen/session/context vendor 方法(dispatch.ts:2347–2356)、以及 REST POST /session/:id/load/resume 的 state。SDK 将 DaemonSessionState.configOptions 描述为"客户端可切换的会话级开关",而 daemon 的 session/set_config_option 会以 Unknown configId 拒绝该 id。——失败场景:某个按这些面渲染会话状态的 daemon/SDK 客户端会显示 Reasoning-effort 选择器;用户切换 → 对 daemon 自己广播的开关返回 INVALID_PARAMS: Unknown configId: reasoning_effort。错误是干净的,状态不会出错;目前仓库内没有任何消费方渲染这些面,因此影响仅限于外部客户端(所以是 Suggestion 而非 Critical)。

修复(跨多处):在 daemon 重新输出子进程 state.configOptions 的所有位置(context status 与 restore state)应用同一允许列表;或在 buildSessionContextStatus/此过滤器处注明:诊断类端点有意携带完整子集,而 setter 受门控。

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

@wenshao

wenshao commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /takeover

@qwen-code-dev-bot qwen-code-dev-bot added the autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+) label Aug 7, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤝 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/takeover label (or comment @qwen-code /takeover stop) to release.

中文说明

🤝 已接管:autofix 循环现在管理此 PR —— 将持续处理新的评审反馈与 base 冲突,直到移除标签或达到轮次上限。本 PR 来自 fork,首轮处理将由下一次定时扫描执行(通常几分钟内)。移除 autofix/takeover 标签(或评论 @qwen-code /takeover stop)即可释放。

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🔀 Base updated: red check(s) [web-shell E2E Smoke (ubuntu-latest, Node 22.x)] pass on current main — merged current main via update-branch; CI will re-run.

中文说明

🔀 已更新 base:红色检查 [web-shell E2E Smoke (ubuntu-latest, Node 22.x)] 在当前 main 上通过 —— 已通过 update-branch 合入当前 main,CI 将重新运行。

@qwen-code-dev-bot

qwen-code-dev-bot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

AutoFix round 6 finishedview run. See this round's report below.

中文说明

AutoFix 第 6 轮已完成 —— 查看运行。本轮报告见下方。

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 1/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 1/100 轮)。改动内容与我反驳保留之处如下:

Review round summary — PR #8526 (round 2)

All four round-2 suggestions are addressed in commit c8067c420d. The five round-1 findings were already fixed in 0273f82cbd; each was re-verified against the current tree and still holds.

Feedback dispositions

Round 2 (new this round)

  • [Suggestion] R2-1 — fourth copy of the set→read-back pattern (rc:3723272452) — RESOLVED. Extracted one shared helper applyReasoningEffort(config, effort): boolean beside the tier ladder in core (reasoning-effort.ts): it performs the set, reads the value back, and returns whether the tier actually landed (clearing the override always reports true). All four surfaces now wrap it with their own reporting semantics: the ACP reasoning_effort setter (throws Invalid params on a no-op), /effort (info message), handleInitialize (warn log), and handleSetEffort (applied boolean). Added unit tests for the helper (applied / no-op / clear), and added the helper to both full-factory core mocks in the ACP test files.
  • [Suggestion] R2-2 — routable config-option ids encoded twice (rc:3723272456) — RESOLVED. Hoisted HTTP_ACP_CONFIG_OPTION_IDS: readonly string[] = ['model', 'mode'] in the dispatcher. Both the configOptionsFor advertisement filter and the session/set_config_option accepted-id check now consume it — the guard moved ahead of the model/mode dispatch (error ordering and the Unknown configId wire shape are unchanged), so a follow-up adding a new routable id must update one constant.
  • [Suggestion] R2-3 — allow-list test never exercises 'mode' (rc:3723272464) — RESOLVED. Added a mode option to the FakeBridge fixture's configOptions and tightened the session/new assertion to ['model', 'mode']. Dropping or inverting the mode half of the predicate now fails this test. Also added a wire test pinning the guard itself: an unroutable id (reasoning_effort) over HTTP ACP is rejected with -32602 / Unknown configId: reasoning_effort and reaches no bridge setter.
  • [Suggestion] R2-4 — raw-state surfaces pass the unfiltered child options (rc:3723272468) — RESOLVED via the documentation option the finding offered. The raw-state surfaces (GET /session/:id/context, _qwen/session/context, REST load/resume state) are full-state diagnostics and expose no setter over REST at all, so filtering them would drop state information without gating any mutation. configOptionsFor's doc now states that only the setter-paired standard ACP responses are gated to routable ids while raw-state surfaces intentionally carry the child's full set.

Round 1 (fixed in 0273f82cbd, re-verified this round)

  • [Critical] R1-1 — HTTP ACP advertised reasoning_effort but rejected sets (rc:3712099312) — still fixed: configOptionsFor filters to routable ids; stdio ACP keeps the option.
  • [Critical] R1-2 — worktree test mock missing REASONING_EFFORT_TIERS (rc:3712099319) — still fixed; the mock now also carries applyReasoningEffort.
  • [Suggestion] R1-3 — silent no-op when thinking is disabled (rc:3712099323) — still fixed, now via the shared helper's read-back.
  • [Suggestion] R1-4 — tier entries missing descriptions (rc:3712099341) — still fixed: all five tiers carry the provider-mapping description.
  • [Suggestion] R1-5 — invalid-tier wire contract not pinned (rc:3712099352) — still fixed: the real stdio ACP integration test asserts -32602 for ultra; re-run green this round against the fresh bundle.

Non-code review notes (no action possible in this PR)

  • Both review rounds noted "Integration Tests (CLI, No Sandbox)" was skipped in CI and not gated by any job. The relevant ACP integration suite was run locally this round and passes (see Verification). Which CI jobs gate which suites is a workflow-configuration decision and is left to maintainers.
  • The "Qwen Code review timed out" issue comment is informational about the review tooling; nothing to address in code.

Verification

Commands actually run this round (working tree at commit c8067c420d unless noted):

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx prettier --check on all nine changed files — passed
  • vitest run src/core/reasoning-effort.test.ts (packages/core) — 13/13 passed
  • vitest run focused (packages/cli): acpAgent.test.ts, acpAgent.worktree.test.ts, transport.test.ts, effort-command.test.ts, systemController.test.ts — 701/701 passed
  • vitest run src/serve/acp-http/transport.test.ts (packages/cli, after adding the guard test) — 292/292 passed
  • npm run bundle — passed, then focused integration against the real bundled subprocess: OPENAI_API_KEY=test-key QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli/acp-integration.test.ts -t "supports session/set_config_option for mode, model, and reasoning effort" — 1/1 passed (10 skipped by filter), no model API calls
  • Full packages/core and packages/cli suites were also run for collateral-damage screening. Every failure in them was proven environmental on this self-hosted runner, unrelated to this diff: the core path tests fail because the runner exports QWEN_HOME pointing at the workflow's temp home (they pass under env -u QWEN_HOME, 118/118 in the affected files); the CLI sandbox/config/settings failures come from the runner's SANDBOX container marker (pass once unset, e.g. config.test.ts 326/326); the remaining UI failures need a writable $HOME (root-owned here: EACCES mkdtemp /home/github-runner/...) or react to the same markers. No failing test imports any module this PR changes.
中文说明

审查轮次总结 — PR #8526(第 2 轮)

四条第 2 轮建议均已在提交 c8067c420d 中解决。五条第 1 轮发现已在 0273f82cbd 中修复;本轮逐条对照当前代码重新验证,仍然成立。

反馈处理

第 2 轮(本轮新增)

  • [建议] R2-1 — set→读回模式的第四份拷贝(rc:3723272452 — 已解决。在 core 的 tier 阶梯旁(reasoning-effort.ts)提取了共享辅助函数 applyReasoningEffort(config, effort): boolean:执行设置、回读取值并返回档位是否真正生效(清除覆盖值恒定返回 true)。四个调用面现在都包装它并保留各自的上报语义:ACP reasoning_effort setter(空操作时抛出 Invalid params)、/effort(提示信息)、handleInitialize(warn 日志)、handleSetEffortapplied 布尔值)。为辅助函数补充了单元测试(生效 / 空操作 / 清除),并在两个 ACP 测试文件的整工厂式 core mock 中补上了该导出。
  • [建议] R2-2 — 可路由配置项 id 被编码两次(rc:3723272456 — 已解决。在 dispatcher 中提取 HTTP_ACP_CONFIG_OPTION_IDS: readonly string[] = ['model', 'mode']configOptionsFor 的广播过滤器与 session/set_config_option 的可接受 id 检查现在共同消费该常量——守卫被移到 model/mode 分发之前(错误顺序与 Unknown configId 的线协议形状不变),后续新增可路由 id 只需更新这一个常量。
  • [建议] R2-3 — 允许列表测试从未覆盖 'mode'rc:3723272464 — 已解决。在 FakeBridge 夹具的 configOptions 中加入 mode 选项,并把 session/new 断言收紧为 ['model', 'mode']。删除或反转谓词中 mode 那一半的变异体现在会被该测试捕获。另新增一个线协议测试固定守卫本身:HTTP ACP 上不可路由的 id(reasoning_effort)会以 -32602 / Unknown configId: reasoning_effort 被拒绝,且不会触达任何 bridge setter。
  • [建议] R2-4 — 原始状态面透传未过滤的子进程选项(rc:3723272468 — 按该发现给出的文档选项解决。原始状态面(GET /session/:id/context_qwen/session/context、REST load/resume state)是完整状态诊断端点,且 REST 侧根本没有暴露 setter,过滤它们只会丢失状态信息而不涉及任何变更门控。configOptionsFor 的文档现在注明:只有与 setter 配对的标准 ACP 响应被门控到可路由 id,原始状态面有意携带子进程的完整集合。

第 1 轮(已在 0273f82cbd 修复,本轮重新验证)

  • [严重] R1-1 — HTTP ACP 广播 reasoning_effort 却拒绝设置(rc:3712099312 — 仍然已修复:configOptionsFor 过滤到可路由 id;stdio ACP 保留该选项。
  • [严重] R1-2 — worktree 测试 mock 缺少 REASONING_EFFORT_TIERSrc:3712099319 — 仍然已修复;mock 现在还包含 applyReasoningEffort
  • [建议] R1-3 — thinking 被禁用时的静默空操作(rc:3712099323 — 仍然已修复,现在通过共享辅助函数的回读实现。
  • [建议] R1-4 — 档位条目缺少 description(rc:3712099341 — 仍然已修复:五个档位均带有 provider 映射说明。
  • [建议] R1-5 — 非法取值的线协议契约未被固定(rc:3712099352 — 仍然已修复:真实 stdio ACP 集成测试对 ultra 断言 -32602;本轮对新 bundle 重跑通过。

非代码审查意见(本 PR 内无法处理)

  • 两轮审查都提到 "Integration Tests (CLI, No Sandbox)" 在 CI 中被跳过且没有任何 job 门控。相关 ACP 集成套件本轮已在本地运行并通过(见验证部分)。由哪些 CI job 门控哪些套件属于工作流配置决策,留给维护者决定。
  • issue 级评论 "Qwen Code review timed out" 仅关于审查工具本身的信息说明,代码层面无需处理。

验证

本轮实际执行的命令(除特别说明外均基于提交 c8067c420d 的工作树):

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • 对全部 9 个变更文件执行 npx prettier --check — 通过
  • vitest run src/core/reasoning-effort.test.ts(packages/core)— 13/13 通过
  • 聚焦 vitest run(packages/cli):acpAgent.test.tsacpAgent.worktree.test.tstransport.test.tseffort-command.test.tssystemController.test.ts — 701/701 通过
  • vitest run src/serve/acp-http/transport.test.ts(packages/cli,新增守卫测试后)— 292/292 通过
  • npm run bundle — 通过,随后对真实 bundle 子进程运行聚焦集成测试:OPENAI_API_KEY=test-key QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli/acp-integration.test.ts -t "supports session/set_config_option for mode, model, and reasoning effort" — 1/1 通过(按过滤跳过 10 个),未调用模型 API
  • 另外完整运行了 packages/corepackages/cli 套件用于排查连带影响。其中所有失败均被证实是这台自托管 runner 的环境问题,与本 diff 无关:core 的路径测试失败源于 runner 导出了指向工作流临时 home 的 QWEN_HOME(在 env -u QWEN_HOME 下通过,受影响文件 118/118);CLI 的 sandbox/config/settings 失败源于 runner 的 SANDBOX 容器标记(去除后即通过,如 config.test.ts 326/326);其余 UI 失败需要可写的 $HOME(此处为 root 所有:EACCES mkdtemp /home/github-runner/...)或受同一标记影响。没有任何失败测试导入本 PR 变更的模块。

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/模型 qwen3.8-max

@wenshao

wenshao commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@wenshao
wenshao enabled auto-merge August 8, 2026 00:17
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (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: 799 passed · 0 failed · 799 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:799 通过 · 0 失败 · 799 总计

Verification report

PR #8526 — feat(cli): expose reasoning effort through ACP

Verdict: merge-ready — 799/799 scripted assertions passed, 0 unexpected failures. Verified head: c8067c420d050b9ee9627caceeac2125e4a43db6 (merge-ref checkout dc8b35a, base 4ec0371e616decbe723cec250e19943b226d31e1).

中文摘要
  • 结论merge-ready。799 条脚本化断言全部通过,0 个意外失败。
  • A/B 结论:对真实 qwen --acp 进程的 stdio JSON-RPC 驱动证明中心声明成立。head(生产 bundle 与 dist 入口各 24/24):session/new 广播 thought_level 类别下的 reasoning_effort 选项,含 default + 五档;session/set_config_option 可逐档应用、可清除回 defaultultra 返回 -32602 Invalid params: Unknown reasoning effort: ultra 且不改变当前值;模型切换后选项与所选档位均保留。base 控制格(8/8):选项不存在,setter 以 Unsupported configId 拒绝,mode setter 正常(阳性对照)。两侧 dist 均经 realpath 校验,base 构建产物中 reasoning_effort 出现 0 次。
  • HTTP 传输qwen serve /acp 通过单一 HTTP_ACP_CONFIG_OPTION_IDS 白名单同时约束广播与路由;292/292 线级测试通过;移除过滤或移除 setter 门禁的突变均被各自测试杀死。
  • 突变矩阵:4/4 守卫被其钉住测试杀死(core 读回、thinking-disabled 抛错、HTTP 广播过滤、HTTP setter 门禁),未突变对照全绿,源码恢复校验通过。
  • 发现:无阻塞项。两条信息性观察:非字符串 value 返回 -32603(ACP SDK schema 层,A/A 对照 base/head 逐字节一致,属既有行为);HTTP 的原始状态面(context status)仍携带完整 configOptions,对其 set 会得到干净的 -32602 Unknown configId,PR 注释已声明此为有意设计。
  • 未覆盖:逐 commit 归因(浅克隆仅可达 1 个 commit);JetBrains 实际渲染与 provider 推理行为(PR 自述范围外);与当前 main 的试合并(无网络);全仓 gate。

Central claim + A/B

Central claim: an ACP stdio client receives a reasoning_effort session config option (category thought_level, six choices) and can apply/clear tiers via session/set_config_option, with invalid values rejected at the protocol boundary without state change.

Mock-free wire harness (acp-harness.mjs) drives a real spawned CLI process through initialize → authenticate → session/new → set_config_option. Cells:

cell entry oracle result
head (PR c8067c4) production bundle dist/cli.js configOptions ids, exact values/names, per-tier read-back, ultra error text + state unchanged, boundary probes, model-switch retention 24/24 (01-ab-head-cell-bundle.png)
head parity packages/cli/dist/index.js same 24/24
base control (4ec0371) base worktree packages/cli/dist/index.js option absent (['mode','model']), setter rejects with Unsupported configId, mode setter positive control 8/8 (02-ab-base-cell.png)

The flip is exactly the feature: base advertises two options and rejects the id; head advertises the third option, applies all five tiers, clears to default, and rejects ultra/XHIGH/'' with -32602 leaving state untouched. Non-string values (null, 42, object) are rejected cleanly by the ACP SDK request schema with -32603 invalid_type — byte-identical on base and head for the pre-existing mode id (A/A probe, aa-probe.mjs), i.e. pre-existing SDK behavior, not introduced by this PR.

Control purity: base worktree node_modules/@qwen-code/* realpaths asserted into the base tree before building; built base acpAgent.js/reasoning-effort.js contain 0 occurrences of reasoning_effort/applyReasoningEffort while head's contain the feature. Lockfile/package.json untouched by the PR, so shared third-party deps are a clean control.

Reviewer Test Plan walk-through (per step): step "options include thought_level with Default + five tiers" — performed, exact match; "set Extra high → xhigh" — performed; "set Default → default" — performed; "ultra → invalid-params error without changing current setting" — performed, exact message, state unchanged; "preserves the option after a model switch" — performed, option and value (xhigh) both retained.

Secondary claim: HTTP transport consistency

The qwen serve HTTP transport cannot route reasoning_effort (its bridge exposes only setSessionModel/approval-mode), and the PR gates both ends from one constant HTTP_ACP_CONFIG_OPTION_IDS = ['model','mode']: configOptionsFor filters every setter-paired response (session/new, session/load/resume, set_config_option result — all four call sites go through the single choke point), and the setter rejects other ids up front. Verified by the over-the-wire suite (transport.test.ts, real HTTP loopback, FakeBridge child advertising reasoning_effort): 292/292 (04-transport-gate.png), including the new leak assertion and the new routable-set rejection.

Secondary claim: applyReasoningEffort refactor is behavior-preserving

The new core helper (set + read-back, clear-always-true) is now the single implementation used by the ACP setter, systemController (both sites), and /effort. Real Config.setReasoningEffort no-ops on reasoning === false and the read-back source (contentGeneratorConfig) is independent of settings persistence, so the /effort reordering (read-back moved before settings.setValue) cannot observe a difference — setValue only mutates settings stores and the file. Suites: core reasoning-effort.test.ts 13/13; cli acpAgent 386 + worktree 3 + effort-command 7 + systemController 21 = 417/417; full ACP integration file 11/11 on the head bundle.

Mutation matrix (03-mutation-matrix.png)

# guard pinning suite control mutant verdict
M1 core read-back (return true) core reasoning-effort.test.ts green exit 1, expected true to be false KILLED
M2 acpAgent thinking-disabled throw acpAgent.test.ts (new test) green exit 1, promise resolved … instead of rejecting KILLED
M3 HTTP advertisement filter transport.test.ts (session/new ids) green exit 1, ['model','reasoning_effort','mode'] ≠ ['model','mode'] KILLED
M4 HTTP setter accepted-id gate transport.test.ts (routable-set test) green exit 1, got invalid mode "high" instead of Unknown configId KILLED

4/4 killed, each by its intended assertion; all files restored byte-identical (sha256-verified). No survivors.

Findings

None blocking. Informational:

  1. Non-string value-32603 Internal error (zod invalid_type from the ACP SDK request schema) rather than -32602. Pre-existing and id-agnostic (A/A identical on base mode); string violations of the new id get the PR's precise -32602. No action needed.
  2. Raw-state HTTP surfaces still carry the full configOptions (context status), so a client enumerating there and calling set_config_option over HTTP gets a clean -32602 Unknown configId with no state change. The dispatch doc comment declares this intentional (raw surfaces report session state; only setter-paired responses are gated). Coherent, but worth a line in the PR description if the author wants hosts to know.

Not covered

  • Per-commit attribution: depth-2 checkout makes only the PR head reachable (snapshot lists 5 commits); verified the aggregate HEAD^1..HEAD diff.
  • Live JetBrains rendering and provider-specific inference behavior (PR-declared out of scope; mapping/clamping path untouched by the diff).
  • Trial merge into current main (no network in this environment); base tip 4ec0371 is the merge base the workflow provided, and the PR already carries two main-merge commits from 2026-08-07.
  • Repo-wide gates; only affected-workspace suites ran (core, cli targeted files, transport, ACP integration file).
  • session/load with a previously-set effort: buildConfigOptions is shared and exercised via session/new/set paths; load-time restoration of a runtime tier was not separately driven (effort is session-scoped by design, not persisted).

Methodology

Environment: CI node:22-bookworm container, merge-ref checkout, npm ci + npm run build pre-run at head; head cells drove the production bundle and the dist entry; the base cell used a scratch worktree at HEAD^1 with a symlinked root node_modules (third-party, lockfile unchanged) plus re-pointed @qwen-code/* links (realpath-asserted) and mirrored per-package nested node_modules, rebuilt via scripts/build.js. Harnesses are plain JSON-RPC stdio clients (acp-harness.mjs, aa-probe.mjs) and the repo's own vitest suites; mutation matrix scripted in mutation-matrix.mjs. Raw logs in logs/, harnesses and captures in this directory. Assertion tally: harness 24+24+8, core 13, cli 417, transport 292, integration 11, matrix 9 (4 controls + 4 kills + 1 restore check), A/A probe 1 = 799.

Evidence images

01-ab-head-cell-bundle

02-ab-base-cell

03-mutation-matrix

04-transport-gate

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

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

LGTM, looks ready to ship. ✅

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

AutoFix round — no action required

No actionable feedback remains on this PR in this round, so no code changes were made.

What was checked

  • New reviews since the last evaluation (2026-08-05T19:04:40Z): one review from the automated reviewer — APPROVED ("LGTM, looks ready to ship. ✅", submitted 2026-08-08T00:28:52Z). It raises no findings.
  • New inline comments since the last evaluation: none. The round-2 suggestions (R2-1 … R2-4) were posted at the 2026-08-05T19:04:40Z evaluation boundary and were already addressed in the previous round (commit c8067c420d — "fix(cli): address ACP reasoning effort review round 2").
  • Issue-level comments since the last evaluation: only a maintainer /triage command and the automated reviewer's "sandboxed verification is running" status post — neither is actionable feedback.
  • Failed checks / still-red checks: none. All 28 checks on the PR are SUCCESS (15) or SKIPPED (13).
  • Base conflicts: none reported (--conflict false), and no merge of origin/main was needed.

Result

The branch stays at c8067c420d with a clean working tree. The PR is approved by the automated reviewer; it now awaits the sandboxed verification report and a maintainer merge decision.

中文说明

AutoFix 轮次 — 无需处理

本轮该 PR 上没有需要处理的反馈意见,因此未做任何代码改动。

检查内容

  • 上次评估(2026-08-05T19:04:40Z)之后的新 review: 自动评审机器人提交了一条 review —— 状态为 APPROVED("LGTM, looks ready to ship. ✅",提交于 2026-08-08T00:28:52Z),其中没有提出任何问题。
  • 上次评估之后的新行内评论: 无。第二轮建议(R2-1 … R2-4)发布于 2026-08-05T19:04:40Z 评估时间点,已在上一轮处理完毕(提交 c8067c420d — "fix(cli): address ACP reasoning effort review round 2")。
  • 上次评估之后的 issue 级评论: 仅有一条维护者的 /triage 命令和自动评审机器人的"沙箱验证运行中"状态通告 —— 两者均不是需要处理的反馈。
  • 失败的检查 / 持续失败的检查: 无。PR 上全部 28 项检查均为 SUCCESS(15 项)或 SKIPPED(13 项)。
  • 与主分支的冲突: 无(--conflict false),无需合并 origin/main

结果

分支保持在 c8067c420d,工作区干净。该 PR 已获自动评审机器人批准,接下来等待沙箱验证报告和维护者的合并决定。

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /review

@github-actions

github-actions Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor
_Qwen Code review request accepted. Review is queued in [workflow run](https://github.com/QwenLM/qwen-code/actions/runs/31243187716)._

@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 — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

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

Comment on lines +2243 to +2244
} else {
// 'mode' — the only other id in HTTP_ACP_CONFIG_OPTION_IDS.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-1: HTTP_ACP_CONFIG_OPTION_IDS has three consumers — the accepted-id check, the advertisement filter, and this routing if/else — but only the first two are keyed to it; routing silently assumes the array is exactly ['model', 'mode']. — Failure scenario: when a future change adds a third id (e.g. reasoning_effort — the constant's doc comment invites exactly this, and this PR is the proof of concept for such an addition on the stdio side), the filter and accepted-id check pick it up automatically, but routing falls into this bare else and handles the request as an approval-mode change: value: 'default' (the reasoning-effort reset sentinel) collides with ApprovalMode.DEFAULT and would silently call setSessionApprovalMode(sessionId, 'default', ...) — changing the permission mode instead of clearing reasoning effort; other tier values fail with a misleading invalid mode "high". Today's two-id array is correct; the hazard is the unguarded coupling this diff introduced.

Suggested fix — restore the exhaustive shape so routing fails loud on any id it can't route:

} else if (configId === 'mode') {
  // ... existing mode handling ...
} else {
  // unreachable unless HTTP_ACP_CONFIG_OPTION_IDS grew an id without a branch
  // reply INVALID_PARAMS `Unknown configId: ${configId}`
}
中文说明

HTTP_ACP_CONFIG_OPTION_IDS 有三个消费方——可接受 id 检查、广播过滤器、以及此处路由 if/else——但只有前两者与该常量联动;路由隐式假设数组恰好是 ['model', 'mode']。——失败场景:未来新增第三个 id(例如 reasoning_effort——常量注释正鼓励这种扩展,且本 PR 就是 stdio 侧此类扩展的实例)时,过滤器与接受检查会自动生效,但路由会落入这个裸 else 分支并按审批模式处理:value: 'default'(reasoning effort 的重置哨兵值)与 ApprovalMode.DEFAULT 冲突,将静默调用 setSessionApprovalMode(sessionId, 'default', ...)——把权限模式改掉而不是清除 reasoning effort;其他档位取值会报出误导性的 invalid mode "high"。当前两元素数组是正确的;问题在于本 diff 引入的无守卫耦合。建议恢复穷举式路由结构,使无法路由的 id 显式报错。

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

Comment on lines +995 to +998
* `session/set_config_option`. Raw-state surfaces (context status, REST
* load/resume state) intentionally carry the child's full unfiltered set —
* they report session state; only these setter-paired responses are gated
* to the ids the transport can route. Best-effort — `undefined` on error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-2: This new docstring claims "only these setter-paired responses are gated to the ids the transport can route", but the filtered configOptionsFor is also consumed by the ACP session/load / session/resume response (~line 1593, overriding loadState.configOptions) and session/fork (~line 1856) — those responses are gated too. The "unfiltered load/resume state" claim is only true of the REST/raw-state surfaces, not the ACP load/resume responses built right below this comment. — Concrete cost: a maintainer debugging why reasoning_effort is absent from an HTTP session/load response reads that load/resume intentionally carry the child's full unfiltered set, and hunts for the filter in the REST layer or the bridge instead of this method — or concludes a load-response bug is intended behavior.

Suggested change
* `session/set_config_option`. Raw-state surfaces (context status, REST
* load/resume state) intentionally carry the child's full unfiltered set —
* they report session state; only these setter-paired responses are gated
* to the ids the transport can route. Best-effort `undefined` on error.
* `session/set_config_option`. Raw-state surfaces (context status, REST
* load/resume state) intentionally carry the child's full unfiltered set —
* they report session state; only the responses built from this helper
* (`session/new`, `session/load`/`session/resume`, `session/fork`, and the
* `session/set_config_option` result) are gated to the ids the transport
* can route. Best-effort `undefined` on error.
中文说明

新增的文档注释声称"只有这些与 setter 配对的响应被门控到传输层可路由的 id",但过滤后的 configOptionsFor 同样被 ACP session/load/session/resume 响应(约 1593 行,覆盖 loadState.configOptions)与 session/fork(约 1856 行)消费——这些响应同样被门控。"load/resume 状态不过滤"的说法只对 REST/原始状态面成立,不适用于就在这段注释下方构建的 ACP load/resume 响应。注释与五个调用点中的三个行为相反,会误导排查 HTTP session/load 响应中为何没有 reasoning_effort 的维护者——他们可能去 REST 层或 bridge 里找过滤器,或误以为 load 响应的某个 bug 是有意行为。建议按实际门控面准确列举。

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

Comment on lines +2228 to +2229
RPC.INVALID_PARAMS,
`Unknown configId: ${configId}`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-3: The raw-state surfaces on this same transport (context status via _qwen/session/context, REST load/resume state) intentionally advertise reasoning_effort in configOptions (the R2-4 resolution documented this), while the setter rejects that same id with Unknown configId: reasoning_effort. "Unknown" asserts the id does not exist anywhere, which the server's own advertisement on the same transport contradicts. — Failure scenario: an HTTP ACP client renders the session's advertised config options from context status, the user picks a reasoning-effort tier, the server replies -32602 Unknown configId: reasoning_effort; the integrator concludes a client-side typo or stale schema and burns debugging time on the client before finding the deliberate transport-level gate.

Suggested change
RPC.INVALID_PARAMS,
`Unknown configId: ${configId}`,
RPC.INVALID_PARAMS,
`ConfigId not supported by this transport: ${configId} (supported: ${HTTP_ACP_CONFIG_OPTION_IDS.join(', ')})`,

Note: transport.test.ts pins the current Unknown configId text — update that expectation together with this message.

中文说明

同一传输层的原始状态面(_qwen/session/context、REST load/resume state)有意在 configOptions 中广播 reasoning_effort(R2-4 的决议已将其文档化),而 setter 却以 Unknown configId: reasoning_effort 拒绝同一 id。"Unknown" 断言该 id 在任何地方都不存在,与服务器自身在同一传输层上的广播相矛盾。——失败场景:HTTP ACP 客户端依据 context status 渲染广播的配置选项,用户选择了 reasoning effort 档位,服务器回复 -32602 Unknown configId: reasoning_effort;集成方会误以为是客户端拼写错误或 schema 过期,在客户端上空耗调试时间,最后才发现这是传输层有意设置的门控。建议改为"此传输层不支持该 configId(支持:...)"。注意:transport.test.ts 固定了当前 Unknown configId 文案,需同步更新该断言。

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

Comment on lines +5193 to +5195
throw RequestError.invalidParams(
undefined,
`Unknown reasoning effort: ${value}`,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-4: Every other effort surface in this PR enumerates the accepted values in its rejection message — effort-command.ts ("Choose one of: ..."), systemController.ts ("Supported: low, medium, high, xhigh, max"), and the HTTP mode branch ("expected one of: ..."). This stdio ACP path alone throws bare Unknown reasoning effort: ${value}. — Failure scenario: an ACP integrator (this is the protocol boundary JetBrains and other clients hit) sends 'High', 'maximum' or 'x-high' — all accepted by normalizeReasoningEffort on the slash-command and systemController surfaces — and receives a rejection with no hint of the valid set, forcing discovery from the option list or source.

Suggested change
throw RequestError.invalidParams(
undefined,
`Unknown reasoning effort: ${value}`,
throw RequestError.invalidParams(
undefined,
`Unknown reasoning effort: ${value}. Choose one of: ${ACP_REASONING_EFFORT_DEFAULT}, ${REASONING_EFFORT_TIERS.join(', ')}`,

Note: two test expectations pin the current message text and need updating with it.

中文说明

本 PR 中其他所有 effort 表面的拒绝消息都列举了合法取值——effort-command.ts("Choose one of: ...")、systemController.ts("Supported: low, medium, high, xhigh, max")、HTTP mode 分支("expected one of: ...")——唯独这里的 stdio ACP 路径只抛出裸的 Unknown reasoning effort: ${value}。——失败场景:ACP 集成方(这正是 JetBrains 等客户端接触的协议边界)发送 'High''maximum''x-high'——这些别名在斜杠命令与 systemController 表面都被 normalizeReasoningEffort 接受——却收到不带任何合法取值提示的拒绝,只能从选项列表或源码中自行发现。注意:有两处测试断言固定了当前文案,需同步更新。

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

Comment on lines +2219 to +2220
if (!HTTP_ACP_CONFIG_OPTION_IDS.includes(configId)) {
if (id !== undefined) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] R3-5: Test-efficacy probe (harness validated): a mutant replacing this notification-guard condition with true left every affected test green — no test pins the negative side of the if (id !== undefined) guard in the new Unknown configId branch. — Failure scenario: a client sends session/set_config_option as a JSON-RPC notification (no id) with an unknown configId; if a future refactor drops the guard (the exact shape the surviving mutant models), the server emits an error response with id: undefined — a JSON-RPC protocol violation a strict client can reject or misroute — and no test in this diff would fail.

Suggested fix: add one transport test that sends session/set_config_option with an unknown configId and no id, asserting no response message is written (alongside the existing unknown-configId error test).

中文说明

测试效能探针(harness 已验证)显示:把该通知守卫条件替换为 true 的变异体不会让任何受影响的测试变红——新增 Unknown configId 分支中 if (id !== undefined) 守卫的反面没有测试固定。——失败场景:客户端以 JSON-RPC 通知形式(无 id)发送未知 configIdsession/set_config_option;若未来重构丢掉该守卫(正是存活变异体的形状),服务器会发出 id: undefined 的错误响应——严格 JSON-RPC 客户端可能拒绝或误路由的协议违规——且本 diff 中没有任何测试会失败。建议补一条 transport 测试:无 id 的未知 configId 请求不产生任何响应消息。

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Addressed the latest review feedback (round 3/100). What changed, and what I pushed back on: · 已处理最新评审反馈(第 3/100 轮)。改动内容与我反驳保留之处如下:

Autofix review round — PR #8526

All five round-3 suggestions from the automated reviewer were implemented in one commit (fix(acp): harden set_config_option routing and rejection messages (#8526)). No conflicts (--conflict false, no merge performed). No feedback was declined, deferred, or escalated.

Feedback points and dispositions

  • R3-1 — unroutable-id routing hazard (rc:3740557100)Fixed. The separate accepted-id pre-check and the bare else (which assumed HTTP_ACP_CONFIG_OPTION_IDS is exactly ['model', 'mode']) were collapsed into one exhaustive if (configId === 'model') / else if (configId === 'mode') / else chain in the HTTP session/set_config_option handler. Any id the transport cannot route now fails loud with an explicit INVALID_PARAMS error instead of falling through into approval-mode handling — including the future-third-id scenario the finding described. The single error site also removed a duplicated reply block.
  • R3-2 — inaccurate configOptionsFor docstring (rc:3740557104)Fixed. Verified all four consumers (session/new, session/load / session/resume, session/fork, and the session/set_config_option result) and rewrote the docstring to name every response gated by the filter, keeping the accurate statement that raw-state surfaces (context status, REST load/resume state) carry the child's full unfiltered set.
  • R3-3 — misleading Unknown configId message (rc:3740557108)Fixed. The rejection now reads ConfigId not supported by this transport: <id> (supported: model, mode), which no longer contradicts the raw-state surfaces that deliberately advertise reasoning_effort. The pinned expectation in transport.test.ts was updated to the new text, including the supported-set hint.
  • R3-4 — bare Unknown reasoning effort rejection on stdio ACP (rc:3740557111)Fixed. The rejection in acpAgent.ts now enumerates the accepted values (Choose one of: default, low, medium, high, xhigh, max), matching every other effort surface in this PR. The two pinned expectations (unit test and integration test) were updated to the full new message.
  • R3-5 — untested negative side of the notification guard (rc:3740557112)Fixed. Added a transport test that posts session/set_config_option with an unroutable configId as a JSON-RPC notification (no id), asserts the HTTP layer acks 202, and asserts no response frame is written to the session stream — pinning exactly the mutant the probe identified.

Changed files

  • packages/cli/src/serve/acp-http/dispatch.ts — exhaustive routing chain, new rejection message, corrected docstrings (constant + configOptionsFor)
  • packages/cli/src/acp-integration/acpAgent.ts — effort rejection now enumerates accepted values
  • packages/cli/src/serve/acp-http/transport.test.ts — updated pinned message, new notification no-response test
  • packages/cli/src/acp-integration/acpAgent.test.ts — updated pinned message
  • integration-tests/cli/acp-integration.test.ts — updated pinned wire message

Verification

  • npm run build — passed
  • npm run typecheck — passed
  • npm run lint — passed
  • npx prettier --write on the five touched files — no changes needed (already conforming)
  • npx eslint on the five touched files — passed
  • npx vitest run src/serve/acp-http/transport.test.ts (packages/cli, touched) — 293 passed
  • npx vitest run src/acp-integration/acpAgent.test.ts (packages/cli, touched) — 386 passed
  • npm run bundle — passed
  • Integration: QWEN_SANDBOX=false npx vitest run cli/acp-integration.test.ts -t "set_config_option" (integration-tests, against the bundled CLI) — 2 passed (covers the exact wire message changed by R3-4 and the invalid-configId path)
中文说明

Autofix 审查轮次 — PR #8526

自动审查器第三轮的全部五条建议已在一次提交中实现(fix(acp): harden set_config_option routing and rejection messages (#8526))。无冲突(--conflict false,未执行合并)。没有拒绝、推迟或升级任何反馈。

反馈点与处理

  • R3-1 — 无法路由 id 的路由隐患(rc:3740557100)已修复。 HTTP session/set_config_option 处理器中原本独立的"可接受 id 前置检查"与裸 else(隐式假设 HTTP_ACP_CONFIG_OPTION_IDS 恰好是 ['model', 'mode'])被合并为一条穷举式 if (configId === 'model') / else if (configId === 'mode') / else 链。任何该传输层无法路由的 id 现在都会以显式 INVALID_PARAMS 错误明确失败,而不是落入审批模式处理——包括该发现所描述的"未来新增第三个 id"的场景。单一错误出口同时消除了一段重复的应答代码。
  • R3-2 — configOptionsFor 文档注释不准确(rc:3740557104)已修复。 核实了全部四个消费方(session/new、session/load / session/resume、session/fork 以及 session/set_config_option 的返回结果),并重写文档注释,列明所有经过滤门控的响应;同时保留准确表述:原始状态面(context status、REST load/resume state)承载子会话完整且未过滤的选项集。
  • R3-3 — 误导性的 Unknown configId 消息(rc:3740557108)已修复。 拒绝消息改为 ConfigId not supported by this transport: <id> (supported: model, mode),不再与有意广播 reasoning_effort 的原始状态面相互矛盾。transport.test.ts 中固定的断言已同步更新为新文案(含支持列表提示)。
  • R3-4 — stdio ACP 上裸的 Unknown reasoning effort 拒绝(rc:3740557111)已修复。 acpAgent.ts 中的拒绝消息现在列举合法取值(Choose one of: default, low, medium, high, xhigh, max),与本 PR 其他所有 effort 表面保持一致。两处固定断言(单元测试与集成测试)均已更新为完整新文案。
  • R3-5 — 通知守卫的反面缺少测试(rc:3740557112)已修复。 新增一条 transport 测试:以 JSON-RPC 通知形式(无 id)发送带无法路由 configIdsession/set_config_option,断言 HTTP 层返回 202,并断言会话流上没有写出任何响应帧——恰好固定了变异探针识别出的那个存活变异体。

变更文件

  • packages/cli/src/serve/acp-http/dispatch.ts — 穷举式路由链、新的拒绝消息、修正的文档注释(常量 + configOptionsFor
  • packages/cli/src/acp-integration/acpAgent.ts — effort 拒绝消息列举合法取值
  • packages/cli/src/serve/acp-http/transport.test.ts — 更新固定文案断言、新增"通知无响应"测试
  • packages/cli/src/acp-integration/acpAgent.test.ts — 更新固定文案断言
  • integration-tests/cli/acp-integration.test.ts — 更新固定的线上消息断言

验证

  • npm run build — 通过
  • npm run typecheck — 通过
  • npm run lint — 通过
  • 对五个变更文件执行 npx prettier --write — 无需改动(已符合格式)
  • 对五个变更文件执行 npx eslint — 通过
  • npx vitest run src/serve/acp-http/transport.test.ts(packages/cli,变更文件)— 293 通过
  • npx vitest run src/acp-integration/acpAgent.test.ts(packages/cli,变更文件)— 386 通过
  • npm run bundle — 通过
  • 集成测试:QWEN_SANDBOX=false npx vitest run cli/acp-integration.test.ts -t "set_config_option"(integration-tests,基于打包后的 CLI)— 2 通过(覆盖 R3-4 变更的确切线上消息与非法 configId 路径)

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/模型 qwen3.8-max

@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 — no blockers. Suggestions are inline.

中文说明

已审查——无阻断问题。 建议见行内评论。

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

const MCP_OAUTH_START_TIMEOUT_MS = 30_000;
const SESSION_DRAIN_TIMEOUT_MS = 30_000;
const ACP_REASONING_EFFORT_DEFAULT = 'default';
const ACP_REASONING_EFFORT_NAMES: Record<ReasoningEffort, string> = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] The new ACP_REASONING_EFFORT_NAMES map duplicates the per-tier display labels already defined for the same five tiers in the reasoningEffort enum options at packages/cli/src/config/settingsSchema.ts:1463 — and the two copies have already drifted in casing: xhigh: 'Extra high' here vs label: 'Extra High' in the schema (showInDialog: true). A third copy ("thinking.xhigh": "Extra High") lives in desktop/packages/shared/src/i18n/locales/en.json. The diff's own new test asserts the ACP spelling, so both spellings now ship. — Concrete cost: the same tier renders with different labels depending on surface (an ACP client such as JetBrains shows "Extra high" while the CLI settings dialog shows "Extra High"), and any future label edit or tier addition must be made in several independent places — missing one silently widens the divergence.

Suggested fix (spans multiple files, so no one-click suggestion): extract one shared tier→label record next to REASONING_EFFORT_TIERS in packages/core/src/core/reasoning-effort.ts and consume it from buildConfigOptions, the settings schema, and the desktop i18n copy, aligning the casing.

中文说明

新增的 ACP_REASONING_EFFORT_NAMES 映射重复定义了 packages/cli/src/config/settingsSchema.ts:1463reasoningEffort 枚举选项里同样五个档位已有的显示标签——且两份拷贝在大小写上已经出现漂移:此处为 xhigh: 'Extra high',schema 中为 label: 'Extra High'showInDialog: true)。第三份拷贝("thinking.xhigh": "Extra High")位于 desktop/packages/shared/src/i18n/locales/en.json。本 diff 新增的测试断言的是 ACP 侧拼写,因此两种拼写现在都会随产品发布。——具体代价:同一档位在不同界面显示不同标签(JetBrains 等 ACP 客户端显示 "Extra high",CLI 设置对话框显示 "Extra High");未来任何标签修改或新增档位都必须同时改多处独立代码,漏改其一会在界面之间悄悄扩大分歧。

建议修复(跨多文件,故不提供一键 suggestion):在 packages/core/src/core/reasoning-effort.tsREASONING_EFFORT_TIERS 旁提取一份共享的 档位→标签 记录,供 buildConfigOptions、settings schema 与 desktop i18n 拷贝共同消费,并统一大小写。

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

};

return [modeConfigOption, modelConfigOption];
const reasoningEffortConfigOption: SessionConfigOption = {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

[Suggestion] buildConfigOptions advertises the reasoning_effort selector unconditionally, but when thinking is disabled (ContentGeneratorConfig.reasoning === false — a user-configurable model generation field) every listed non-default value deterministically fails with -32602 'Reasoning effort cannot be applied while thinking is disabled'. — Failure scenario: a session whose active model/user config disables thinking still receives reasoning_effort in session/new configOptions with currentValue: 'default' and five selectable tiers; the ACP client (JetBrains, per the docs bullet this PR adds) renders the selector from that list, the user picks e.g. high, and each tier pick reproduces the same error, with no advertised way to re-enable thinking. Sibling mode/model options list only settable values.

Suggested fix: omit the option or mark it unavailable when config.getContentGeneratorConfig()?.reasoning === false (which is distinguishable from an unset tier), so clients only render a selector that can succeed — or keep the advertise-always behavior deliberately and document it as such.

中文说明

buildConfigOptions 无条件地广播 reasoning_effort 选择器,但当 thinking 被禁用时(ContentGeneratorConfig.reasoning === false——这是用户可配置的模型生成字段),列表中除 default 外的每个取值都必然以 -32602 'Reasoning effort cannot be applied while thinking is disabled' 失败。——失败场景:活动模型/用户配置禁用了 thinking 的会话,仍然会在 session/newconfigOptions 中收到 reasoning_effortcurrentValue: 'default' 加五个可选档位);ACP 客户端(按本 PR 新增的文档条目,即 JetBrains)会据此渲染选择器,用户选择如 high 后每次都会得到同样的错误,且没有任何广播出来的途径可以重新启用 thinking。同级的 mode/model 选项只列出可设置的取值。

建议修复:当 config.getContentGeneratorConfig()?.reasoning === false 时省略该选项或将其标记为不可用(该状态与未设置档位可区分),使客户端只渲染能够成功的选择器——或有意保留“始终广播”的行为并在文档中注明。

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

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 AutoFix ran out of time before finishing (timeout (7200000ms)) (attempt 4/100) — it will retry on the next scan.

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:
Qwen failed during address-review: timeout (7200000ms).

See the Qwen Autofix agent step logs for model/tool output.

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31261778101


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 AutoFix ran out of time before finishing (timeout (1080000ms)) (attempt 5/100) — it will retry on the next scan.

⚠️ This change was NOT pushed — any commit referenced below was made only in the runner workspace and has been discarded. What the agent reported:
Qwen failed during address-review: timeout (1080000ms).

See the Qwen Autofix agent step logs for model/tool output.

Run log: https://github.com/QwenLM/qwen-code/actions/runs/31267311990


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round: no action required (PR #8526)

This round found no actionable feedback, so no changes were made and no commit was created.

Triage summary:

  • Reviews: none newer than the last evaluation (2026-08-08T11:11:46Z).
  • Inline comments: none newer than the last evaluation.
  • Issue-level comments: none newer than the last evaluation.
  • Failed checks: none — all 42 reported CI checks are SUCCESS or SKIPPED.
  • Still-red checks: none.
  • Deferred non-Critical feedback (Critical-only mode active after 5 change-producing rounds): 3 items remain open for human follow-up — one automated review and two inline findings on packages/cli/src/acp-integration/acpAgent.ts (rc:3740833501, rc:3740833503). Per the Critical-only rules these are an audit record, not work for this round: no code changes, thread resolutions, or comment replies were made for them, and their threads stay open.

Note on the budget warning: the previous two rounds exhausted the time budget, but since this round's actionable sections are empty there was nothing to address or verify — no retry of prior work was needed or performed.

Current state: branch fix/issue-8514-acp-reasoning-effort remains at commit 6c4a28e5 (fix(acp): harden set_config_option routing and rejection messages), clean working tree, in sync with the remote head.

中文说明

Autofix 评审轮次:无需处理(PR #8526

本轮没有发现可执行的反馈,因此未做任何改动,也未创建提交。

分类摘要:

  • 评审(Reviews): 没有比上次评估时间(2026-08-08T11:11:46Z)更新的评审。
  • 行内评论(Inline comments): 没有比上次评估时间更新的行内评论。
  • Issue 级评论: 没有比上次评估时间更新的评论。
  • 失败的检查: 无 —— 报告的全部 42 项 CI 检查均为 SUCCESSSKIPPED
  • 持续失败的检查: 无。
  • 延后的非 Critical 反馈(已完成 5 个产生改动的轮次,当前处于仅处理 Critical 的模式): 有 3 个条目保持开放,留待人工跟进 —— 一条自动化评审,以及 packages/cli/src/acp-integration/acpAgent.ts 上的两条行内发现(rc:3740833501、rc:3740833503)。按照 Critical-only 规则,这些是审计记录而非本轮工作:未对其修改代码、未解决相关线程、未回复评论,对应线程保持开放。

关于预算警告: 此前两轮耗尽了时间预算,但本轮可执行区域为空,没有任何需要处理或验证的内容 —— 无需也没有重试此前的工作。

当前状态: 分支 fix/issue-8514-acp-reasoning-effort 仍位于提交 6c4a28e5(fix(acp): harden set_config_option routing and rejection messages),工作区干净,与远端 head 保持同步。

Deferred non-Critical feedback

Critical-only mode is active after 5 change-producing rounds. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

完成 5 个产生改动的轮次后进入仅处理 Critical 的模式。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

wenshao pushed a commit that referenced this pull request Aug 8, 2026
@wenshao

wenshao commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Maintainer-local deep verification round (runs on top of the CI verify job's evidence) — merge-ready.

Verdict

merge-ready — 744 scripted assertions executed, 744 passed, 0 failed (unexpected). Verified head: 6c4a28e556fab7af9a67d3ee0da134d209ac69ee. Base (control): cb485b423323e2b39330e52c1a3f62e92ae51260. No blocking findings; two informational observations at the bottom.

中文摘要
  • 结论:merge-ready — 共 744 项脚本断言全部通过(0 项非预期失败)。
  • A/B 结论:以真实打包产物 qwen --acp(stdio ACP 子进程)驱动同一套 wire 场景,head 侧 12/12 通过(reasoning_effort 以 thought_level 类别广播 Default+5 档,set_config_option 应用 xhigh、清除回 default,非法值 ultra-32602 拒绝且携带完整档位清单,模型切换后选项保留);base 侧 8/8 控制断言通过(不广播该选项,所有 set 请求以 Unsupported configId 拒绝)。行为翻转干净,证明核心改动是承重的。
  • Mutation 矩阵:对 6 处单点源码突变(helper 读回删除、选项 id 改名、case 标签改名、HTTP 传输过滤器移除、拒绝消息回退)逐一跑对应测试,6/6 全部被击杀,证明 PR 新增测试均非空转。
  • Gates:head 上 core reasoning-effort 13/13、cli 4 个文件 687/687、effort-command 7/7、systemController effort 路径 7/7、集成测试(set_config_option 真 bundle E2E)1/1、core+cli typecheck 均通过。systemController 的 2 个 get_usage_info 用例在 base 与 head 上以完全相同的方式超时(usage dashboard 环境性失败),与 PR 无关。
  • 观察(非阻断):① HTTP ACP 传输(qwen serve /acp)刻意只路由 model/mode,对 reasoning_effort 大声拒绝(已文档化+测试);JetBrains 走 --acp stdio,功能可到达。② acpAgent.test.ts 的 core mock 复刻了 applyReasoningEffort 的实现,真实 helper 的行为由 core 与 dialog 测试钉住(见 M1/M2)。
  • 未覆盖:wire 层 thinking-disabled(reasoning: false)路径未构造(需深层 provider 配置,由 core helper 测试 + agent 单测覆盖);JetBrains IDE 内实机渲染;HTTP 传输的 session/load/resume/fork 过滤行为由 transport.test.ts 293 例覆盖但未在真实 daemon 上复跑;未运行仓库级全量测试。

Central claim + A/B

Central claim: an ACP session over qwen --acp advertises a reasoning_effort session config option in the standard thought_level category (Default + Low/Medium/High/Extra high/Max), and session/set_config_option applies the tier (xhigh), clears it (default), and rejects unknown values (ultra) with -32602 while leaving the current value unchanged. The option survives a model switch.

Method: a mock-free wire harness spawns the bundled CLI as a real ACP stdio subprocess (node dist/cli.js --acp --no-chat-recording, isolated QWEN_HOME + project settings mirroring the repo's own integration-test rig) and drives newline-delimited JSON-RPC over its stdin/stdout. The identical scenario runs against the head bundle and the base bundle; the base arm encodes the expected control behavior so its "fails" count as passes. Both worktrees were built from the exact OIDs above, and node_modules/@qwen-code/* symlinks were verified to resolve inside each worktree.

# Cell (identical scenario, both arms) Head bundle (PR) Base bundle (control)
1 initialize + authenticate(openai) succeed
2 session/new config options mode, model, reasoning_effort mode, model
3 reasoning_effort category / type / initial value thought_level / select / default option absent
4 option values default, low, medium, high, xhigh, max n/a
5 option names Default … Extra high, Max n/a
6 set_config_option(reasoning_effort, xhigh) → currentValue xhigh rejected -32602 Unsupported configId: reasoning_effort
7 set_config_option(reasoning_effort, default) → currentValue default rejected (same)
8 set_config_option(reasoning_effort, ultra) -32602 "Unknown reasoning effort: ultra. Choose one of: default, low, medium, high, xhigh, max" rejected (same as 6)
9 model switch then re-apply option preserved; default round-trip works after setModel still rejected (same as 6)
Arm totals 12/12 pass 8/8 pass

Mutation matrix (vacuity)

Single-point mutations of the PR's production code, each run against its target test — 6/6 killed, 0 survivors (no vacuous new tests):

# Mutation Test that goes red
M1 applyReasoningEffort read-back removed (always true) reasoning-effort.test.ts "reports false"
M2 same M1 mutation use-effort-command.test.ts "warns in-chat when thinking is disabled"
M3 advertised option id renamed acpAgent.test.ts "exposes and applies the ACP reasoning effort selector"
M4 set_config_option case label renamed same acpAgent.test.ts test
M5 HTTP transport option filter removed transport.test.ts "session/new reply rides the connection-scoped stream" (['model','mode'])
M6 HTTP transport rejection message reverted transport.test.ts "rejects ids outside the routable set"

Targeted gates (head)

  • packages/core reasoning-effort.test.ts: 13/13
  • packages/cli acpAgent.test.ts (386) + acpAgent.worktree.test.ts (3) + transport.test.ts (293) + use-effort-command.test.ts (5): 687/687
  • packages/cli effort-command.test.ts: 7/7; systemController.test.ts effort paths: 7/7
  • Integration E2E (real bundle, integration-tests/cli/acp-integration.test.ts "supports session/set_config_option for mode, model, and reasoning effort"): 1/1
  • Typecheck tsc --noEmit: packages/core ✅, packages/cli
  • Pre-existing: systemController.test.ts get_usage_info (2 tests) time out identically on base and head (usage-dashboard fetch; untouched by this PR) — A/A control, not a regression.

Findings

No blocking findings. Informational only:

  1. HTTP ACP transport (qwen serve /acp) intentionally does not route reasoning_effort (by design — the final commit's whole point). It advertises only model/mode (new HTTP_ACP_CONFIG_OPTION_IDS filter) and rejects reasoning_effort with -32602 "ConfigId not supported by this transport: reasoning_effort (supported: model, mode)", so the advertised set cannot drift from the routable set. Consequence: clients of the HTTP surface won't see the selector; JetBrains connects via --acp stdio (integration-jetbrains.md "args": ["--acp"]), so the feature reaches it. Documented in code, covered by transport.test.ts.
  2. acpAgent.test.ts re-implements applyReasoningEffort inside its core mock (nit). The real helper's behavior is pinned by core reasoning-effort.test.ts and the /effort dialog tests (M1/M2 killed); the agent test pins the wiring. Duplicated logic to keep in sync, nothing broken.

Not covered

  • Wire-level thinking-disabled path (reasoning: false rejection): requires deep provider config; covered per-link by core helper tests + agent unit test, not end-to-end.
  • JetBrains IDE live rendering (out of scope per the PR).
  • HTTP transport session/load/resume/fork filtering on a real qwen serve daemon (covered by transport.test.ts, not re-run against a live daemon).
  • Repo-wide test suite (only affected workspaces + ACP integration file run).
  • Per-commit attribution (8 commits incl. 3 main merges; aggregate base..head diff verified).

Evidence images

01-ab-stdio-head

02-ab-stdio-base

03-mutation-matrix

04-gates-unit

05-integration

06-preexisting-failures

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round: no action taken

Round inputs (PR #8526, head 6c4a28e5):

  • Reviews: none
  • Inline comments: none
  • Issue-level comments: one — the maintainer-local deep verification report
  • Failed / still-red checks: none
  • Base-conflict flag: false

Triage result: nothing to address. The only feedback this round is the deep verification report, and its verdict is merge-ready: 744 scripted assertions executed, 744 passed, 0 failed. The verified head (6c4a28e556fab7af9a67d3ee0da134d209ac69ee) matches this branch's HEAD exactly, and all CI checks report SUCCESS or SKIPPED.

The report's two observations are explicitly informational, non-blocking, and need no code change:

  1. The HTTP ACP transport intentionally does not route reasoning_effort — that exclusion is the design point of the final commit, is documented in code, and is pinned by transport.test.ts. JetBrains connects via --acp stdio, so the feature still reaches it.
  2. acpAgent.test.ts re-implements applyReasoningEffort inside its core mock — a nit; the real helper's behavior is pinned by the core helper tests and the /effort dialog tests (mutation matrix M1/M2 both killed), while the agent test pins the wiring. Nothing is broken.

Additionally, this PR is in Critical-only mode (after 5 change-producing rounds); the deferred non-Critical feedback section for this round is empty, so no deferred items apply either.

Outcome: no code changes, no commit. Adding a commit now would only widen the diff with nothing to fix. The branch stays at 6c4a28e556 as verified.

中文说明

Autofix 审查轮次:无需处理

本轮输入(PR #8526,head 6c4a28e5):

  • Reviews:无
  • Inline comments:无
  • Issue 级评论:1 条 —— 维护者本地深度验证报告
  • 失败 / 持续失败的检查:无
  • 与 base 冲突标记:false

分诊结果:没有需要处理的内容。 本轮唯一的反馈是深度验证报告,其结论为 merge-ready:共执行 744 项脚本断言,全部通过(744 通过,0 失败)。验证的 head(6c4a28e556fab7af9a67d3ee0da134d209ac69ee)与本分支当前 HEAD 完全一致,且所有 CI 检查均为 SUCCESS 或 SKIPPED。

报告中的两条观察均为信息性、非阻断,无需任何代码改动:

  1. HTTP ACP 传输刻意不路由 reasoning_effort —— 这一排除正是最后一个 commit 的设计要点,已在代码中文档化,并由 transport.test.ts 钉住。JetBrains 通过 --acp stdio 连接,因此该功能仍可到达。
  2. acpAgent.test.ts 在其 core mock 内部重新实现了 applyReasoningEffort —— 属于 nit;真实 helper 的行为已由 core helper 测试和 /effort 对话框测试钉住(突变矩阵 M1/M2 均被击杀),agent 测试钉住的是接线。没有损坏任何东西。

此外,本 PR 已处于 Critical-only 模式(在 5 个产生改动的轮次之后);本轮的延后非 Critical 反馈部分为空,因此也没有任何延后条目需要处理。

结果:不做任何代码改动,不创建提交。此时新增提交只会扩大 diff 而没有任何可修复的内容。分支保持在已验证的 6c4a28e556

Deferred non-Critical feedback

Critical-only mode is active after 5 change-producing rounds. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

完成 5 个产生改动的轮次后进入仅处理 Critical 的模式。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ⚠️ incomplete — the run timed out with partial evidence - workflow run

The verification run did not complete, so the phases below may be partial or missing entirely. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 848 passed · 0 failed · 848 total

中文 — 判定:⚠️ 未完成 · 运行超时,证据不完整

本次验证运行未正常结束,下列内容可能不完整甚至缺失。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:848 通过 · 0 失败 · 848 总计

Verification report

PR #8526 — feat(cli): expose reasoning effort through ACP (follow-up round)

Verdict: merge-ready — 848/848 scripted assertions passed, 0 unexpected failures. Verified head: 6c4a28e556fab7af9a67d3ee0da134d209ac69ee (merge-ref checkout b10681348f, base tip 4a7951781501b95b93baf84cf5899dc2b752d80e).

中文摘要
  • 结论merge-ready。848 条脚本化断言全部通过,0 个意外失败。本轮为跟进轮:上轮验证 head 为 c8067c4,此后新增 3 个 commit(合并 main、/effort 对话框迁移到 applyReasoningEffortset_config_option 路由与拒绝消息加固),全部重新实测而非沿用上轮数据。
  • A/B 结论:中心声明再次成立。head(bundle 与 dist 入口各 31/31,见 01-ab-head-bundle.png):session/new 广播 thought_level 类别下含 default + 五档的 reasoning_effort 选项,逐档应用/清除、ultra/XHIGH/''-32602 精确消息拒绝且状态不变、模型切换后选项与档位保留。base 控制格(13/13,见 02-ab-base-cell.png):仅 ['mode','model']reasoning_effortUnsupported configId 拒绝,mode/model setter 阳性对照正常。控制纯度:base worktree 的 @qwen-code/* realpath 全部指向 base 树,base 构建产物中 reasoning_effort 出现 0 次。
  • 增量验证:加固后的 HTTP 拒绝消息 ConfigId not supported by this transport: reasoning_effort (supported: model, mode) 由 293/293 线级测试钉住;通知路径(无 id)不转发、不产生响应;session/new、load/resume、session/fork、setter 结果四个 setter-paired 响应全部经唯一 configOptionsFor 过滤点(调用点已逐一核实)。
  • 突变矩阵:9 个突变体全部被其目标断言杀死(05-mutation-matrix.png),含新增守卫:HTTP 拒绝消息、通知路径不转发、对话框与斜杠命令的 !applied 分支(正反两侧)。未突变对照全绿,源码 sha256 校验恢复。
  • 发现:无阻塞项。两条信息性观察:① 两个依赖环境模型实时推理的集成测试在本容器超时——plan-mode 在 base/head 两侧以完全相同方式失败(base tip 上的既有问题),smoke 在两侧单独运行均通过(97.9s vs 100.6s,自然时长贴近 60s 请求超时,并发负载下翻车),均非本 PR 引入(见 06-integration-aa.png);② 非字符串 value 仍返回 -32603 Internal error(ACP SDK schema 层,A/A 两侧逐字节一致,既有行为)。
  • 未覆盖:逐 commit 归因(depth-2 仅可达 1/8);JetBrains 实际渲染与 provider 推理行为(PR 自述范围外);与当前 main 的试合并(无网络);全仓 gate;session/load/session/fork 携带已设档位的端到端驱动(过滤点为单一 choke point,M3 突变经 session/new 断言杀死全部四个响应面)。

Previous-finding status (round 1 → round 2)

Round 1 verified head c8067c4 and returned merge-ready with no blocking findings. Three commits landed since: a main merge (d703074, conflict resolved in docs/users/integration-jetbrains.md), the /effort dialog migration to applyReasoningEffort (54163b7), and the set_config_option routing/rejection hardening (6c4a28e). Every carried-forward measurement below was rebuilt and re-run at the new head, not diffed against the old report.

# Round-1 finding Severity Status at 6c4a28e
1 Non-string value-32603 Internal error (ACP SDK request schema), id-agnostic, pre-existing informational stands, unchanged — re-measured via A/A probe: null/42/{a:1} on pre-existing id mode return {"code":-32603,"message":"Internal error"} byte-identical on base and head (scripted equality check passed); head's reasoning_effort id returns the same code/message class. No action needed.
2 HTTP raw-state surfaces carry the full configOptions; setting one returns a clean -32602 Unknown configId informational addressed (hardened) — the rejection is now ConfigId not supported by this transport: reasoning_effort (supported: model, mode), naming both the transport limitation and the routable set; the transport suite's routable-set test pins the exact message (M4 kills its removal). Raw-state retention remains by documented design (dispatch doc comment now enumerates the gated surfaces); the setter-side rejection is verified by the 293/293 wire suite.

Central claim + A/B

Central claim: an ACP stdio client receives a reasoning_effort session config option (category thought_level, six choices) and can apply/clear tiers via session/set_config_option, with invalid values rejected at the protocol boundary without state change.

Mock-free wire harness (acp-harness.mjs) drives a real spawned CLI process through initialize → authenticate → session/new → set_config_option…, isolated via per-run QWEN_HOME and a modelProviders openai registry entry; no inference is performed.

cell entry oracle result
head (6c4a28e) production bundle dist/cli.js option ids/category/values/names/descriptions exact, per-tier read-back (5 tiers), clear→default, ultra exact error text + state unchanged via non-mutating mode read-back, XHIGH/'' boundary, model-switch retention (option + selected tier), unknown-id rejection, non-string probes 31/31 (01-ab-head-bundle.png)
head parity packages/cli/dist/index.js same 31/31
base control (4a79517) base worktree packages/cli/dist/index.js ids exactly ['mode','model'], no thought_level, setter rejects reasoning_effort with Unsupported configId, mode/model positive controls, unknown-id same default branch 13/13 (02-ab-base-cell.png)
base control (swap check) base bundle swapped into dist/cli.js same 13 oracles (validates the A/A bundle swap) 13/13

The flip is exactly the feature: base advertises two options and rejects the id; head advertises the third, applies all five tiers, clears to default, and rejects ultra/XHIGH/'' with -32602 leaving state untouched (proven over the wire by re-setting the current mode, which returns configOptions without touching effort).

Reviewer Test Plan walk-through (re-performed per step): options include thought_level with Default + five tiers — exact match; set Extra high → xhigh; set Default → default; ultra → invalid-params error with state unchanged; option preserved after a model switch — option and value (xhigh) both retained.

Control purity: base worktree at HEAD^1 with a root node_modules symlink farm (1163 third-party entries; lockfile/package.json untouched by the PR) plus mirrored per-package nested node_modules (123 third-party pins); every @qwen-code/* link realpath-asserted into the base tree before building (scripts/build.js --cli-only). Built base acpAgent.js contains 0 occurrences of reasoning_effort and base reasoning-effort.js 0 of applyReasoningEffort; head's contain both. For the bundle-level A/A, dist/ was swapped to the base bundle and back; each swap was sha256-verified and probed with the 13-assertion harness in the expected mode.

Secondary claim: HTTP transport routing + hardened rejection (the delta)

The qwen serve HTTP transport routes only model/mode; 6c4a28e hardens both ends from the shared constant HTTP_ACP_CONFIG_OPTION_IDS = ['model','mode']:

  • configOptionsFor filters every setter-paired response. All four call sites verified structurally: session/new (dispatch.ts:1403), session/load/resume (:1594), session/fork (:1857), and the setter result (:2270) — one choke point, so a single mutation kills every gated surface.
  • The setter rejects unroutable ids with -32602 ConfigId not supported by this transport: <id> (supported: model, mode) instead of the old Unknown configId; a notification (no id) for an unroutable configId writes no response and forwards nothing to the bridge.
  • Raw-state surfaces (context status) intentionally keep the child's full unfiltered set — the doc comment says so, and the loud setter rejection is the compensating control.

Over-the-wire suite (transport.test.ts, real HTTP loopback, FakeBridge child advertising reasoning_effort): 293/293 (04-transport-gate.png) — round 1's 292 plus the new notification test; the routable-set test now pins the hardened message text.

Secondary claim: applyReasoningEffort single-implementation refactor

The core helper (set + read-back; clearing always true; returns false when Config.setReasoningEffort no-ops under reasoning: false) is the implementation used by all four surfaces: the ACP setter, systemController (both sites), the /effort slash command, and — new in this round (54163b7) — the /effort dialog (use-effort-command.ts). Behavior preservation: the read-back source (contentGeneratorConfig) is independent of settings persistence, and each surface's chat/debug message keys off the helper's boolean exactly as before. The dialog's tests exercise the real helper through a mocked Config seam (no core mock), so the mutation below bites the production code path.

Mutation matrix (05-mutation-matrix.png, details in logs/mutation-matrix.json + logs/mutation-extras.json)

# guard (mutated) pinning suite control verdict
M1 core read-back → return true core reasoning-effort.test.ts green (13/13) KILLED — expected true to be false in "reports false when setReasoningEffort no-ops"
M2 acpAgent thinking-disabled block removed (coarse) acpAgent.test.ts (new test) green (387/387) KILLED — expected "spy" to be called with arguments: ['xhigh']
M2b acpAgent throw disabled, apply call kept (fine) same green KILLED — promise resolved … instead of rejecting at the .rejects.toThrow( (line 6379)
M3 HTTP advertisement filter removed transport.test.ts (session/new ids) green (293/293) KILLED — ['model','reasoning_effort','mode'] ≠ ['model','mode']
M4 HTTP setter gate: unroutable ids fall into mode branch transport.test.ts (routable-set) green KILLED — got invalid mode "high" … instead of ConfigId not supported by this transport…
M5 dialog guard !appliedfalse use-effort-command.test.ts (warn case) green (5/5) KILLED — success text where warning expected
M5b dialog guard !appliedtrue use-effort-command.test.ts (success case) green KILLED — warning text where requested confirmation expected
M6 slash-command guard !appliedfalse effort-command.test.ts green (7/7) KILLED — "reports thinking is disabled when setReasoningEffort is a no-op"
M7 dispatch forwards unroutable id to bridge before the id check (notification path) transport.test.ts (notification test) green KILLED — expected 'high' to be undefined (bridge.lastApprovalMode, line 4778)

9/9 killed, each by its intended behavioral assertion; every unmutated control green; all sources restored byte-identical (sha256-verified, git status clean). M2 vs M2b shows the coarse mutant removed the apply call along with the throw (it sat in the condition); the fine mutant proves the test also pins the throw itself. No survivors.

Findings

None blocking. Informational:

  1. Live-inference integration tests time out in this container — pre-existing / load-induced, not PR-attributed (06-integration-aa.png). Full-file run at head: basic smoke test and handles exit plan mode… failed 3/3 retries on session/prompt timeout. Isolated A/A against the base bundle:
    • handles exit plan mode… fails identically on both arms (Request 5 (session/prompt) timed out, 1 failed | 10 skipped (11) each; scripted equality check passed) → pre-existing at base tip 4a79517815.
    • basic smoke test passes on both arms in isolation: base 97.9 s, head 100.6 s. These tests run real inference against the ambient model endpoint with no fake server; their natural round-trip (~50–90 s here) straddles the hard 60 s REQUEST_TIMEOUT_MS, so parallel file load on the shared runner tips them over — a speed-correlated timeout, fully correlated across retries, not random flake. The PR's own config-option integration test (no inference) passed in the full-file run. Nothing in the diff touches the prompt path; the A/A isolates the cause to the base tip + ambient latency.
  2. Non-string value-32603 Internal error (carried from round 1, re-measured): zod invalid_type wrapped by the ACP SDK, id-agnostic, byte-identical on base and head for mode; string violations of the new id get the PR's precise -32602. Pre-existing SDK behavior; no action needed.

Not covered

  • Per-commit attribution: depth-2 checkout — git rev-list HEAD^1..HEAD^2 reaches 1 commit while the snapshot lists 8; verified the aggregate HEAD^1..HEAD diff. The three post-round-1 commits are covered behaviorally (docs merge via conflict-marker check + rendered bullet; dialog migration via M5/M5b; hardening via M4/M7 + transport suite), not individually diffed.
  • Live JetBrains rendering and provider-specific inference behavior (PR-declared out of scope; mapping/clamping path untouched by the diff).
  • Trial merge into current main (no network in this environment); base tip 4a79517815 is what the workflow provided, and the PR already carries the 2026-08-08 main merge (d703074) whose one conflict (JetBrains doc) was checked clean of markers.
  • Repo-wide gates; only affected-workspace suites ran (core reasoning-effort 13, cli acpAgent 387 + worktree/effort-command/use-effort-command/systemController 36, transport 293, integration ACP file).
  • session/load/session/fork responses carrying a previously-set effort were not driven end-to-end; their gating rests on the single configOptionsFor choke point proven by M3 through the session/new assertion (effort is session-scoped by design, not persisted).
  • Live qwen serve /acp probe against a real child (round-1 scope kept): HTTP behavior is verified by the real-loopback transport suite whose FakeBridge child advertises reasoning_effort.

Methodology

Environment: CI node:22-bookworm container, merge-ref checkout (HEAD merge commit, HEAD^1 base tip, HEAD^2 PR head), npm ci + npm run build pre-run at head; the production bundle was produced via npm run bundle at head and at the base worktree. Head cells drove the bundle and the tsc dist entry; the base cell used a scratch worktree at HEAD^1 with a symlinked root node_modules farm (lockfile unchanged), mirrored per-package nested node_modules, and @qwen-code/* links realpath-asserted into the base tree, rebuilt via scripts/build.js --cli-only. Harnesses are plain JSON-RPC stdio clients (acp-harness.mjs) plus the repo's own vitest suites; the bundle A/A swapped dist/ with sha256 + harness-mode probes on each side. Mutation matrix scripted in mutation-matrix.mjs (five mutants) with four finer mutants applied ad hoc; every mutation restored and sha256-verified. Assertion tally is generated by tally-assertions.mjs from the real logs, including the two A/A equality checks: harness cells 88, suites 738 (core 13 + acpAgent 387 + transport 293 + cli-four 36 + integration 9), isolated A/A cells 2, A/A equality checks 2, mutation matrix 18 = 848. Raw logs in logs/, captures in evidence/.

Evidence images

01-ab-head-bundle

02-ab-base-cell

03-cli-acpagent-suite

04-transport-gate

05-mutation-matrix

06-integration-aa

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

🤖 Reviewed the latest feedback — no changes needed. Why, point by point: · 已审阅最新反馈——无需改动。逐点说明原因如下:

Autofix review round — no action needed

No actionable feedback arrived this round, so no code changes were made and no commit was created. The branch remains at its current head.

  • Reviews: no new review findings. The latest review on the PR is an approval.
  • Inline comments: none newer than the last evaluation.
  • Issue-level comments: none actionable — the only new comments are a /triage workflow command trigger and its automated acknowledgment, which are not review feedback.
  • Failed / still-red checks: none — all CI checks on the current head completed as success or intentionally skipped.
  • Conflicts: none reported (--conflict false); no merge with the base branch is needed.

Critical-only mode is active after five change-producing rounds. The single deferred non-Critical item (the bot's own prior round-summary comment) is an audit record and remains open for human follow-up; per policy it is not acted on in this round.

中文说明

Autofix 审阅轮次——无需处理

本轮没有收到可处理的反馈,因此未做任何代码改动,也未创建提交。分支保持在当前 head。

  • 审阅(Reviews):没有新的审阅发现。该 PR 上最新的一条审阅为批准(Approve)。
  • 行内评论:没有比上次评估更新的行内评论。
  • Issue 级评论:没有可处理的内容——唯一的新评论是一条 /triage 工作流命令触发及其自动确认回复,不属于审阅反馈。
  • 失败 / 持续失败的检查:无——当前 head 上的所有 CI 检查均已完成,结果为成功或按预期跳过。
  • 冲突:未报告冲突(--conflict false),无需与 base 分支合并。

在完成 5 个产生改动的轮次后,已进入仅处理 Critical 的模式。唯一被延后的非 Critical 条目(机器人此前的轮次总结评论)属于审计记录,保持开放留待人工跟进;按策略本轮不予处理。

Deferred non-Critical feedback

Critical-only mode is active after 5 change-producing rounds. The workflow excluded the non-Critical feedback below from this round's actionable sections; the items remain open for human follow-up. Maintainer feedback is deferred only after its author has used 2 regular feedback batches in this window's Critical-only tail; authors at that budget, if any, are named below. (@qwen-code /retry starts a fresh counting window.)

中文说明

完成 5 个产生改动的轮次后进入仅处理 Critical 的模式。本轮可执行区域已排除下方非 Critical 反馈;这些条目保持开放,留待人工跟进。维护者反馈仅在其本人于本窗口 Critical-only 阶段已使用 2 批常规反馈预算后才会延后;达到预算的作者(如有)在下方点名。(评论 @qwen-code /retry 可开启新的计数窗口。)

Base-conflict check · 基分支冲突检查: no conflict with main. · 与 main 无冲突。


🧠 Handled by Qwen Code · model/模型 qwen3.8-max

@wenshao

wenshao commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ✅ passed — merge-ready (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: 863 passed · 0 failed · 863 total

中文 — 判定:✅ 通过 · 可合入(agent 判定)

沙箱验证在隔离、无凭证的容器中执行了该 PR 的代码(与 base 构建 A/B 对照、无 mock harness 断言、定向门禁)。仅作为评审证据,不构成评审、批准或 CI 检查

脚本断言:863 通过 · 0 失败 · 863 总计

Verification report

PR #8526 — feat(cli): expose reasoning effort through ACP (follow-up round 3)

Verdict: merge-ready — 863/863 scripted assertions passed, 0 unexpected failures. Verified head: 6c4a28e556fab7af9a67d3ee0da134d209ac69ee (merge-ref checkout 9f0b36c684, base tip e20601d6c4455701f584a9b2b0d2e89a607db0a6).

中文 — 判定:merge-ready · 863/863 断言通过 · 0 意外失败
  • 结论merge-ready。本轮为第 3 轮跟进:PR head 与上轮完全相同6c4a28e,同一 commit OID),但合并基线前进4a79517815e20601d6c4,含 fix(cli): switch @ completion category tabs with bare arrow keys #8576 等)。因此上轮所有测量全部在新合并点重跑,未沿用任何旧数据。
  • A/B 结论:中心声明再次成立。head(bundle 与 dist 入口各 36/36,见 01-ab-head-bundle.png):session/new 广播 thought_level 类别下 default + 五档的 reasoning_effort 选项(值/名称/描述逐一精确匹配),逐档应用与读回、清除回 defaultultra/XHIGH/''-32602 精确消息拒绝且状态不变(经非变异 mode 回读证明)、模型切换后选项与已选档位保留。base 控制格(19/19,见 02-ab-base-cell.png):仅 ['mode','model']reasoning_effortInvalid params: Unsupported configId: reasoning_effort 拒绝,mode/model setter 阳性对照正常。控制纯度:base worktree 的 @qwen-code/* realpath 全部指向 base 树;base 构建产物中 reasoning_effort 出现 0 次、applyReasoningEffort 出现 0 次,head 侧均存在。
  • 增量验证:HTTP 传输层四个 setter-paired 响应面(session/newsession/resumesession/fork、setter 结果)在新 head 上逐一核实均经唯一 configOptionsFor 过滤点;加固拒绝消息与通知路径不转发由 300/300 线级测试钉住。
  • 突变矩阵:7 个突变体全部被其目标断言杀死(03-mutation-matrix.png),0 幸存;未突变对照全绿(core 13、acpAgent 396、transport 300、cli 四件套 36);所有源文件 sha256 校验恢复。
  • 发现:无阻塞项。两条信息性观察均沿自 2 轮并重新实测:① 非字符串 value 仍返回 -32603 Internal error(ACP SDK schema 层,head 新 id 与 base 既有 id mode 行为一致);② 上轮集成测试超时本轮未复现——同一文件同一点 11/11 通过,smoke 仅 9.1s(上轮同代码约 98–101s),直接证明该超时由环境推理延迟与固定 60s 请求超时的关系决定,与本 PR 无关。
  • 未覆盖:逐 commit 归因(depth-2 仅可达 1/8);JetBrains 实际渲染与 provider 推理行为(PR 自述范围外);与当前 main 的试合并(无网络);全仓 gate;session/resume/session/fork 携带已设档位的端到端驱动(单一 choke point 由 M3 经 session/new 断言杀死);集成超时既未复现,未再做 base 臂 A/A。

Previous-finding status (round 2 → round 3)

Round 2 verified head 6c4a28e and returned merge-ready; its run timed out at the tail, but the substantive report was published and is carried here. The PR head is byte-identical this round (same commit OID); the merge base advanced from 4a79517815 to e20601d6c4 (which includes #8576 and other main merges), so the merged tree changed and every measurement below was rebuilt and re-run at the new head — nothing was diffed against the old report. Suite-count deltas vs round 2 (acpAgent 387→396, transport 293→300) are tests added by the new base, all green at the merge point.

# Round-2 finding Severity Status at merged head 9f0b36c6
1 Live-inference integration tests time out in the container — pre-existing / load-induced, speed-correlated against the fixed 60 s request timeout informational stands as attribution; failure did NOT reproduce this round — the same file passes 11/11 at head (05-integration-full-file.png), including basic smoke test at 9.1 s (round 2: 97.9–100.6 s isolated on identical code) and handles exit plan mode… at 20.2 s (round 2: timed out 3/3 retries). Same code, same test file, ~10× duration swing — direct evidence the outcome tracks ambient inference latency vs the fixed 60 s cap, not the PR. No base-arm A/A re-run because there is no failure left to attribute.
2 Non-string value-32603 Internal error (ACP SDK request schema, id-agnostic, pre-existing) informational stands, unchanged — re-measured in the wire harness on both arms: head's new id reasoning_effort and base's pre-existing id mode each return code -32603, message Internal error for null / 42 / {a:1} (12 scripted equality checks passed). Pre-existing SDK behavior; no action needed.

Central claim + A/B

Central claim: an ACP stdio client receives a reasoning_effort session config option (category thought_level, Default + five tiers) and can apply/clear tiers via session/set_config_option, with invalid values rejected at the protocol boundary without state change.

Mock-free wire harness (acp-harness.mjs) spawns a real CLI process (initialize → authenticate → session/new → set_config_option…), isolated via per-run QWEN_HOME and a modelProviders openai registry entry; no inference performed. Base control built in a scratch worktree at HEAD^1.

cell entry oracle result
head (9f0b36c6 merged) production bundle dist/cli.js option id/category/currentValue/values/names/descriptions exact; per-tier apply + read-back (5 tiers); clear→default; ultra exact -32602 message + state unchanged via non-mutating mode read-back; XHIGH/'' boundary; model-switch retention (option + selected tier xhigh); unknown-id default branch; non-string probes (×3) 36/36 (01-ab-head-bundle.png)
head parity packages/cli/dist/index.js same 36/36
base control (e20601d6) base worktree packages/cli/dist/index.js ids exactly ['mode','model']; no thought_level; reasoning_effort rejected Invalid params: Unsupported configId: reasoning_effort; mode + model setter positive controls; unknown-id same default branch; non-string probes on pre-existing id (×3) 19/19 (02-ab-base-cell.png)

The flip is exactly the feature: base advertises two options and rejects the id; head advertises the third, applies all five tiers, clears to default, and rejects ultra/XHIGH/'' with -32602 leaving state untouched (proven over the wire by re-setting the current mode — which returns fresh configOptions without touching effort — and reading xhigh back).

Reviewer Test Plan walk-through (re-performed per step at the new head): options include thought_level with Default + five tiers — exact match; set Extra high → xhigh; set Default → default; ultra → invalid-params error with state unchanged; option preserved after a model switch — option and value both retained.

Control purity: base worktree at HEAD^1 (e20601d6c4) with a root node_modules symlink farm (1161 third-party entries; lockfile/package.json untouched by the PR) plus mirrored per-package nested node_modules (third-party pins only, verified free of @qwen-code entries); every @qwen-code/* link re-created into the base tree and realpath-asserted before building (readlink -ftmp/base-tree/packages/…), then rebuilt via scripts/build.js --cli-only. Built base acpAgent.js contains 0 occurrences of reasoning_effort and base reasoning-effort.js 0 of applyReasoningEffort; head's contain both.

Secondary claim: HTTP transport routing + hardened rejection

The qwen serve HTTP transport routes only model/mode; the round-2 hardening is intact at the new head:

  • configOptionsFor filters every setter-paired response. All four call sites re-verified structurally at the new head: session/new (dispatch.ts:1606), session/resume (:1817), session/fork (:2125), and the setter result (:2538) — one choke point, so a single mutation kills every gated surface.
  • The setter rejects unroutable ids with -32602 ConfigId not supported by this transport: <id> (supported: model, mode); a notification (no id) for an unroutable configId writes no response and forwards nothing to the bridge (M7 below).
  • Raw-state surfaces intentionally keep the child's full unfiltered set (doc comment at the helper); the loud setter rejection is the compensating control.

Over-the-wire suite (transport.test.ts, real HTTP loopback, FakeBridge child advertising reasoning_effort): 300/300 at the merged head.

Secondary claim: applyReasoningEffort single-implementation refactor

The core helper (set + read-back; clearing always true; returns false when Config.setReasoningEffort no-ops under reasoning: false) is used by all four surfaces — ACP setter, systemController (both sites), the /effort slash command, and the /effort dialog. The dialog's tests exercise the real helper through a mocked Config seam, so M5/M5b below bite the production path.

Mutation matrix (03-mutation-matrix.png, logs in logs/mutation-M*.txt)

# guard (mutated) pinning suite (unmutated control) verdict
M1 core read-back → return true core reasoning-effort.test.ts (13/13 green) KILLED — expected true to be false in "reports false when setReasoningEffort no-ops"
M2b acpAgent throw disabled, apply call kept (fine mutant) acpAgent.test.ts (396/396 green) KILLED — promise resolved … instead of rejecting in the PR's own selector test
M3 HTTP advertisement filter removed transport.test.ts (300/300 green) KILLED — ['model','reasoning_effort','mode'] ≠ ['model','mode'] on session/new
M4 HTTP setter gate: unroutable ids fall into mode branch transport.test.ts KILLED — got invalid mode "high" (expected one of: plan, default, auto-edit, auto, yolo) instead of the hardened message
M5 dialog guard !appliedfalse use-effort-command.test.ts (green) KILLED — success text where "thinking is currently disabled" warning expected
M5b dialog guard !appliedtrue use-effort-command.test.ts KILLED — warning text where requested confirmation expected
M6 slash-command guard !appliedfalse effort-command.test.ts (green) KILLED — "reports thinking is disabled when setReasoningEffort is a no-op"
M7 dispatch forwards unroutable id to bridge (notification path) transport.test.ts KILLED — expected 'high' to be undefined (bridge.lastApprovalMode); routable-set test also red

7/7 killed, each by its intended behavioral assertion; every unmutated control green in this round; all mutated files restored byte-identical (sha256-verified, git status clean). Round 2's coarse M2 is subsumed by the fine M2b (the throw itself is pinned, not just the apply call). No survivors.

Findings

None blocking. Informational:

  1. Integration-test timeout attribution confirmed environmental (carried, re-measured). Round 2 saw basic smoke test / handles exit plan mode… fail 3/3 retries on the 60 s session/prompt timeout while passing only in isolation (~98–101 s). This round the identical file passes 11/11 at the merged head with smoke at 9.1 s — a ~10× swing in natural duration on unchanged prompt-path code, which is the definition of the speed-correlated regime round 2 described (the diff touches no prompt path). The PR's own config-option integration test passed in both rounds. No action needed; the tests themselves are sound given a normal-latency endpoint.
  2. Non-string value-32603 Internal error (carried, re-measured). ACP SDK request-schema validation, id-agnostic: head's reasoning_effort and base's mode return byte-identical {code: -32603, message: "Internal error"} for null/42/{a:1} (12 scripted checks). String violations of the new id get the PR's precise -32602. Pre-existing SDK behavior; no action needed.

Not covered

  • Per-commit attribution: depth-2 checkout — git rev-list HEAD^1..HEAD^2 reaches 1 commit while the snapshot lists 8 (and the shallow boundary makes the count itself unreliable); verified the aggregate HEAD^1..HEAD diff. The PR head is the same commit round 2 covered behaviorally, and this round re-ran everything at the new merge point.
  • Live JetBrains rendering and provider-specific inference behavior (PR-declared out of scope; mapping/clamping path untouched by the diff).
  • Trial merge into current main (no network in this environment); base tip e20601d6c4 is what the workflow provided, and the merge into it was clean (effective diff = exactly the PR's 13 files).
  • Repo-wide gates; only affected-workspace suites ran (core 13, cli acpAgent 396 + worktree/effort-command/use-effort-command/systemController 36, transport 300, integration ACP file 11).
  • session/resume/session/fork responses carrying a previously-set effort were not driven end-to-end; their gating rests on the single configOptionsFor choke point proven by M3 through the session/new assertion (effort is session-scoped by design, not persisted).
  • Live qwen serve /acp probe against a real child (round 1–2 scope kept): HTTP behavior is verified by the real-loopback transport suite whose FakeBridge child advertises reasoning_effort.
  • No base-arm A/A for the integration timeout finding this round — the failure did not reproduce at head, so there was nothing to attribute; the head-side re-run itself is the re-measurement.

Methodology

Environment: CI node:22-bookworm container, merge-ref checkout (HEAD = merge commit 9f0b36c684, HEAD^1 = base tip e20601d6c4, HEAD^2 = PR head 6c4a28e556), npm ci + npm run build pre-run at head; the production bundle was produced via npm run bundle at head. Head cells drove the bundle and the tsc dist entry; the base cell used a scratch worktree at HEAD^1 with a symlinked root node_modules farm (lockfile unchanged), mirrored third-party nested node_modules, and @qwen-code/* links realpath-asserted into the base tree, rebuilt via scripts/build.js --cli-only. The stdio harness (acp-harness.mjs) is a plain JSON-RPC client spawning the real CLI with per-run QWEN_HOME; the transport suite is the repo's own real-loopback vitest file. Mutations were applied by exact-text substitution, run against the pinning vitest suites (which execute from source), and restored with git checkout + sha256 verification. Assertion tally: harness cells 91 (head 36 + head-dist 36 + base 19), unit suites 745 (core 13 + cli-four 36 + acpAgent 396 + transport 300), integration 11, mutation kills 7 + unmutated controls 4, structural checks 5 (four configOptionsFor call-site mappings + docs conflict-marker check) = 863, all pass. Raw logs in logs/, captures in evidence/.

Evidence images

01-ab-head-bundle

02-ab-base-cell

03-mutation-matrix

04-targeted-gates

05-integration-full-file

Harness scripts and raw logs are in the workflow run artifacts (7-day retention).

Qwen Code · sandboxed verification

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

LGTM, looks ready to ship. ✅

@wenshao
wenshao added this pull request to the merge queue Aug 11, 2026
Merged via the queue into QwenLM:main with commit e6a3272 Aug 11, 2026
93 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.10.

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

Labels

autofix/takeover Summon the autofix loop to manage this PR (remove to release; needs triage+)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[ACP] Expose reasoning effort (5 tiers: low/medium/high/xhigh/max) as a session config option

4 participants