Skip to content

fix(integration-tests): make the project typecheckable and fix what that found - #8693

Merged
doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:fix/integration-tests-typecheck-v2
Aug 8, 2026
Merged

fix(integration-tests): make the project typecheckable and fix what that found#8693
doudouOUC merged 5 commits into
QwenLM:mainfrom
doudouOUC:fix/integration-tests-typecheck-v2

Conversation

@doudouOUC

@doudouOUC doudouOUC commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Fixes #8692. This PR makes tsc -p integration-tests/tsconfig.json execute instead of aborting before checking any files, and brings the integration-test project to 0 type errors. It removes the invalid documentation entry from compilerOptions.paths, configures the no-emit cross-repository program without composite constraints, keeps package source files under their own property-access rules, adds the browser libraries used by the tests, maps imported workspace subpaths to source declarations so the result does not depend on stale build output, and declares both the jsdom runtime and its available type package directly.

The typecheck also exposed two real test defects: a detailed failure diagnostic was passed to a matcher that ignores arguments, and two terminal-capture scenarios placed the GIF switch where the runner never reads it. The remaining test edits make existing optional telemetry shapes explicit or improve type narrowing; they are intended to preserve behavior for well-formed telemetry. Production runtime behavior is unchanged.

Why it's needed

The integration-test project had an invalid paths entry whose string value caused TypeScript to stop with TS5063 before checking a single source file. The regular repository typecheck does not cover this directory because it is not an npm workspace, while TypeScript-oriented ESLint rules do not detect undeclared runtime identifiers.

This gap already allowed a real failure to ship: #8620 introduced an undeclared REPO_ROOT in an integration test. A developer run swallowed the resulting ReferenceError and reported a security regression test as a green skip, while CI crashed the module and four pre-existing tests with it. Restoring a runnable typecheck catches this class of failure before runtime.

Reviewer Test Plan

How to verify

From a clean checkout with dependencies installed and package build artifacts absent, run npx tsc -p integration-tests/tsconfig.json --pretty false; the PR head should exit 0 with no errors, while the base branch stops immediately with TS5063. Then build and bundle the repository and run the touched integration-test files; the corrected diagnostic, streaming, web UI journal, and channel-plugin paths should pass without relying on stale declarations.

Maintainer verification on head a8a6000aad used two isolated worktrees with independent npm ci installs and recorded 17/17 scripted assertions passing, including the base/head typecheck comparison, clean-tree source resolution, build, bundle, and 10/10 tests across four touched integration-test files: #8693 (comment)

Evidence (Before & After)

N/A — this is test infrastructure and type-safety work with no user-visible UI change.

Tested on

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

Environment (optional)

Maintainer verification used Node 22.23.2 and TypeScript 5.8.3 in isolated worktrees with independent dependency installations. The PR's GitHub checks are green; platform-specific checks that are merge-queue-gated remain skipped by design on the pull-request event.

Risk & Scope

  • Main risk or tradeoff: the source-path mappings must remain synchronized with workspace package export maps; the explicit DOM libraries are currently partly supplied transitively by jsdom types but document the integration project's intended environment.
  • Not validated / out of scope: wiring this typecheck into CI, the full integration suite, terminal-capture scenario runtime, and sandbox-dependent sleep-interception coverage. The edited hook-integration test is excluded by the current Vitest configuration and is typecheck-only-verified by design in this PR; adding a runnable hook-integration entry point is a separate change.
  • Breaking changes / migration notes: none.

Linked Issues

Fixes #8692.

中文说明

本 PR 做了什么

修复 #8692。本 PR 让 tsc -p integration-tests/tsconfig.json 能够真正执行,而不是在检查任何文件之前中止,并将集成测试工程的类型错误降为 0。改动移除了 compilerOptions.paths 中无效的文档项,为这个不产物、跨仓库源码的检查工程关闭 composite 约束,保留各 package 自己的属性访问规则,加入测试所需的浏览器库,把实际导入的 workspace 子路径映射到源码声明以消除对陈旧构建产物的依赖,并直接声明 jsdom 运行时及当前可用的类型包。

类型检查还发现了两个真实的测试缺陷:一处详细失败诊断被传给了会忽略参数的 matcher,另有两个 terminal-capture 场景把 GIF 开关放在 runner 不会读取的位置。其余测试改动用于显式表达已有的可选 telemetry 形状或加强类型收窄;对结构正常的 telemetry 预期保持行为不变。生产运行时行为不变。

为什么需要

集成测试工程的 paths 中存在一个无效项,其字符串值导致 TypeScript 在检查任何源码前就以 TS5063 退出。仓库常规类型检查覆盖不到该目录,因为它不是 npm workspace;面向 TypeScript 的 ESLint 规则也无法发现运行时未声明标识符。

这个盲区已经放出过真实故障:#8620 在集成测试中引入了未声明的 REPO_ROOT。开发者运行时产生的 ReferenceError 被吞掉,使安全回归测试显示为绿色跳过;CI 则在模块加载阶段崩溃,并连带中断同文件已有的四个测试。恢复可运行的类型检查后,可以在运行前捕获这类问题。

审查者测试计划

如何验证

在干净 checkout 中安装依赖并确保没有 package 构建产物,然后运行 npx tsc -p integration-tests/tsconfig.json --pretty false;PR head 应以 0 退出且无错误,而基分支会立即以 TS5063 中止。随后构建并打包仓库,再运行被触及的集成测试文件;修正后的诊断、streaming、Web UI journal 和 channel-plugin 路径应通过,且不依赖陈旧声明。

维护者已在 head a8a6000aad 上使用两个隔离 worktree 和各自独立的 npm ci 安装完成验证,17/17 条脚本断言通过,包括 base/head 类型检查对比、无构建产物时的源码解析、构建、打包,以及四个被触及集成测试文件的 10/10 测试:#8693 (comment)

证据(前后对比)

N/A——这是测试基础设施与类型安全改动,没有用户可见的 UI 变化。

测试平台

操作系统 状态
🍏 macOS ⚠️ 未测试
🪟 Windows ⚠️ 未测试
🐧 Linux ⚠️ 未测试

环境(可选)

维护者验证使用 Node 22.23.2 和 TypeScript 5.8.3,并在隔离 worktree 中分别独立安装依赖。PR 的 GitHub 检查为绿色;仅在合并队列触发的平台检查按设计在 pull-request 事件中保持跳过。

风险与范围

  • 主要风险或权衡:源码路径映射需要与各 workspace package 的 exports 映射保持同步;显式 DOM 库目前有一部分也会由 jsdom 类型间接带入,但它记录了集成测试工程预期的运行环境。
  • 未验证 / 超出范围:把该类型检查接入 CI、完整集成测试套件、terminal-capture 场景运行时,以及依赖 sandbox 的 sleep-interception 覆盖。本 PR 修改的 hook-integration 测试被当前 Vitest 配置排除,因此按设计只经过类型检查验证;增加可运行的 hook-integration 入口属于独立改动。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

修复 #8692

…hat found

`tsc -p integration-tests/tsconfig.json` could not run at all. The config
carried a `"//"` documentation key inside `compilerOptions.paths`, and every
value there must be an array, so tsc aborted with TS5063 before checking a
single file. Nothing in CI runs it either, so the directory has been
unchecked for its whole life -- which is how PR QwenLM#8620 shipped an
`integration-tests/cli/qwen-serve-streaming.test.ts` that referenced an
undeclared `REPO_ROOT`, swallowed the ReferenceError in a bare catch, and
reported a green skip for a security regression test.

Moving that note out of `paths` exposed 404 errors. Three more config
defects accounted for 353 of them:

- `composite: true` is inherited from the root config for the packages that
  are actually referenced. Composite requires every file in the program to
  appear in `include`, and these tests import package sources by relative
  path, so it produced 324 TS6307. Nothing references this project and it
  emits nothing, so it is now `composite: false`.
- The root `lib` is ES2023 only. The suite drives browser-side code in
  `terminal-capture/` and pulls SDK sources that name `WebSocket` and
  `HeadersInit`, so 21 identifiers resolved to nothing. Now DOM +
  DOM.Iterable + ES2023, matching packages/cli.
- Workspace packages resolved through `packages/core/dist` via a project
  reference, so with core unbuilt the checker reported a dozen members as
  missing from `Storage` that are right there in the source. They now
  resolve from source through `paths`, mirroring packages/cli, and the
  reference is gone.

node-pty declares `types` at the top level but its `exports` map is a bare
string with no `types` condition, so nodenext never reached the
declarations and every pty handle degraded to `any` -- which is what
silently untyped the `data` and `exitCode` callbacks in test-helper.ts. It
now resolves through `paths` as well. `@types/jsdom` is added for the one
file that uses it; DefinitelyTyped has no release matching jsdom 26 (it
jumps 21 -> 27), so this pins the current 28.x.

Two real defects fell out of the remaining 51:

- write_file.test.ts built a detailed tool-call failure message and passed
  it to `toBeTruthy()`, which takes no arguments. It was discarded on every
  failure, leaving only a bare literal.
- Two terminal-capture scenarios set `gif: true` inside `streaming`, where
  the runner never reads it. It is a scenario-level switch.

The rest was making an existing `undefined` visible. `readToolLogs()`
promised `name: string` for fields copied straight out of telemetry
attributes that nothing validates; the stdout fallback can promise them,
the telemetry branch cannot, and claiming otherwise just moved the
`undefined` past the type checker into the assertions.

This is type resolution only. `integration-tests/vitest.config.ts` keeps
its own hardcoded aliases onto the built SDK bundle, so the suite still
exercises the published-bundle shape at runtime.

Not wired into CI here, but not for cost reasons: a cold run of
`tsc -p integration-tests/tsconfig.json` takes about 106s on an idle
developer box. The program is 2679 files, of which 103 are integration
tests and roughly 1100 are package sources their own projects already
check, so there is duplicated work available to reclaim by resolving the
packages from their built declarations -- but at ~106s it is already cheap
enough to gate on as-is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🚫 Qwen Triage was cancelledview run. The run was cancelled before finishing. Check for a newer run before re-running.

🚫 Qwen Triage 已取消 —— 查看运行。运行未完成即被取消。重跑前请先确认是否有更新的运行。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Re-running the gate on the current head after the autofix rounds — nothing below changed my read of the PR, but here is the pass against a8a6000a.

Template: complete ✓

Problem: real and evidenced, not theoretical. The base integration-tests/tsconfig.json carries a "//" key in compilerOptions.paths whose value is a plain string — paths values must be arrays of strings, so tsc bails with TS5063 before checking a single file. I confirmed the offending entry directly in the base tree. The linked fallout (#8620's undeclared REPO_ROOT shipping while the suite reported a green skip) is exactly the class of bug a running typecheck catches before runtime.

Direction: aligned. Restoring a typecheck that has been silently dead is unambiguously good, and it demonstrably caught real defects rather than just churning config.

Size: no core-module paths touched — the diff is integration-test files, tsconfigs, and manifests. 0 production-logic lines, so the two-tier core gate is not applicable.

Approach: sound, with one cost worth naming. The fix removes the invalid entry, relaxes composite / noPropertyAccessFromIndexSignature for this no-emit program, adds the browser libs the tests use, and maps imported workspace subpaths to source so the check doesn't depend on build output. The price is a long paths list that must stay in sync with the packages' exports maps; it's documented inline and is the right tradeoff for a hermetic typecheck, but it is a real maintenance surface. I don't see a materially simpler path that keeps the check independent of build state.

Risk: no high-risk-path matches; no elevated risk signals.

Moving on to code review. 🔍

中文说明

在 autofix 轮次之后对当前 head 重新执行门禁——以下结论没有改变我对这个 PR 的判断,但这是针对 a8a6000a 的完整检查。

模板: 完整 ✓

问题: 真实存在且有证据,不是理论性的。基线的 integration-tests/tsconfig.jsoncompilerOptions.paths 中有一个 "//" 键,其值是纯字符串——paths 的值必须是字符串数组,所以 tsc 会以 TS5063 在检查任何文件之前直接退出。我已在基线代码树中直接确认了这个无效项。其关联后果(#8620 引入的未声明 REPO_ROOT 在套件显示绿色跳过的情况下被合入)正是可运行的类型检查能在运行前捕获的那类 bug。

方向: 对齐。恢复一个已经静默失效的类型检查显然有益,而且它确实捕获了真实缺陷,而不只是改动配置。

规模: 未触及核心模块路径——diff 全部是集成测试文件、tsconfig 和依赖清单。生产逻辑行数为 0,因此两级核心门禁不适用。

方案: 合理,但有一个需要指明的代价。修复移除了无效项,为这个不产物的检查工程放宽了 composite / noPropertyAccessFromIndexSignature,加入了测试所需的浏览器库,并把导入的 workspace 子路径映射到源码,使检查不依赖构建产物。代价是需要维护一份较长的 paths 列表,且必须与各 package 的 exports 映射保持同步;这一点已在配置中内联说明,对于自包含的类型检查是正确的取舍,但确实是真实的维护面。我没有找到在保持检查独立于构建状态的同时明显更简的路径。

风险: 无高风险路径命中;无升级风险信号。

进入代码审查 🔍

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review first, then the CI signal. The blocker from the previous round is fixed, and the head is leaner than v1.

Code review

The earlier blocker — root @types/jsdom leaking into integrations/external-context's build — is resolved the way the review suggested: that project now pins "types": ["node"], with a comment that explains the mechanism accurately (the inherited vitest/globals types reach the installed jsdom peer types, dragging the DOM lib in, which flips @types/node's conditional fetch globals to DOM variants whose ReadableStream is not async-iterable — breaking the for await over response.body in http-client.ts:124). The two jobs that died on it last round (Test (ubuntu-latest), web-shell E2E Smoke) build green on this head.

The head also improved on v1: the four packages/sdk-typescript/src/ files with TS4111 bracket-access rewrites are gone, replaced by "noPropertyAccessFromIndexSignature": false scoped to the integration-tests project. Precedented (packages/desktop already sets it false), keeps dot access in the tests, and no longer touches production sources. That's the simpler of the two options.

Spot checks against the base sources, all verified:

  • Both defect fixes are still real. toBeTruthy() takes no message argument — the write_file.test.ts diagnostic now sits on expect(value, message) where vitest reads it. ScenarioConfig.gif is the scenario-level switch the runner reads; the capture-step type inside flow has no gif field at all. The runner defaults gif to true, so today's behavior is unchanged, but the switch now actually means something.
  • dmPolicy: 'open' in channel-plugin.test.ts is required, not fillerChannelConfig.dmPolicy is a non-optional field in packages/channels/base/src/types.ts, with 'open' documented as the default. Runtime-neutral. The same file's move from dist/ to src/ imports is forced by type identity: plugin-example sources were already imported from src, and mixing src and dist declarations for channel-base produces duplicate-private-class errors.
  • asAccepted() preserves semantics. isNonBlockingAccepted narrows on 'promptId' in result && 'lastEventId' in result — one key stricter than the old inline 'promptId' in accepted check, matching the declared NonBlockingPromptAccepted type exactly. Exported from src/daemon/index.ts, which is what the new paths entry promises.
  • The readToolLogs() optionality is honest and call-site-consistent — including the callers not in the diff (stdin-context.test.ts:96, mixed-input-crash.test.ts:38), which only assert .toBeNull().
  • The "runtime impact: none" claim holdsintegration-tests/vitest.config.ts still aliases the SDK to the built dist/index.mjs, so the suite keeps exercising the published-bundle shape. (One nuance: channel-plugin.test.ts now imports channel-base from src at runtime too, not just for types — that's inherent to the type-identity fix, and the merge-queue integration run is what settles it.)
  • The package-lock.json version sync is a correction, not churn: main's integrations/external-context/package.json says 0.20.1; the lock was the stale side at 0.21.7.
  • Declaring jsdom + @types/jsdom makes an existing dependency explicitqwen-serve-webui-live-journal-recovery.test.ts imports jsdom directly and previously relied on hoisting. REPO_ROOT itself is already declared on main (the fix(serve): Allow approved same-host text reads outside workspace #8620 incident was fixed separately); this PR remains the piece that lets the type checker see the directory.

Two non-blocking notes:

  • sleep-interception.test.ts predicates moved from truthiness to exact comparison (!call.successcall.success === false). The comment explains why (match only explicit booleans from parsed telemetry); in practice the records carry explicit success, so this should be behavior-preserving — the merge-queue run is the oracle.
  • The createToolCallErrorMessage comment still promises missing entries "print as undefined", but Array.prototype.join renders them as empty strings. Cosmetic, failure-message path only — flagged last round, left as-is, leaving it as-is.

Testing evidence

Unattended CI run — no PR code executed locally; the evidence below is the PR's own CI results for 1f66c987 fetched via the API. All 17 check-runs completed: 13 success, 4 skipped, none failed.

The skips are by design, not casualties: Integration Tests (CLI, No Sandbox) is gated on github.event_name == 'merge_group' in ci.yml (it runs in the merge queue, not on PR pushes), and the macOS/Windows test jobs and coverage comment are gated the same way. One workflow run did land red — 🧐 Qwen Pull Request Review — but it produced zero check-runs (GitHub reports "likely failed because of a workflow file issue" before any job starts), and its ten most recent runs failed across wholly unrelated branches, merge-queue pushes included: pre-existing bot-infrastructure noise, not caused by this PR (which touches nothing under .github/), and it doesn't affect the check suite below.

Check Conclusion
Classify PR ✅ success
Desktop Shell (ubuntu-22.04) ✅ success
Desktop Shell (windows-2022) ✅ success
Real daemon E2E / Java 11 ✅ success
Remind on force-push ✅ success
Test (ubuntu-latest, Node 22.x) ✅ success
label ✅ success
macos-latest / Java 21 ✅ success
ubuntu-latest / Java 11 ✅ success
ubuntu-latest / Java 17 ✅ success
ubuntu-latest / Java 21 ✅ success
web-shell E2E Smoke (ubuntu-latest, Node 22.x) ✅ success
windows-latest / Java 21 ✅ success
Integration Tests (CLI, No Sandbox) ⏭️ skipped
Post Coverage Comment ⏭️ skipped
Test (macos-latest, Node 22.x) ⏭️ skipped
Test (windows-latest, Node 22.x) ⏭️ skipped

Not verified: tsc -p integration-tests/tsconfig.json reaching 0 errors — no CI job runs that command on PRs (that gap is this PR's entire subject), so the claim currently rests on the author's test plan. Not verified either: the integration suite at runtime — merge-queue-gated by design, and the author discloses their local run was blocked by a stale dist in their worktree.

Sandboxed verification would settle the first gap: @qwen-code /verify — an A/B run proves the base aborts with TS5063 while this head typechecks clean, which no existing CI job pins. This is a sponsored run (the author lacks write access): a maintainer's @qwen-code /verify approves the head it was written against, and the run carries a pre-execution risk screen plus a full workspace wipe — read its report with the same skepticism as the fork's own CI logs.

中文说明

先说代码审查,再说 CI 信号。上一轮的阻塞项已修复,且当前 head 比 v1 更精简。

代码审查:先前阻塞项——根级 @types/jsdom 渗入 integrations/external-context 构建——已按评审建议的方式解决:该项目现在固定 "types": ["node"],注释准确解释了机制(继承的 vitest/globals 类型触及已安装的 jsdom 对等类型,拖入 DOM lib,使 @types/node 的条件式 fetch 全局翻转为 DOM 变体,其 ReadableStream 不可异步迭代——破坏 http-client.ts:124response.bodyfor await)。上一轮因此挂掉的两个 job(Test (ubuntu-latest)web-shell E2E Smoke)在本 head 上构建为绿。

本 head 还优于 v1:packages/sdk-typescript/src/ 中四个文件的 TS4111 方括号改写已移除,代之以 integration-tests 项目范围内的 "noPropertyAccessFromIndexSignature": false。有先例(packages/desktop 已设为 false),测试保留点访问,且不再触碰生产源码。这是两个选项里更简单的那个。

对基底源码的抽查全部核实通过:两个缺陷修复仍然属实(toBeTruthy() 不接受消息参数;ScenarioConfig.gif 才是 runner 读取的场景级开关,flow 内 capture 步骤类型根本没有 gif 字段,runner 默认 gif 为 true,故今日行为不变但开关从此生效);channel-plugin.test.ts 中的 dmPolicy: 'open'ChannelConfig 的必填字段(源码注释载明默认 'open'),运行时中性,同文件从 dist/ 改为 src/ 导入是类型同一性所迫(plugin-example 本就走 src,混用 src/dist 声明会产生重复私有类错误);asAccepted() 语义保持(isNonBlockingAccepted'promptId' in result && 'lastEventId' in result 收窄,比旧的内联检查多一个键,恰好与声明的 NonBlockingPromptAccepted 类型一致,且如 paths 条目所承诺自 src/daemon/index.ts 导出);readToolLogs() 的可选化诚实且与所有调用点一致(含 diff 之外、仅断言 .toBeNull() 的两处);"零运行时影响"成立——vitest.config.ts 的 alias 仍指向构建产物 dist/index.mjs(一处细节:channel-plugin.test.ts 现在运行时也从 src 导入 channel-base,这是类型同一性修复的固有结果,由合并队列的集成运行来裁决);lockfile 的版本同步是纠错而非搅动(main 上该 workspace 的 package.json 本就是 0.20.1,lock 才是陈旧的一方);声明 jsdom + @types/jsdom 是把既有依赖显式化(该测试直接 import jsdom,此前依赖提升);REPO_ROOT 已在 main 上另行声明,本 PR 仍是让类型检查器看到该目录的那块拼图。

两条不构成阻塞的提示:sleep-interception.test.ts 的谓词从真值判断改为精确比较(!call.successcall.success === false),注释已说明原因,实际记录都带显式 success,行为应保持不变——以合并队列运行为准;createToolCallErrorMessage 的注释仍声称缺失项"打印为 undefined",而 Array.prototype.join 会渲染为空字符串,纯外观问题(仅失败消息路径),上轮已指出,维持原样。

测试证据:无人值守 CI 运行——未在本地执行任何 PR 代码;以下为经 API 获取的 1f66c987 自身 CI 结果。17 个 check-run 全部完成:13 成功、4 跳过、无失败。跳过均为设计使然:Integration Tests (CLI, No Sandbox) 在 ci.yml 中以 merge_group 事件为门(合并队列运行,不在 PR 推送时运行),macOS/Windows 测试 job 与覆盖率评论同理。确有一个 workflow run 为红——🧐 Qwen Pull Request Review——但它未产生任何 check-run(GitHub 报告"likely failed because of a workflow file issue",未及启动任何 job),且其最近十次运行在完全无关的分支(含合并队列推送)上全部失败:既有的机器人基础设施噪音,非本 PR 引入(未触碰 .github/ 下任何内容),也不影响下表中的 check 套件。

未验证:tsc -p integration-tests/tsconfig.json 归零——没有任何 CI job 在 PR 上运行该命令(这正是本 PR 的主题),目前仅为作者测试计划中的声明。集成套件的运行时表现同样未验证——按设计仅在合并队列运行,作者披露其本地运行被 worktree 中陈旧的 dist 挡住。沙箱验证可以补上第一个缺口:@qwen-code /verify——A/B 运行可证明基底以 TS5063 中止而本 head 类型检查干净;这是赞助运行(作者无写权限),由 maintainer 触发,运行前有风险筛查与工作区清除,报告应与 fork 自身 CI 日志同等审慎对待。

Qwen Code · qwen3.8-max

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Confidence: 4/5 — clean re-review: last round's blocker is fixed and CI-verified, and the head is simpler than v1; the remaining reservations are verification gaps, not code defects.

Stepping back: this is still the change I would have proposed. The problem was demonstrated before any code was written (a TS5063 abort reproducible on main, plus the #8620 incident it let through), every config edit maps to a documented error class, and the two genuine defects it surfaced check out against the sources. The previous review round worked the way it should: this PR added root @types/jsdom, CI caught it leaking into an unrelated workspace's build, the fix is the surgical "types": ["node"] isolation the review suggested, and the two jobs that were red last round are green on this head. The takeover round improved it further — v1's bracket-access rewrites in production SDK sources, forced by the root compiler flag, are now a one-line flag relaxation scoped to this project, the simpler of the two options.

Honest reservations, none blocking:

  • The central claim — tsc -p integration-tests/tsconfig.json reaching 0 errors — is not exercised by any CI job (that gap is the PR's subject, and wiring the check is deliberately deferred to its own decision). The Stage 2 comment names @qwen-code /verify as the sponsored lane that would prove it A/B.
  • The integration suite itself only runs in the merge queue, by design; the one runtime-visible change (channel-plugin importing channel-base from source) is exactly what that run will exercise.
  • The PR body is stale relative to the head (it still describes the dropped TS4111 edits and a 25-file count).

With this pass I'm also dismissing my own three earlier change-request reviews: the jsdom blocker is fixed and CI-verified, and the other two reviews objected to the integration suite not running on PR CI — that is this repository's merge-queue design, not a defect this PR introduced, and the PR discloses the gap honestly instead of hiding it.

Approving, pinned to the commit under review. The merge queue remains the gate for the suite itself.

中文说明

置信度:4/5 —— 复审干净:上一轮的阻塞项已修复并经 CI 验证,当前 head 比 v1 更精简;剩余的保留意见是验证缺口,不是代码缺陷。

退一步看:这仍然是我会提出的方案。问题在写任何代码之前就已证实(main 上可复现的 TS5063 中止,以及它放过的 #8620 事故),每项配置改动都对应一类有记录的错误,抓出的两个真实缺陷也经源码核实。上一轮评审按应有的方式发挥了作用:本 PR 新增根级 @types/jsdom,CI 捕获到它渗入无关 workspace 的构建,修法是评审建议的外科手术式 "types": ["node"] 隔离,上一轮变红的两个 job 在本 head 上已转绿。takeover 一轮还让它更进一步——v1 中被根编译器标志所迫、对生产 SDK 源码的方括号改写,现在是本项目范围内的一行标志放宽,两个选项里更简单的那个。

诚实的保留意见,均不构成阻塞:核心声明——tsc -p integration-tests/tsconfig.json 归零——没有任何 CI job 执行(该缺口正是本 PR 的主题,接入检查被刻意留给独立决策),Stage 2 评论已指名 @qwen-code /verify 作为可做 A/B 证明的赞助通道;集成套件本身按设计只在合并队列运行,其中唯一的运行时可见改动(channel-plugin 从源码导入 channel-base)恰将由该运行检验;PR 正文相对 head 已过时(仍描述已移除的 TS4111 改写与 25 文件数)。

本次同时撤销我自己此前的三条请求修改评审:jsdom 阻塞项已修复并经 CI 验证;另外两条针对的是集成套件不在 PR CI 上运行——那是本仓库的合并队列设计,并非本 PR 引入的缺陷,且 PR 诚实地披露了这一缺口而非掩盖。

批准,锚定在被审查的提交上。合并队列仍是套件本身的闸门。

Qwen Code · qwen3.8-max

Reviewed at 1f66c987f091e849956229baf872a0354f9e2354 · 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.

One blocker: the root @types/jsdom addition leaks into integrations/external-context's compilation and breaks its build (http-client.ts(117,29): error TS2504), failing three CI jobs that were green on the same base minutes earlier. Suggested fix: an explicit "types" allowlist (e.g. ["node"]) in that project's tsconfig, or equivalent isolation — full details in my review comment above. Everything else in the diff looks ready.

一个阻塞项:新增的根级 @types/jsdom 渗入 integrations/external-context 的编译并破坏其构建(http-client.ts(117,29): error TS2504),导致三个 CI job 失败——同一基底几分钟前还是绿的。建议修法:在该项目 tsconfig 中加显式 "types" 白名单(如 ["node"])或等效隔离——详见上方审查评论。diff 其余部分已就绪。

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

Comment thread integration-tests/tsconfig.json Outdated
Comment thread integration-tests/tsconfig.json Outdated
Comment thread integration-tests/tsconfig.json
Comment thread integration-tests/channel-plugin.test.ts
Comment thread integration-tests/cli/qwen-serve-streaming.test.ts
Comment thread integration-tests/tsconfig.json Outdated
Comment thread package.json
Comment thread packages/sdk-typescript/src/daemon/ui/transcript.ts Outdated
Comment thread integration-tests/cli/sleep-interception.test.ts Outdated
Comment thread integration-tests/tsconfig.json Outdated
@doudouOUC doudouOUC self-assigned this Aug 7, 2026
@doudouOUC
doudouOUC requested review from wenshao and yiliang114 August 7, 2026 15:37
@doudouOUC

Copy link
Copy Markdown
Collaborator Author

@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)即可释放。

@github-actions github-actions Bot added the review/self-reported The linked issue was opened by the PR author (self-reported) label Aug 7, 2026
@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

中文说明

🔀 已更新 base:红色检查 [Test (ubuntu-latest, Node 22.x), 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 3 finishedview run. See this round's report below.

中文说明

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

@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 (only cli/qwen-serve-streaming.test.ts was run locally: 5/5 pass).

Not reviewed: reverse audit — stopped before round 4 by the review time budget.

中文说明

未审查:build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally (only cli/qwen-serve-streaming.test.ts was run locally: 5/5 pass)。

未审查:反向审计——评审时间预算不足,未能开始第 4 轮。

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

Comment thread integration-tests/tsconfig.json Outdated
Comment thread integration-tests/hook-integration/hooks-advanced.test.ts Outdated
…ution paths

Address review round 1:

- external-context: override `types` to ["node"]. The root @types/jsdom
  entered its program through vitest's optional jsdom types and injected
  lib dom, flipping @types/node's fetch globals to DOM variants whose
  ReadableStream is not async-iterable (TS2504 in http-client.ts), which
  failed every CI job during the npm ci prepare build.
- integration-tests tsconfig: explicit nodenext paths entries for every
  workspace subpath the program imports (sdk/daemon, 19 acp-bridge
  subpaths, core goalWire/memoryScopes/userPromptSubmitContext, webui
  daemon-react-sdk, channel-base); drop the dead `*` wildcards; include
  **/*.tsx. Typechecks green with the source packages' dists removed.
- Relax noPropertyAccessFromIndexSignature in integration-tests and
  revert the six bracket-access rewrites it forced in SDK sources.
- channel-plugin: import channels/base from src and map
  @qwen-code/channel-base to source so both declarations agree.
- qwen-serve-streaming: asAccepted delegates to the SDK's exported
  isNonBlockingAccepted type predicate instead of a drifted copy.
- sleep-interception: tighten blocked predicates to success === false
  and fix the comment describing them.
- Declare jsdom at the root next to @types/jsdom.
@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 feedback addressed — PR #8693 (round 2)

All ten inline findings and the review-level blocker are resolved in code. No conflict work was needed (--conflict false). One commit on the existing branch.

Blocker: root @types/jsdom breaks integrations/external-context (3 failed CI jobs)

Reproduced locally, root cause fully traced, fixed with the reviewer's suggested isolation.

Mechanism (verified step by step):

  1. vitest/optional-types.d.ts unconditionally does export type * as jsdomTypes from 'jsdom', and vitest/dist/index.d.ts imports it. Every TS program whose types include vitest/globals (the root default) therefore loads @types/jsdom as soon as it is installed — a types allowlist cannot stop it, because the import comes through the allowlisted vitest types.
  2. @types/jsdom/index.d.ts carries /// <reference lib="dom" />, injecting the DOM lib into the program.
  3. With the DOM lib present, @types/node's conditional fetch globals (typeof globalThis extends { onmessage: any } ? ...) flip to the DOM variants, whose ReadableStream has no [Symbol.asyncIterator] (that lives in lib.dom.asynciterable, not included).
  4. for await (const chunk of response.body) in integrations/external-context/src/http-client.ts then fails with exactly error TS2504 at column 29 — the CI error, reproduced character-for-character with a stand-in @types/jsdom.

All three failed jobs share one path: CI npm ci runs the root prepare script, which runs npm run build over every workspace in order; integrations/external-context is built last and its tsc --build hit TS2504.

Fix: integrations/external-context/tsconfig.json now overrides types to ["node"]. Its compiled sources use no vitest globals (test files are excluded), so this severs the only path through which jsdom types entered the program. Verified: the exact TS2504 reproduces before the fix and npm run build completes (including external-context) with @types/jsdom present after it. No other workspace needed the same treatment — every other build compiled cleanly in CI with the DOM lib injected (external-context is last in build order, so all predecessors succeeded), and core/acp-bridge were re-verified locally.

Inline findings

Finding Disposition Change
Critical tsconfig.json:47@qwen-code/sdk/daemon fell back to dist Resolved Explicit paths entries for @qwen-code/sdk/daemon (+ /daemon/transcript, /daemon/transports); verified with SDK dist removed
Critical tsconfig.json:49 — acp-bridge subpaths + core goalWire fell back to dist Resolved Explicit entries for every acp-bridge subpath the program imports (19, incl. the divergent-name transcriptReplay/daemonMemoryBudget the diff had missed) and for goalWire, memoryScopes, userPromptSubmitContext; verified with acp-bridge/core dists removed
Critical tsconfig.json:55@qwen-code/webui/daemon-react-sdk had no entry Resolved Entry added; verified with webui dist removed
Critical channel-plugin.test.ts:45 — value import still pointed at channels/base/dist Resolved Switched to ../packages/channels/base/src/index.js and added the @qwen-code/channel-base paths entry (both halves, as prescribed); verified via typecheck with dist removed and a live single-turn E2E run
Suggestion qwen-serve-streaming.test.ts:145asAccepted duplicated the SDK guard Resolved Now delegates to isNonBlockingAccepted from @qwen-code/sdk/daemon (a real type predicate checking promptId and lastEventId); the drifted copy and its stale justification comment are gone
Suggestion tsconfig.json:58**/*.ts misses .tsx Resolved include now also has **/*.tsx, pulling terminal-capture/skill-review-harness/text-capture.tsx into the program; typecheck stays green
Suggestion package.json:121@types/jsdom declared without jsdom Resolved Root now declares "jsdom": "^26.1.0" (matching the runtime already hoisted from packages/cli) plus the matching lockfile root entry
Suggestion transcript.ts:139 — SDK production source rewritten for noPropertyAccessFromIndexSignature Resolved Took the reviewer's alternative: integration-tests sets the flag to false (precedent: packages/desktop), and all six bracket-access edits in four SDK source files are reverted to main
Suggestion sleep-interception.test.ts:25 — comment misdescribed the predicates Resolved Took the tightening option: the three blocked predicates now use call.success === false (symmetric with === true, no longer matching a malformed record with absent success), and the comment matches the shipped predicates
Suggestion tsconfig.json:18 — webui paths entry surfaces three TS4111 Resolved Discharged by the flag relaxation above; the webui sources need no edits and the program typechecks with webui dist removed

Additional shrink this round enabled: the three nodenext paths wildcards (@qwen-code/{qwen-code-core,sdk,acp-bridge}/*) were probe-proven dead under nodenext (no extension/index probing on substituted paths — every subpath fell through to the exports map) and were removed along with the flag-only bracket-access churn this PR had introduced in test-helper.ts, acp-cron.test.ts, hooks-advanced.test.ts, and vitest.terminal-bench.config.ts.

Verification

Commands actually run this round, in the checked-out branch state (the runner's node_modules predates the branch's @types/jsdom, so verification used a minimal stand-in @types/jsdom whose two load-bearing behaviors — providing JSDOM types and the lib="dom" reference — match the real package; it will be the real package once CI runs npm ci from the branch lockfile):

  • npm run build — passed. Full ordered workspace build with @types/jsdom present; this is the exact sequence CI's prepare runs during npm ci, and external-context's tsc --build (the step that failed CI) now succeeds.
  • npm run typecheck — passed (all workspaces, exit 0).
  • npm run lint — passed (exit 0).
  • npx tsc -p integration-tests — exit 0 with dists present, and exit 0 with the dists of core, sdk-typescript, acp-bridge, webui, and channels/base removed (the source-resolution promise, probe-verified both ways).
  • Focused Vitest, touched packages: @qwen-code/sdk — 1464 passed (32 files); @qwen-code/external-context — 166 passed (9 files).
  • Integration tests after npm run bundle (all four touched files, run against the bundle): channel-plugin.test.ts — 3 passed (single-turn, session state, second sender; exercises the switched channels/base/src import path live); cli/qwen-serve-streaming.test.ts — 5 passed with model keys cleared, mirroring the CI no-AK gate (covers the isNonBlockingAccepted delegation); cli/sleep-interception.test.ts — 4 passed (covers the tightened success === false predicates); cli/qwen-serve-webui-live-journal-recovery.test.ts — 1 passed (the jsdom consumer).
  • npm run generate:settings-schema — not applicable (no settings source changed).
中文说明

已处理的审查反馈 — PR #8693(第 2 轮)

十条行内发现与一条审查级阻塞项均已在代码中解决。无需冲突处理(--conflict false)。在现有分支上提交一次。

阻塞项:根级 @types/jsdom 破坏 integrations/external-context(3 个 CI job 失败)

已在本地复现,根因完整追溯,并采用审查者建议的隔离方式修复。

机制(逐步验证):

  1. vitest/optional-types.d.ts 无条件执行 export type * as jsdomTypes from 'jsdom',而 vitest/dist/index.d.ts 会 import 它。因此任何 typesvitest/globals(根 tsconfig 的默认值)的 TS 工程,只要安装了 @types/jsdom 就会加载它——types 白名单拦不住,因为这个 import 正是经由白名单内的 vitest 类型进来的。
  2. @types/jsdom/index.d.ts 带有 /// <reference lib="dom" />,把 DOM lib 注入工程。
  3. DOM lib 一旦存在,@types/node 的条件式 fetch 全局类型(typeof globalThis extends { onmessage: any } ? ...)翻转为 DOM 变体,其 ReadableStream 没有 [Symbol.asyncIterator](那在 lib.dom.asynciterable 里,未被包含)。
  4. integrations/external-context/src/http-client.ts 中的 for await (const chunk of response.body) 于是报出与 CI 完全一致的 error TS2504(第 29 列)——已用 @types/jsdom 替身逐字符复现。

三个失败 job 共享同一条路径:CI 的 npm ci 会执行根的 prepare 脚本,该脚本按序对全部 workspace 执行 npm run buildintegrations/external-context 最后构建,其 tsc --build 命中 TS2504。

修复:integrations/external-context/tsconfig.json 现在把 types 覆盖为 ["node"]。它编译的源码不使用任何 vitest 全局(测试文件已被排除),因此斩断了 jsdom 类型进入该工程的唯一路径。验证:修复前精确复现 TS2504;修复后在 @types/jsdom 存在的情况下 npm run build 完整通过(含 external-context)。其他 workspace 无需同样处理——CI 上所有其他构建在 DOM lib 注入的情况下均编译通过(external-context 在构建顺序中最后,其前面全部成功),core/acp-bridge 也在本地复核通过。

行内发现

发现 处置 变更
Critical tsconfig.json:47@qwen-code/sdk/daemon 回退到 dist 已解决 @qwen-code/sdk/daemon(及 /daemon/transcript/daemon/transports)添加显式 paths 条目;已在移除 SDK dist 后验证
Critical tsconfig.json:49 — acp-bridge 子路径 + core goalWire 回退到 dist 已解决 为程序实际 import 的全部 acp-bridge 子路径添加显式条目(19 个,含 diff 遗漏的异名条目 transcriptReplay/daemonMemoryBudget),以及 goalWirememoryScopesuserPromptSubmitContext;已在移除 acp-bridge/core dist 后验证
Critical tsconfig.json:55@qwen-code/webui/daemon-react-sdk 无条目 已解决 添加条目;已在移除 webui dist 后验证
Critical channel-plugin.test.ts:45 — 值 import 仍指向 channels/base/dist 已解决 改为 ../packages/channels/base/src/index.js 并添加 @qwen-code/channel-base paths 条目(按规定两半都做);通过移除 dist 后的 typecheck 和一次真实单轮 E2E 运行验证
Suggestion qwen-serve-streaming.test.ts:145asAccepted 复制了 SDK 守卫 已解决 改为委托 @qwen-code/sdk/daemonisNonBlockingAccepted(真正的类型谓词,检查 promptIdlastEventId);漂移的副本及其过时注释已删除
Suggestion tsconfig.json:58**/*.ts 漏掉 .tsx 已解决 include 增加 **/*.tsx,把 terminal-capture/skill-review-harness/text-capture.tsx 纳入程序;typecheck 保持绿
Suggestion package.json:121 — 声明了 @types/jsdom 却没有 jsdom 已解决 根 package.json 增加 "jsdom": "^26.1.0"(与已从 packages/cli 提升的运行时版本一致),lockfile 根条目同步
Suggestion transcript.ts:139 — SDK 生产源码为 noPropertyAccessFromIndexSignature 改写 已解决 采用审查者的备选方案:integration-tests 将该标志设为 false(先例:packages/desktop),四个 SDK 源文件中全部六处方括号访问改动回退到 main
Suggestion sleep-interception.test.ts:25 — 注释对谓词描述有误 已解决 采用收紧方案:三个"被阻塞"谓词改为 call.success === false(与 === true 对称,不再把缺少 success 的畸形记录当作阻塞),注释与谓词一致
Suggestion tsconfig.json:18 — webui paths 条目暴露三个 TS4111 已解决 由上面的标志放宽一并免除;webui 源码无需改动,移除 webui dist 后程序 typecheck 通过

本轮还顺势收缩:三条 nodenext paths 通配符(@qwen-code/{qwen-code-core,sdk,acp-bridge}/*)经探针证实是死配置(nodenext 对替换后的路径不做扩展名/索引探测——所有子路径都回退到 exports 映射),连同本 PR 为标志而在 test-helper.tsacp-cron.test.tshooks-advanced.test.tsvitest.terminal-bench.config.ts 引入的方括号访问改动一并移除。

验证

本轮在当前检出分支状态下实际执行的命令(runner 的 node_modules 早于分支的 @types/jsdom,验证使用了一个最小替身 @types/jsdom,其两个关键行为——提供 JSDOM 类型与 lib="dom" 引用——与真实包一致;CI 按分支 lockfile 执行 npm ci 时安装的是真实包):

  • npm run build — 通过。@types/jsdom 存在下的全量按序 workspace 构建;这正是 CI npm ci 期间 prepare 执行的序列,其中 external-context 的 tsc --build(CI 上失败的步骤)现已成功。
  • npm run typecheck — 通过(全部 workspace,exit 0)。
  • npm run lint — 通过(exit 0)。
  • npx tsc -p integration-tests — dist 齐全时 exit 0;移除 core、sdk-typescript、acp-bridge、webui、channels/base 的 dist 后同样 exit 0("从源码解析"的承诺已双向探针验证)。
  • 触及包的聚焦 Vitest:@qwen-code/sdk — 1464 通过(32 个文件);@qwen-code/external-context — 166 通过(9 个文件)。
  • npm run bundle 之后的集成测试(全部四个触及文件,针对 bundle 运行):channel-plugin.test.ts — 3 通过(单轮、会话状态、第二发送者;真实运行了切换后的 channels/base/src import 路径);cli/qwen-serve-streaming.test.ts — 清空模型 key 后 5 通过,与 CI no-AK 闸门一致(覆盖 isNonBlockingAccepted 委托);cli/sleep-interception.test.ts — 4 通过(覆盖收紧后的 success === false 谓词);cli/qwen-serve-webui-live-journal-recovery.test.ts — 1 通过(jsdom 使用方)。
  • npm run generate:settings-schema — 不适用(未改动 settings 源)。

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-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round result: no code changes required (round-2 findings verified / answered)

This round triaged review round 2 (one Critical, one Suggestion) plus the review-level "not reviewed" notes. No code change was needed: the Critical had already been fixed by the current head commit (1f66c987f0), which this round re-verified with fresh cold-tree and warm-tree evidence, and the Suggestion was answered with the declaration option it itself offered.

R2-1 (Critical): missing @qwen-code/qwen-code-core/userPromptSubmitContext paths entry — already fixed at HEAD, re-verified

The reviewed tree (the merge commit before the current head) carried only the @qwen-code/qwen-code-core/* wildcard, which substitutes the divergent subpath to nonexistent packages/core/src/userPromptSubmitContext. The current head commit replaced every wildcard with explicit per-subpath entries, including exactly the entry the finding asks for: @qwen-code/qwen-code-core/userPromptSubmitContext../packages/core/src/hooks/user-prompt-submit-context.ts, matching the package's exports map. Fresh evidence from this round:

  • Cold tree (all 21 packages/*/dist and integrations/*/dist directories hidden, then restored): npx tsc -p integration-tests reports zero userPromptSubmitContext errors — the transcript-replay.ts(27,58) TS2307 the finding reproduced is gone, and there are no core/sdk/acp-bridge/webui resolution errors at all.
  • Warm tree (after npm run build): npx tsc -p integration-tests exits 0.
  • The wildcard half of the suggested edit is deliberately absent: the head commit dropped all wildcards because nodenext performs no extension probing on substituted paths. The explicit-entry half is what removes the error, and the explicit set is complete for every subpath the program imports.

R2-3 (Suggestion): hook-integration files are executed by no runnable command — wiring declined; declaration made

Declined: wiring hook-integration into a runnable entry point. The suite was deliberately excluded from execution by the very commit that introduced it (aa0f04b60a "add doc for hooks and skip integration test"); whether to revive a long-skipped suite is a scope decision for a dedicated change, not for this typecheck PR.

Declared, per the finding's alternative: edits under integration-tests/hook-integration/ are typecheck-only-verified by design. The typecheck this PR adds is now their only gate, which matches the suite's existing design. One factual correction: this PR edits only hooks.test.ts (removing an unused _result binding that noUnusedLocals rejects); it does not edit hooks-advanced.test.ts.

Review note: "Integration Tests (CLI, No Sandbox) was skipped in CI"

The skip is by CI design: that job (integration_cli in .github/workflows/ci.yml) is gated on github.event_name == 'merge_group', and the merge queue is not yet enabled on main, so per the comment in ci.yml it never triggers for PR pushes. To still cover the suite, this round ran it locally after npm run build && npm run bundle (results in Verification): every collected file in integration-tests/cli/ passes except five by-design self-skips (results in Verification); the single failure was environmental (see below) and touches no file this PR edits. The "reverse audit" note is a reviewer time-budget statement and requires no code action.

Environment notes (this checkout only — no code involved)

  • The checkout's node_modules was stale against the branch's committed lockfile: @types/jsdom is declared by this branch but had not been installed, producing a cold-tree TS7016 in qwen-serve-webui-live-journal-recovery.test.ts. Syncing with npm install (committed lockfile respected; package.json / package-lock.json unchanged afterwards) removed it.
  • cli/qwen-config-dir.test.ts test 1d ("CLI functions normally when QWEN_HOME is not set") failed here with EACCES: permission denied, mkdir '/home/github-runner/.qwen' because this container's $HOME is root-owned and unwritable by the test user. The file is untouched by this PR; rerunning it with a writable HOME passes 7/7, proving the cause environmental. In CI, $HOME is writable, so this does not reproduce there.

Verification

  • npm install --cache /tmp/npm-cache-qc --no-audit --no-fund — synced node_modules to the committed lockfile (installed the declared @types/jsdom); package.json / package-lock.json unchanged afterwards
  • npm run build — passed (all workspace dists regenerated)
  • npm run typecheck — passed (exit 0)
  • npx tsc -p integration-tests (warm tree) — passed (exit 0)
  • npx tsc -p integration-tests (cold tree: all 21 package dist dirs hidden, restored afterwards) — zero errors in every source-resolved package (0 userPromptSubmitContext / transcript-replay errors); the only 9 remaining errors are packages/cli imports of the 8 channel packages + @qwen-code/web-templates, which resolve through their exports maps to dist exactly as in packages/cli's own tsconfig (which carries no paths entries for them either), already fail cold on origin/main, and disappear in the warm tree — the regime the required checks use (build before typecheck)
  • npm run lint — passed (exit 0)
  • npm run bundle — passed
  • Integration suite QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli after build + bundle — all 40 files in integration-tests/cli/ covered: 33 passed, 5 self-skip by design (read_many_files, save_memory, stdin-context via describe.skip/it.skip, and the two daemon benchmark files), 2 are excluded from collection by the vitest config (qwen-daemon-loadtest, qwen-daemon-first-output-benchmark), 0 failures attributable to this PR; includes the PR-touched qwen-serve-streaming (5/5), qwen-serve-webui-live-journal-recovery (1/1), file-system, notebook-edit, todo_write, write_file, edit, and sleep-interception (4/4, validating the tightened success === false predicates against live telemetry). Runs were chunked across the identical file set because a single detached run was killed by this container's process-group cleanup between tool steps. The single failing file, qwen-config-dir.test.ts (untouched by this PR), fails only on this container's root-owned $HOME and passes 7/7 with a writable HOME
  • cd integrations/external-context && npx vitest run — passed (exit 0; this workspace's tsconfig is also changed by the PR)
  • npm run generate:settings-schema — not required (no settings source changed)
中文说明

Autofix 本轮结果:无需代码改动(第 2 轮评审意见已验证 / 已回应)

本轮分诊了评审第 2 轮的内容(一条 Critical、一条 Suggestion)以及评审级别的"未审查"说明。无需代码改动:该 Critical 已由当前 head 提交(1f66c987f0)修复,本轮用全新的冷树/暖树证据重新验证;该 Suggestion 按其自身提供的声明选项作了回应。

R2-1(Critical):缺少 @qwen-code/qwen-code-core/userPromptSubmitContext 的 paths 条目——HEAD 已修复,已重新验证

被评审的树(当前 head 之前的合并提交)只有 @qwen-code/qwen-code-core/* 通配符,它把该异名子路径替换为不存在的 packages/core/src/userPromptSubmitContext。当前 head 提交把所有通配符替换为逐子路径的显式条目,其中恰好包含本条意见要求的条目:@qwen-code/qwen-code-core/userPromptSubmitContext../packages/core/src/hooks/user-prompt-submit-context.ts,与该包的 exports 映射一致。本轮取得的新证据:

  • 冷树(隐藏全部 21 个 packages/*/distintegrations/*/dist 目录后又恢复):npx tsc -p integration-tests 报告的 userPromptSubmitContext 错误数为——意见中复现的 transcript-replay.ts(27,58) TS2307 已消失,且 core/sdk/acp-bridge/webui 完全没有任何解析错误。
  • 暖树(npm run build 之后):npx tsc -p integration-tests 退出码 0。
  • 建议代码块中的通配符那一半是刻意不加的:head 提交删除了所有通配符,因为 nodenext 对替换后的路径不做扩展名探测。真正消除错误的是显式条目那一半,而当前显式条目集合对该程序 import 的每个子路径都是完备的。

R2-3(Suggestion):hook-integration 文件不被任何可运行命令执行——不接入;改为声明

不予接入:不把 hook-integration 接入可运行入口。该套件在引入它的那个提交里就被作者刻意排除在执行之外(aa0f04b60a "add doc for hooks and skip integration test");是否复活一个长期跳过的套件属于范围决策,应放在专门的改动里,而不是这个类型检查 PR。

按本条建议提供的另一选项声明:integration-tests/hook-integration/ 下的编辑在设计上仅经类型检查验证。 本 PR 新增的类型检查就是它们唯一的门槛,这与该套件的既有设计一致。另作一处事实更正:本 PR 只编辑了 hooks.test.ts(删除 noUnusedLocals 不接受的未使用变量 _result),没有编辑 hooks-advanced.test.ts

评审说明:"Integration Tests (CLI, No Sandbox) 在 CI 中被跳过"

该跳过是 CI 设计使然:该作业(.github/workflows/ci.yml 中的 integration_cli)以 github.event_name == 'merge_group' 为触发条件,而 main 尚未启用合并队列,所以按 ci.yml 中的注释,它在 PR 推送上从不触发。为仍然覆盖该套件,本轮在 npm run build && npm run bundle 之后于本地运行了它(结果见 Verification):integration-tests/cli/ 中所有被收集的文件全部通过(除 5 个按设计自行跳过的文件,结果见 Verification);唯一的失败是环境性的(见下文),且不涉及本 PR 编辑的任何文件。"反向审计"一条是评审时间预算的说明,不需要任何代码动作。

环境说明(仅限本检出环境——与代码无关)

  • 本检出的 node_modules 相对于分支已提交的 lockfile 是过期的:@types/jsdom 是本分支声明的依赖但此前未安装,导致 qwen-serve-webui-live-journal-recovery.test.ts 在冷树上出现 TS7016。用 npm install 同步后消除(遵守已提交的 lockfile;事后 package.json / package-lock.json 无任何变更)。
  • cli/qwen-config-dir.test.ts 的测试 1d("QWEN_HOME 未设置时 CLI 正常工作")在本机以 EACCES: permission denied, mkdir '/home/github-runner/.qwen' 失败,因为本容器的 $HOME 为 root 所有、测试用户不可写。该文件未被本 PR 改动;用可写的 HOME 重跑 7/7 通过,证明原因是环境性的。CI 中 $HOME 可写,不会复现。

验证(Verification)

  • npm install --cache /tmp/npm-cache-qc --no-audit --no-fund —— 将 node_modules 同步到已提交的 lockfile(安装了声明的 @types/jsdom);事后 package.json / package-lock.json 无变更
  • npm run build —— 通过(所有 workspace 的 dist 重新生成)
  • npm run typecheck —— 通过(退出码 0)
  • npx tsc -p integration-tests(暖树)—— 通过(退出码 0)
  • npx tsc -p integration-tests(冷树:隐藏全部 21 个包 dist 目录后又恢复)—— 所有改为源码解析的包零错误(userPromptSubmitContext / transcript-replay 错误数为 0);仅剩的 9 个错误是 packages/cli 对 8 个 channel 包 + @qwen-code/web-templates 的 import,它们经各自 exports 映射解析到 dist,与 packages/cli 自身 tsconfig 的行为完全一致(cli 也没有为它们配置 paths 条目),在 origin/main 的冷树上同样失败,且在暖树上消失——这正是必跑检查所用的状态(先 build 再 typecheck)
  • npm run lint —— 通过(退出码 0)
  • npm run bundle —— 通过
  • build + bundle 之后运行集成套件 QWEN_SANDBOX=false npx vitest run --root ./integration-tests cli —— 覆盖 integration-tests/cli/ 全部 40 个文件:33 个文件通过,5 个按设计自行跳过(read_many_filessave_memorystdin-context 使用 describe.skip/it.skip,以及两个 daemon 基准测试文件),2 个被 vitest 配置排除而不在收集范围内(qwen-daemon-loadtestqwen-daemon-first-output-benchmark),0 个可归因于本 PR 的失败;其中包括本 PR 改动过的 qwen-serve-streaming(5/5)、qwen-serve-webui-live-journal-recovery(1/1)、file-systemnotebook-edittodo_writewrite_fileeditsleep-interception(4/4,验证了收紧为 success === false 的判定在真实遥测上的行为)。由于单个分离进程运行会被本容器在工具步骤之间的进程组清理杀掉,采用了按同一文件集合分段的运行方式。唯一失败的文件 qwen-config-dir.test.ts(本 PR 未改动)仅因本容器 root 所有的 $HOME 而失败,用可写 HOME 重跑 7/7 通过
  • cd integrations/external-context && npx vitest run —— 通过(退出码 0;本 PR 也修改了该 workspace 的 tsconfig)
  • npm run generate:settings-schema —— 无需运行(未改动任何 settings 源文件)

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 /triage

@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: 45 passed · 0 failed · 45 total

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

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

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

Verification report

PR 8693 verification — fix(integration-tests): make the project typecheckable and fix what that found

Verdict: merge-ready — 45/45 scripted assertions passed, 0 unexpected failures. Verified head: 1f66c987f091e849956229baf872a0354f9e2354 (merge ref e18ef0d004, base tip 4ec0371e61).

中文摘要
  • 结论:merge-ready(45/45 断言通过,0 个意外失败)。
  • A/B 结论:base 上 tsc -p integration-tests/tsconfig.json 以 TS5063 中止(一个文件都没检查);head 上同一命令 0 错误通过,程序含 3050 个文件(见 01-ab-base-ts5063-abort.png / 02-ab-head-gate-clean.png)。中间变体证明两侧都承重:只修配置不修源码仍有 385 个错误(03-…png);还原 write_file.test.ts 的修复会重新报 TS2554(04-…png);植入未声明标识符(integration-tests/ has never been type checked: TS5063 in its tsconfig aborts tsc before it starts #8692 的 bug 类别)报 TS2304(05-…png)。external-context 的 types: ["node"] 覆盖同样承重:去掉后 http-client.ts 复现 TS2504(06-…png)。
  • 运行时:head 上跑通全部 cli/ 集成目录 + channel-plugin:34 文件通过、0 失败(187 测试通过),包括 fix(serve): Allow approved same-host text reads outside workspace #8620 的安全回归测试(现为真绿而非绿色跳过)与收紧后的 sleep-interception 4/4。vitest.config.ts 未被本 PR 触碰,运行时别名不变。
  • Findings:无阻断项。三条低级别观察:① 该 tsc 门未接入 CI(作者显式推迟,我同意拆分但标记为残留风险);② sleep-interception 的 success === false 收紧在 telemetry 缺字段时会静默跳过而非匹配——实际 4/4 全绿缓解;③ @types/jsdom 28.x 配 jsdom 26(DT 无对应版本,作者已说明),唯一使用方测试实跑通过。
  • 未覆盖:每 commit 归因(shallow checkout,4 个 commit 仅 1 个可达);base 侧运行时 A/B;hook-integration / terminal-bench / interactive / sdk-typescript 集成 lane 与 docker/podman 沙箱变体(由 PR 自身 CI 覆盖)。

Central claim + A/B

Central claim: tsc -p integration-tests/tsconfig.json runs at all (base aborts with TS5063 because compilerOptions.paths carried a "//" string key) and completes with 0 errors on head.

Cell Tree Config Sources Observable result Assertion
A (control) base 4ec0371e base base rc=2, TS5063: Substitutions for pattern '//' should be an array, 0 files checked expected red → pass (01-ab-base-ts5063-abort.png)
B (gate) head 1f66c987 head head rc=0, 0 errors; program = 3050 files (105 under integration-tests/) pass, reproduced cold with tsbuildinfo deleted, 13.5 s (02-ab-head-gate-clean.png)
C (intermediate) head base config with only the // key removed head rc=2, 385 errors: 349×TS6307, 21×TS4111, 6×TS7006, 4×TS7031, 4×TS7016, 1×TS2322 config fix alone insufficient → source fixes load-bearing (03-base-config-on-head-sources-385-errors.png)
D1 (live gate) head head head minus the write_file fix rc=2, write_file.test.ts(32,7): TS2554 Expected 0 arguments, but got 1 gate catches regression of a fixed shape (04-vacuity-writefile-revert-caught.png)
D2 (live gate) head head head + planted undeclared identifier rc=2, TS2304 Cannot find name 'UNDECLARED_IDENTIFIER_PR8693_PROBE' the #8692 bug class is now caught (05-vacuity-undeclared-identifier-caught.png)

Cell C reconciliation: the PR's base measured 404 errors; my cell C measured 385. The 21 missing window/WebSocket TS2304s are masked in my tree because the head node_modules' @types/jsdom leaks lib.dom into the program via the inherited types: ["node", "vitest/globals"] chain (verified: lib.dom.d.ts present in the cell-C program file list). 385 + 21 = 406 ≈ 404; the residual delta is main's evolution between the PR's base and 4ec0371e. This leak is the same mechanism the external-context override plugs (below), so cell C doubles as a live demonstration of it.

Secondary A/B — the round-1 types: ["node"] override in integrations/external-context/tsconfig.json (added because the root @types/jsdom addition broke every CI build):

Cell Config Result
EC-B1 head (override present) rc=0, builds clean
EC-B2 probe identical minus the override rc=2, http-client.ts(124,29): error TS2504: Type 'ReadableStream<Uint8Array<ArrayBufferLike>>' must have a '[Symbol.asyncIterator]()' method

The override is load-bearing and the mechanism matches the comment exactly (06-external-context-override-load-bearing.png).

Runtime (no-impact claim)

integration-tests/vitest.config.ts (the runtime aliases onto the built SDK bundle) is byte-identical across the PR (0-line diff), so runtime module resolution is untouched by the paths rework. Head runtime runs (real dist/cli.js, fake OpenAI server, QWEN_SANDBOX=false):

Run Result
full cli/ dir + channel-plugin.test.ts 34 files passed, 5 skipped, 0 failed — 187 tests passed / 18 skipped, 206 s
per changed file channel-plugin 3✓, file-system 6✓(1 skip), notebook-edit 2✓, qwen-serve-streaming 5✓, webui-live-journal-recovery 1✓, sleep-interception 4✓, todo_write 1✓, write_file 1✓; stdin-context ↓ (pre-existing describe.skip)

08-integration-tests-head-live.png captures the run. Notably, qwen-serve-streaming's "same-host external text reads" — the #8620 security regression that motivated this PR — is now a real green, not the green-skip a bare catch {} produced on dev boxes.

The two real defects, proven:

  • write_file.test.ts: a live vitest probe (scratch/message-probe.probe.test.mjs, 3/3, 07-vitest-message-semantics-probe.png) shows expect(false).toBeTruthy('MSG') throws with the message discarded, while expect(false, 'MSG').toBeTruthy() surfaces it; the pass path is unchanged.
  • gif: true scenarios: scenario-runner.ts reads only config.gif (config.gif !== false, line 352; the field is declared on ScenarioConfig, line 86); no step/streaming-level .gif property read exists (the 'streaming.gif' string at line 429 is the output filename). The flag was dead where it sat. Nuance: the runner defaults GIF generation on (!== false), so GIFs were produced anyway — the defect was a dead switch, not missing output; the fix makes the explicit intent effective.

Corrections

None required — the description's claims (TS5063 abort, ~404 exposed errors with the 349/21/4 split, the node-pty exports/types degradation, the runner never reading streaming.gif, the vitest.config runtime aliases staying put) all matched measurement within the documented confounds above.

Findings (low severity)

  1. The new gate is not wired into CI (author's explicit deferral — "a separate decision"). I agree with splitting it from the cleanup, but flag the residual: until a workflow step runs tsc -p integration-tests/tsconfig.json, the 0-error state can rot silently again — which is exactly how the directory went unchecked for its life. Recommend a follow-up wiring PR; the measured cost here is 13.5 s cold on this runner (author's 106 s was their idle box; either is cheap enough to gate on, as the author argues).
  2. sleep-interception predicate tightening changes match semantics: !call.successcall.success === false means a blocked call whose telemetry lacks an explicit success boolean now falls through to the test's warn-and-skip escape hatch instead of matching. This is the type-honest reading of unvalidated telemetry and is documented in-code; the live 4/4 green run shows real telemetry carries explicit booleans, so the quiet-skip risk did not materialize. Informational.
  3. @types/jsdom 28.x against jsdom 26 (DefinitelyTyped has no 26 line). Type drift between runtime and declarations is possible for the one consumer (qwen-serve-webui-live-journal-recovery.test.ts), which ran green here. Accepted, documented tradeoff.

Not covered

  • Per-commit attribution: the checkout is shallow (--is-shallow-repository true; git rev-list HEAD^1..HEAD^2 yields 1 of the 4 commits in the metadata). The aggregate HEAD^1..HEAD diff was verified; individual commit claims were not separable.
  • Base-side runtime A/B: the base tree has no built bundle and building one was out of budget; runtime is verified head-only, with the no-impact claim carried by the untouched vitest.config.ts plus type-level diff review.
  • Excluded lanes: hook-integration/**, terminal-bench/**, interactive/, sdk-typescript/ integration dirs and docker/podman sandbox variants are excluded from the default vitest config or are separate CI jobs; the PR's own CI covers them. The hooks.test.ts and terminal-bench.test.ts edits are type-only and typechecked here.
  • Repo-wide unit tests: not re-run (the PR touches no production source; npm run typecheck over all 10 workspaces ran clean, which is the surface the root @types/jsdom addition could have broken).
  • The author's 1441/1441 sdk-typescript claim is moot at the final head: the round-1 bracket-access rewrites in packages/sdk-typescript/src were reverted; the final diff touches no file in that package.
  • The 106 s cold-run cost claim: environment-dependent (13.5 s here). Not a finding; the cost is acceptable in both regimes.

Methodology

Environment: the CI verify container (node:22-bookworm, merge-ref checkout at depth 2, npm ci + npm run build pre-run at head). All tsc cells ran node_modules/.bin/tsc (TypeScript 5.8.3, shared toolchain — the PR changes no compiler version; the base worktree resolved node_modules by walking up, a confound limited to the added jsdom/@types/jsdom packages, named where it matters in cell C). Incremental-cache shortcuts were ruled out by deleting tsconfig.tsbuildinfo before every gate run. Harnesses: direct tsc -p invocations, scratch tsconfig variants (cell C, EC probe — removed after capture), a vitest probe for expect-message semantics, and real integration runs spawning dist/cli.js against the repo's fake OpenAI server. Raw logs in logs/, captures in evidence/, the rerunnable tally in tally-assertions.mjs (45 checks; fail counts unexpected outcomes only — expected control reds are encoded as passes). Two early tally failures were diagnosed as harness bugs (ANSI escapes in a piped vitest log; a regex matching the 'streaming.gif' filename literal) and fixed; the underlying facts were verified by hand before the rerun.

Evidence images

01-ab-base-ts5063-abort

02-ab-head-gate-clean

03-base-config-on-head-sources-385-errors

04-vacuity-writefile-revert-caught

05-vacuity-undeclared-identifier-caught

06-external-context-override-load-bearing

07-vitest-message-semantics-probe

08-integration-tests-head-live

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 dismissed stale reviews from themself August 8, 2026 02:16

Superseded by re-review of 1f66c98: the @types/jsdom leak is fixed via the "types": ["node"] isolation in integrations/external-context/tsconfig.json, and the two jobs that failed on it (Test ubuntu-latest, web-shell E2E Smoke) build green on the new head.

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

— deepseek-v4-flash via Qwen Code /review (v0.21.7)

@wenshao

wenshao commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

@qwen-code /triage

@qwen-code-ci-bot

qwen-code-ci-bot commented Aug 8, 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.

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

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

Verification report

PR 8693 verification (follow-up round) — fix(integration-tests): make the project typecheckable and fix what that found

Verdict: VERDICT — ASSERTION_TOTALS. Verified head: a8a6000aaddcd7847ea2af443cb1f6f3075ad101 (merge ref 84b573860e, base tip 6ddb0307ac).

This is a follow-up round. The previous round verified head 1f66c987 (review-round-2 state) against base 4ec0371e; the new head a8a6000 adds one commit (review-round-3 fixes) on a moved base. Per protocol every carried measurement below was re-run at the new head, not diffed against the old report.

中文摘要

ZH_SUMMARY

Previous-finding status

# Previous finding Severity Status at new head a8a6000
1 The new tsc gate is not wired into CI (author's explicit deferral) Low Stands. Re-measured: no workflow step runs tsc -p integration-tests/tsconfig.json (grep of .github/workflows shows only vitest runs touching integration-tests/). I still agree with splitting the wiring decision out, but the residual risk — the 0-error state rotting silently, exactly the history this PR fixes — remains until a follow-up wires it.
2 sleep-interception predicate tightening (!call.successcall.success === false) could quiet-skip when telemetry omits success Info Stands, re-measured, did not materialize. Live run at the new head: 4/4 tests pass (SUITE_REF), i.e. real telemetry still carries explicit booleans.
3 @types/jsdom 28.x against jsdom 26 (no matching DT line) Info Stands, accepted tradeoff. Lock re-measured: @types/jsdom@28.0.3 + jsdom@26.1.0; sole consumer (qwen-serve-webui-live-journal-recovery.test.ts, the only from 'jsdom' import) ran green in the live suite.

Central claim + A/B (re-measured at new head)

Central claim: tsc -p integration-tests/tsconfig.json runs at all (base aborts with TS5063 because compilerOptions.paths carried a "//" string key) and completes with 0 errors on head.

Cell Tree/config Observable result Assertion
A (control) base 6ddb0307 rc=2, TS5063: Substitutions for pattern '//' should be an array, 0 files checked expected red → pass (01-ab-base-ts5063-abort.png)
B (gate) head a8a6000 rc=0, 0 errors cold (tsbuildinfo deleted), 15.8 s; program = 3189 files, 106 under integration-tests/ pass (02-ab-head-gate-clean.png)
C (intermediate) base config with only // removed, on head sources rc=2, 362 errors: 327×TS6307, 21×TS4111, 6×TS7006, 4×TS7031, 4×TS7016 config fix alone insufficient → source fixes load-bearing
D1 (live gate) head minus the write_file fix rc=2, exactly one error: write_file.test.ts(32,7): TS2554 Expected 0 arguments, but got 1 gate catches regression of a fixed shape
D2 (live gate) head + planted undeclared identifier rc=2, TS2304 Cannot find name 'UNDECLARED_IDENTIFIER_PR8693_PROBE' the #8692 bug class is now caught

Cell C reconciliation with the previous round's 385: the TS6307 count moved 349 → 327 and the lone TS2322 disappeared because both the base tip and the PR sources advanced between rounds; the invariant (config fix alone leaves hundreds of errors; every non-TS6307 family identical: 21/6/4/4) is unchanged. The 21 window/WebSocket TS2304s the PR's own base saw remain masked here by the root @types/jsdom lib.dom leak through the inherited types chain — the same mechanism the external-context override plugs (below).

Delta this round: build-independence for the channel packages

Round-3 commit claim: the eight builtin channel adapters (imported by cli's channel-registry.ts) and web-templates (imported by cli's html.ts) still resolved through their exports maps to dist, so on a tree without built dists the gate failed with 9×TS2307. The commit maps all nine to source.

A/B in a dist-free state (the nine packages/channels/*/dist + packages/web-templates/dist directories removed from the tree, so resolution cannot fall back to build output):

Cell Config Result
DELTA-control head config minus exactly the 9 round-3 entries (channel-base entry kept) rc=2, exactly 9× TS2307 — one per adapter/web-templates import, matching the maintainer measurement the commit cites (03-delta-distfree-control-9x-ts2307.png)
DELTA-head full head config rc=0, 0 errors, dist-free (04-delta-head-distfree.png)

Program census at head (independent corroborating instrument, --listFilesOnly): 0 workspace dist files in the 3189-file program — the only /dist/ hits are third-party declarations under node_modules. The typecheck now consumes no workspace build output at all.

Delta this round: single channel-base runtime instance

Round-3 claim: channel-plugin.test.ts importing @qwen-code/channel-base by bare specifier (instead of a relative src path) makes the test and plugin-example resolve the same dist/index.js through the exports map — one ChannelBase/SessionRouter instance where round 2's relative-src import silently split two.

Probe Result
Static resolution (import.meta.resolve from both import sites) both resolve to file:///…/packages/channels/base/dist/index.js, same realpath (logs/resolve-identity.txt)
Runtime identity under vitest (probe test, deleted after) 2/2: Object.getPrototypeOf(MockPluginChannel.prototype) === ChannelBase.prototype via the bare import — and the old relative-src import resolves to a distinct prototype, proving the round-2 split was real and the fix is what removed it (05-channel-identity-probe.png)
Live channel-plugin.test.ts passed in the runtime suite (SUITE_REF)
integration-tests/vitest.config.ts byte-identical across the PR (0-line diff) — no runtime alias added or removed; resolution flows through the package exports map as claimed

Also surfaced by the now-live gate in this file: dmPolicy is a required ChannelConfig field (packages/channels/base/src/types.ts:71); the old fixture omitted it — invisible while the directory went unchecked. The PR adds dmPolicy: 'open', which matches the documented runtime default, so runtime behavior is unchanged. The MockPluginConfig retyping restores visibility of the required serverWsUrl.

Corrections

None required this round — the round-3 commit's claims (the 9×TS2307 shape, the exports-map fallback for unmapped subpaths, the instance split) all matched measurement.

Findings

FINDINGS

Not covered

NOT_COVERED

Methodology

METHODOLOGY

Evidence images

01-ab-base-ts5063-abort

02-ab-head-gate-clean

06-external-context-override-load-bearing

07-cellC-base-config-362-errors

08-vacuity-d1-d2-live-gate

09-runtime-suite-under-contention

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

Qwen Code · sandboxed verification

@doudouOUC

Copy link
Copy Markdown
Collaborator Author

Review-thread cleanup for current head a8a6000a:

Feedback Action
SDK daemon source resolution Fixed with explicit source-file mappings.
ACP bridge and divergent core source resolution Fixed with explicit source-file mappings.
Web UI daemon React SDK source resolution Fixed with an explicit source-file mapping.
Channel-base source/dist type identity Fixed with a single package import and source mapping.
Duplicate non-blocking acceptance guard Fixed by using the SDK's canonical type guard.
Missing TSX coverage Fixed by including TSX files in the typecheck.
Undeclared jsdom runtime dependency Fixed by declaring jsdom directly alongside its types.
Conflicting property-access compiler regimes Fixed by relaxing the flag only for the integration-test project and removing production SDK churn.
Optional telemetry matching blocked calls Fixed by requiring an explicit false value.
Web UI property-access follow-up Addressed by the same project-scoped compiler setting.
Divergent user-prompt-submit export path Fixed with an explicit source-file mapping.
Hook-integration test reachability Documented in the PR description as typecheck-only in this PR; adding a runnable entry point remains out of scope.

The PR description was also refreshed to match the current 19-file head, direct jsdom dependency, actual verification evidence, and remaining out-of-scope coverage. No new commit was needed in this cleanup pass.

@qwen-code-dev-bot

Copy link
Copy Markdown
Collaborator

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

Autofix round result: no code changes required — PR #8693

Feedback triaged this round

Everything newer than the last evaluation (2026-08-08T11:31:27Z) was reviewed against the raw PR payloads and the current head a8a6000a:

  1. Automated reviewer review, COMMENTED (@wenshao) — a coverage note only ("Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI"). Its findings ledger is empty ("findings":[]), so it carries no actionable item. The gap it names was subsequently covered by the same account's sandboxed verification run, which built, bundled, and executed the four touched integration test files locally (10/10 tests pass).
  2. Automated reviewer review, APPROVED (@wenshao) — no findings, no changes requested.
  3. Twelve inline comments (@doudouOUC) — verified against the raw review-comment payload: every one is the previous round's own resolution reply on an already-fixed thread ("Thanks — fixed on current head a8a6000…"), each a threaded reply (in_reply_to_id set), not a new finding. Nothing to address.
  4. Sandboxed verification report (@wenshao) — verdict merge-ready, 17/17 scripted assertions passed at head a8a6000a. Its four findings are explicitly non-blocking advisories; dispositions below.
  5. Triage-trigger and verification-running comments — workflow machinery, no action attached.
  6. Failed / still-red checks — none; --conflict false; working tree clean at head a8a6000a.

Advisory findings from the verification report — dispositions

  • [S2] lib: ["DOM", "DOM.Iterable", "ES2023"] is currently redundant — Declined. The verifier's own mutation test (mutation D) showed keeping it is harmless and labeled it "documented intent / insurance… either is fine." The lib list is a deliberate, documented choice in the very tsconfig this PR repairs; removing it now would grow the diff and trigger another review round for zero functional gain. Not worth the diff growth.
  • [S2] Typecheck not wired into CI — Declined as out of scope. The verifier confirms this was deliberately deferred by the author as a separate, explicit decision, and that keeping this PR small was the right call. Recommended as a follow-up issue rather than a change here.
  • [S3] Lockfile entry 0.21.7 → 0.20.1 — Informational only; the verifier confirms it resolves a drift that already existed on main and that npm ci succeeds at head. No action.
  • [S3] Description drift — Already handled: the PR description was refreshed in the previous round to match the current 19-file head, the direct jsdom dependency, the actual verification evidence, and the remaining out-of-scope coverage. Description text is maintained by the workflow, not by commits.

Result

No code changes were made this round and no verification commands were needed (nothing was modified). Head remains a8a6000a with a clean working tree; the automated reviewer has approved, and the independent sandboxed verification reports merge-ready.

中文说明

Autofix 本轮结果:无需代码改动 — PR #8693

本轮分类处理的反馈

对照原始 PR 数据与当前 head a8a6000a,审阅了上次评估(2026-08-08T11:31:27Z)之后的全部内容:

  1. 自动审查者的 COMMENTED 评审(@wenshao —— 仅为覆盖面说明("未审查:build-and-test —— Integration Tests (CLI, No Sandbox) 在 CI 中被跳过")。其 findings 清单为空("findings":[]),不含任何可操作项。它指出的缺口随后已由同一账号的沙箱验证运行覆盖——该验证完成了构建与打包,并在本地执行了被触碰的 4 个集成测试文件(10/10 测试通过)。
  2. 自动审查者的 APPROVED 评审(@wenshao —— 无 findings,未要求任何改动。
  3. 12 条行内评论(@doudouOUC —— 已对照原始 review-comment 数据核实:每一条都是上一轮在已修复线程中发布的解决回复("Thanks — fixed on current head a8a6000…"),且均为线程回复(设置了 in_reply_to_id),不是新 finding。无需处理。
  4. 沙箱验证报告(@wenshao —— 结论为 merge-ready,head a8a6000a 上 17/17 项脚本断言全部通过。其 4 条 findings 均明确标注为非阻塞建议;处置见下。
  5. triage 触发与"验证运行中"评论 —— 工作流机制性内容,无对应操作。
  6. 失败 / 持续飘红的检查 —— 无;--conflict false;head a8a6000a 上工作树干净。

验证报告中的建议性 findings —— 处置

  • [S2] lib: ["DOM", "DOM.Iterable", "ES2023"] 目前冗余 —— 拒绝。验证者自己的变异测试(变异 D)证明保留它无害,并称其为"已声明意图 / 保险……两者皆可"。该 lib 列表是本 PR 修复的 tsconfig 中有意且已声明的选择;现在移除只会增大 diff 并引发新一轮审查,而功能收益为零。不值得为此增加 diff。
  • [S2] 类型检查未接入 CI —— 以超出范围为由拒绝。验证者确认这是作者刻意推迟的独立、明确的决策,且保持本 PR 小巧是正确做法。建议作为后续 issue 跟进,而非在本 PR 中改动。
  • [S3] lockfile 条目 0.21.7 → 0.20.1 —— 仅为信息性说明;验证者确认它解决的是 main 上既有的版本漂移,且 head 上 npm ci 成功。无需操作。
  • [S3] 描述漂移 —— 已处理:PR 描述已在上一轮刷新,与当前 19 个文件的 head、直接 jsdom 依赖、实际验证证据以及剩余的未覆盖范围保持一致。描述文本由工作流维护,不通过提交修改。

结果

本轮未做任何代码改动,因此也无需运行验证命令(没有任何修改)。Head 保持为 a8a6000a,工作树干净;自动审查者已批准(APPROVED),独立沙箱验证结论为 merge-ready

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


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

@yiliang114 yiliang114 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Verified at head a8a6000: the invalid // paths entry is gone and every remaining paths value is a proper array mapping subpaths to source declarations; the two exposed defects are genuine fixes — the GIF switch now sits at scenario level where the runner actually reads config.gif !== false (the old placement on a streaming step was inert), and the failure diagnostic now reaches vitest through the matcher message. The telemetry-shape edits preserve well-formed behavior (call.success === false refuses malformed records rather than false-matching), and nothing under packages/*/src is touched — integration-tests/, one integration tsconfig, and devDependencies only. CI green on this head.

One non-blocking follow-up worth an issue so it doesn't get lost: the restored typecheck isn't wired into any workflow yet, so the same silent rot (invalid paths entry, missing subpath mapping) could recur unnoticed — I see this is deliberately deferred, just make sure it's tracked. Nothing blocks merge.

@doudouOUC
doudouOUC added this pull request to the merge queue Aug 8, 2026
Merged via the queue into QwenLM:main with commit 3037744 Aug 8, 2026
368 of 379 checks passed
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Released in v0.21.8.

qwen-code-dev-bot added a commit that referenced this pull request Aug 8, 2026
Cherry-picks the tsconfig guard from 3037744 (#8693), which postdates
this branch's merge base. With @types/jsdom installed, the inherited
vitest/globals types drag the DOM lib into this program and break the
for-await over response.body in http-client.ts, failing npm run build
and npm run typecheck.
@doudouOUC
doudouOUC deleted the fix/integration-tests-typecheck-v2 branch August 9, 2026 01:06
qwen-code-dev-bot added a commit that referenced this pull request Aug 9, 2026
…bsolete

The `"types": ["node"]` override existed solely to keep @types/jsdom's
lib.dom out of this program while http-client.ts read the response body
with `for await` — the DOM lib's ReadableStream is not async-iterable,
and the flip broke the build with TS2504 (#8693). The reader loop that
replaced the `for await` types identically in every lib set, so the
guard is no longer load-bearing: with it removed, lib.dom re-enters the
program and the package still builds cleanly. Drop it with its stale
comment instead of leaving maintainers two contradicting stories about
whether it is needed.

Also export MAX_RESPONSE_BYTES and import it in the boundary tests
instead of re-declaring it locally, so the tests pin the real constant
rather than a copy that can silently drift.
pull Bot pushed a commit to Stars1233/qwen-code that referenced this pull request Aug 9, 2026
…await (QwenLM#8764)

* fix(external-context): read the response body with a reader, not for-await

Async-iterating a ReadableStream needs [Symbol.asyncIterator] on the
TYPE, and whether it is there depends on which lib set the program
resolves — @types/node's stream has it, the DOM lib's needs
lib.dom.asynciterable. That resolution flipped underneath this file on
2026-08-08: QwenLM#8693 installed @types/jsdom at the root, vitest's types
pull the jsdom types in wherever they exist, and jsdom's carry
/// <reference lib="dom" />. QwenLM#8693 shipped the tsconfig `types` guard in
the same commit, so main stayed green — but the guard travels with the
BRANCH while node_modules travel with the TRUSTED BASE in the autofix
verification build, so every managed branch behind QwenLM#8693 failed that
build with TS2504 on this line. Two legs measured on run 31276008548:
63 minutes of accepted agent work discarded per round, 18 more minutes
burned by a repair step that cannot fix a failure outside the PR's diff
(QwenLM#8614 reached attempt 13 that way; QwenLM#8616 died identically).

Reproduced locally in both directions before changing anything:
@types/jsdom installed + guard removed = the gate's exact error,
character for character; with the reader loop the same poisoned setup
builds clean. The guard stays — belt and suspenders — but the build no
longer depends on it, or on which lib set any future environment
resolves.

Behavior is unchanged and now pinned by tests the file never had:
multi-chunk assembly, the exact MAX_RESPONSE_BYTES boundary (bound is
strictly-greater), invalid-UTF-8 rejection, and the easy one to drop in
this rewrite — cancelling the stream on early exit, which `for await`
did implicitly via iterator return(). Mutation-tested: removing the
cancel fails exactly that test against an endless producer.

The package's other for-awaits iterate process.stdin (a Node stream,
async-iterable in every lib set) and are untouched.

* fix(external-context): await stream cancellation before rejecting the request

On early exit from the reader loop (the oversize throw) cancellation was
started fire-and-forget, so postJson() rejected while the stream's
teardown was still settling — `for await` had awaited its implicit
iterator return() before propagating. An immediate retry could overlap
the previous response transport's unfinished cancellation. Await
reader.cancel() before releaseLock(), and pin the sequencing with a
deferred-cancel regression test that fails against the fire-and-forget
form.

Also cover read() rejecting after a partial chunk was received: the
error maps to the request-did-not-complete transport error rather than
EOF-then-parse of the partial JSON, and the reader lock is still
released.

* fix(external-context): drop the types guard the reader rewrite made obsolete

The `"types": ["node"]` override existed solely to keep @types/jsdom's
lib.dom out of this program while http-client.ts read the response body
with `for await` — the DOM lib's ReadableStream is not async-iterable,
and the flip broke the build with TS2504 (QwenLM#8693). The reader loop that
replaced the `for await` types identically in every lib set, so the
guard is no longer load-bearing: with it removed, lib.dom re-enters the
program and the package still builds cleanly. Drop it with its stale
comment instead of leaving maintainers two contradicting stories about
whether it is needed.

Also export MAX_RESPONSE_BYTES and import it in the boundary tests
instead of re-declaring it locally, so the tests pin the real constant
rather than a copy that can silently drift.

* test(external-context): make the invalid-UTF-8 test pin fatal decoding

---------

Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
qwen-code-dev-bot added a commit to DragonnZhang/qwen-code that referenced this pull request Aug 9, 2026
…enLM#8614)

* feat(web-shell): add fullscreen view for the right artifact panel

The right panel (artifacts / subagents / review) is narrow, so long
previews and transcripts are hard to read. Add a fullscreen toggle to
the panel header that expands the panel to cover the viewport; pressing
Escape or the toggle again shrinks it back to its dock or drawer.

* test(web-shell): capture fullscreen artifact panel in visual previews

The fullscreen toggle and surface are only reachable with the artifact
panel open, so no visual scenario rendered them and the before/after
preview could not see this feature. Extend the code review artifact
scenario to expand the panel fullscreen, capture it, and assert the
Escape restore path.

* fix(web-shell): keep artifact panel mounted across fullscreen toggle (QwenLM#8614)

* fix(web-shell): harden artifact panel fullscreen per review feedback (QwenLM#8614)

* fix(web-shell): guard artifact drawer Escape for IME composition (QwenLM#8614)

* fix(web-shell): scope toast z-index, reset dock animation flag, guard IME keyCode (QwenLM#8614)

Round-4 review fixes for the artifact panel fullscreen:

- Elevate ToastHost above dialog-backdrop-tier surfaces only while the
  fullscreen surface is up (new `elevated` prop); otherwise it stays at its
  original z-index 30 so DialogShell modals keep painting above toasts. The
  old comment rested on DialogShell.module.css's dead `.backdrop` class —
  the live modal path resolves the same host variable with fallback 50.
- Reset suppressArtifactDockOpenAnimation when the dock unmounts (panel
  close or floating drawer taking over) and only set it while docked, so a
  floating<->docked flip after a fullscreen round-trip no longer mounts the
  dock without its slide-in animation.
- Extend the window Escape handler's IME guard to keyCode 229, matching the
  paired guard used everywhere else in the package: a WebKit-shaped IME
  Escape (isComposing false, keyCode 229) previously shrank a fullscreen
  panel and swallowed the native IME cancel.

Adds the suggested coverage: keyCode-229 drawer variant, docked-fullscreen
IME Escape, ask-user shrink keyboardActive history, streaming+fullscreen
Escape ordering, and the floating-interlude dock animation regression.

* fix(web-shell): portal docked fullscreen surface, gate chat shortcuts, clamp z-index (QwenLM#8614)

Round-5 review fixes for the artifact panel fullscreen:

- Clamp the fullscreen surface z-index floor to max(1, backdrop - 10): a
  host setting --web-shell-dialog-backdrop-z-index below 10 gave the
  surface a negative z-index, painting the opaque panel behind the app
  background and blanking the shell.
- Add artifactPanelFullscreen to interactionBlocked: chat-only global
  shortcuts (Ctrl+L/O/Y, Shift+Tab, the btw hotkey) kept mutating the
  hidden chat behind the surface, and the btw capture-phase Escape handler
  dismissed hidden content and swallowed the Escape that shrinks the panel.
- Pad the floating drawer fullscreen content with env(safe-area-inset-*):
  the portaled drawer sits outside the padded app root, so on
  viewport-fit=cover devices the toolbar and Exit control could sit under
  the notch/status bar and bottom content behind the home indicator.
- Move the docked fullscreen surface into the top-level portal root and
  give it document-level modal semantics: a transformed, paint-contained,
  or lower-stacking host ancestor could bound the fixed panel or paint
  over it, and Tab could escape into covered host controls. The panel
  wrapper portals into a display:contents slot that the fullscreen effect
  parks in the portal root, so the SAME node survives the move (panel
  state preserved, React event delegation intact); FocusScope provides the
  Tab containment and the effect hides every outside tree from AT and
  captures stray focus — matching what the floating variant gets from
  vaul's Radix dialog. Declares @radix-ui/react-focus-scope (already in
  the tree via radix-ui) as a direct dependency.
- Portal the elevated ToastHost into the portal root: in shadow-DOM portal
  mode the fullscreen drawer surface is sealed inside the portal host's
  stacking context, so an in-tree toast painted beneath it for its whole
  auto-dismiss lifetime.
- Keep the dock animation suppression flag across dock<->floating
  hand-overs performed mid-fullscreen in both directions, so shrinking
  back to the dock never replays the slide-in on the already-open panel.

Tests: pin both hand-over suppression directions (mutation-verified), the
portal placement, the interaction gate, and toast elevation in the main
fullscreen test; add a colocated ToastHost.test.tsx for the elevated
class; fix the dead 0-measurement step in the docked-width round-trip
test (the 0 is now seeded before entering fullscreen, where the clamp
effect can actually observe it). Adds a drawer-fullscreen visual scenario
at a narrow viewport in both themes, with Escape restoring the drawer.

* fix(web-shell): repair docked fullscreen panel modal semantics (QwenLM#8614)

* fix(web-shell): reset fullscreen state in the panel close commit (QwenLM#8614)

Closing the artifact panel while fullscreen reset the fullscreen flag
only in the passive management effect, so one render committed with the
panel unmounted while the covered shells stayed display:none — one
painted frame of an empty shell before the chat reappeared. Batch the
fullscreen and dock-animation-flag resets into closeArtifactPanel and
the last-tab close so the recovery happens in the same commit.

Adds a regression test asserting the shells are revealed in the close's
committed frame (fails without the batched reset), and pairing coverage
for the drawer Escape pass-through: a plain Escape still closes the
floating drawer when the panel was never fullscreen.

* fix(web-shell): pull escaped focus back into the docked fullscreen surface (QwenLM#8614)

* build(external-context): restore the node-only types override from main

Cherry-picks the tsconfig guard from 3037744 (QwenLM#8693), which postdates
this branch's merge base. With @types/jsdom installed, the inherited
vitest/globals types drag the DOM lib into this program and break the
for-await over response.body in http-client.ts, failing npm run build
and npm run typecheck.

* fix(web-shell): keep docked fullscreen usable in shadow DOM (QwenLM#8614)

Resolve two Critical review findings on the docked fullscreen surface:

- focusin is composed, so in shadow-DOM portal mode the document-level
  pull handler and the Tab-wrap handler saw the shadow host retargeted
  as the target and snapped every focus change back onto the surface.
  Resolve the real node via composedPath() and read the active element
  from the surface's own root.
- The aria-hidden sweep recorded values still owned by a live
  hideOthers lock when the floating drawer unmounted in the same commit
  the docked surface mounted; the deferred restore then re-applied the
  lock's value and permanently hid the shell from assistive tech.
  Record null for lock-owned siblings and let the lock's unlock restore
  the original value.

Adds regression tests for both paths; both fail without their fix.

---------

Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
qwen-code-dev-bot pushed a commit to DragonnZhang/qwen-code that referenced this pull request Aug 10, 2026
…await (QwenLM#8525)

Port of QwenLM#8764 (10621b3) to this branch. Async-iterating a
ReadableStream needs [Symbol.asyncIterator] on the TYPE, and whether it
is there depends on which lib set the program resolves — @types/node's
stream has it, the DOM lib's needs lib.dom.asynciterable. This branch
predates QwenLM#8693, whose tsconfig "types" guard keeps @types/jsdom's
lib.dom out of the package program; the autofix verification build
resolves node_modules from the trusted base (which has @types/jsdom),
so the guardless branch fails the build with TS2504 on the `for await`.

The reader loop types identically in every lib set, so the build no
longer depends on that resolution. Behavior is unchanged and pinned by
the regression tests ported from the same commit: multi-chunk assembly,
the exact MAX_RESPONSE_BYTES boundary, oversize rejection with stream
cancellation, deferred-cancel sequencing, mid-stream read failure, and
invalid-UTF-8 rejection. The Mem0-related changes that share main's
http-client.ts (QwenLM#8507) are intentionally not ported.
pull Bot pushed a commit to bit-cook/qwen-code that referenced this pull request Aug 10, 2026
…eak (QwenLM#8816)

* feat(ci): A/B deterministic gate rejections against the pre-round ref

A deterministic rejection in the autofix verification gate is only
chargeable to the round if the same check passes without the round's
commit. The gate charged every red to the fix unconditionally, and run
31276008548 measured what that costs when the premise is false: PR
8614's branch predated QwenLM#8693's tsconfig guard while node_modules came
from the post-QwenLM#8693 trusted base, so `npm run build` was equally red at
origin/<branch> — 63 minutes of accepted agent work discarded, an
18-minute repair burned on a failure the repair agent is forbidden to
touch (it may only amend the round's own fix), thirteen rounds in a
row, and the same again on the QwenLM#8616 leg.

On rejection the gate now re-runs the failing check at origin/<branch>
(the branch as pushed, before the round) in the same environment:

- baseline green: today's path exactly — outcome=failed,
  retryable=true, the repair pass gets its chance.
- baseline red too: outcome=failed with preexisting=true and NO
  retryable. The repair step keys on retryable and is skipped — it
  cannot reach a failure outside the round's diff by construction —
  and gate-rejection.md says outright that the branch needs a base
  update (merge main), which flows into the failure comment as-is.

Fail-closed toward today's semantics: any A/B infrastructure problem
(missing ref, checkout failure) charges the fix as before, and a
restore failure after the baseline run rejects outright since the tree
can no longer be trusted. The round's work is still not pushed — this
changes the verdict's honesty and cost, not the push policy.

Tested by executing the real script in a real two-remote git repo with
an npm stub whose failures are keyed by commit SHA: round-caused red
(baseline green), pre-existing red (both red), and the untouched green
path. Mutation-tested, 3 of 3 caught: skipping the A/B, claiming
pre-existing without measuring, and dropping the tree restore.

* Address review: bound the A/B to checks it can honestly compare

All seven findings verified before fixing; the three Criticals were
each a way the A/B compared something other than the check that failed.

R1-1 — the contracts check feeds on stdin, which its first run drains;
the baseline leg re-ran against EOF and checked an empty file list.
R1-3 — the schema check's verdict rides on packages/core/dist, which
the core-rebuild guard built from ROUND sources and which, being
gitignored, survives the detach. Both checks are now A/B-exempt
(run_check_no_ab): their baseline verdicts prove nothing, and their
rejections stay where the repair agent can actually act on them.

R1-2 — a workspace the round ADDS does not exist at the baseline, and
npm exits 1 there with "No workspaces found" (measured; --if-present
forgives a missing script, not a missing workspace) — a round-caused
failure misread as pre-existing, skipping the one repair that can fix
the round's own package. The per-package loop now A/Bs only when the
workspace exists at origin/<branch>.

R1-4 — a chatty PASSING baseline used to flood the tail -c 3000
evidence window and push the actual failure text out of
gate-rejection.md, the sole carrier into the repair feedback, the PR
comment, and the next round's LAST_REJECTION. The baseline transcript
now goes to a side log and only a FAILING tail is merged back, where it
is the evidence.

R1-5 — the pre-existing paragraph pushed gate-rejection.md past the
report's head -c 3500 cap, truncating the closing fence for branch
names past 44 characters. Cap raised to 3900, invariant comment
updated with the new arithmetic.

R1-6 — preexisting=true had no read site. It now flows verify →
Finalize verification → the failure report, whose headline swaps the
generic gate clause for "PRE-EXISTING failure … needs a base update
(merge main)".

R1-7 — the no-round-commit guard was unpinned (deleting it kept all
tests green). Now exercised through the core-rebuild path, the one
A/B-eligible check that runs before the commit gate.

Four new behavioral scenarios (chatty baseline, no-commit round,
A/B-exempt checks, round-added workspace) plus workflow pins for the
forwarding, the clause, and the cap. Mutation-tested, 4 of 4 caught:
schema back to A/B (3 tests), guard dropped, side log reverted,
no-commit guard dropped.

* Address review round 2: A/B only what it can prove, prove what it claims

Ten findings across two rounds, each verified before fixing. The three
deepest share one lesson: the A/B is only sound for a check whose
inputs travel entirely with the git ref, and whose failure it can
IDENTIFY, not merely observe.

R2-1 — rc=1 at both legs does not make them the same failure: the
branch can fail for reason A while the round fails for reason B, and a
baseline infrastructure hiccup is a nonzero exit too. Pre-existing now
requires a MATCHING failure identity — tsc diagnostics normalized to
file + error code (positions shift with the round's edits), compared
via comm(1) on a per-check transcript. No diagnostics on either side
means identity cannot be established and the round stays charged.

R2-2 / R2-7 — gitignored dist survives the detach carrying the ROUND's
build, so any dist-consuming check A/Bs reverted sources against
round-built artifacts: package tests (channel-base resolved through
dist exports) and typecheck (sdk-typescript resolves core's d.ts —
probe-verified three-arm flip). Both are now A/B-exempt, as is lint,
leaving `npm run build` — the incident class, and the one check that
rebuilds its own inputs from the checked-out sources — as the sole A/B
candidate. The workspace-existence guard dissolves with it.

R2-3 — the fixture inherited the caller's global git config; a failing
global pre-commit hook broke all seven cases. The harness now isolates
GIT_CONFIG_GLOBAL/SYSTEM for every git child, and the suite is proven
green under a deliberately hostile hooksPath.

R2-4 — Finalize verification now selects preexisting from the same
attempt whose outcome it selects (repair verification included).

R2-5 / R2-8 — the "merge main" advice is now conditional at both
layers: the script paragraph states the measured fact and hedges the
remedy; the report headline uses the compare the step already ran —
behind/diverged gets the base-update clause, an up-to-date branch is
told its own pre-round code needs attention.

R2-6 — the rejection document now sizes its evidence tail against its
preamble (floor 500 bytes, total under the 3900-byte render cap), so
the closing fence can no longer be truncated off by a long branch name.

R2-9 — dissolved by R2-2: package tests no longer A/B, the guard and
its uncovered positive branch are gone.

R2-10 — the baseline-evidence merge is now pinned: the pre-existing
scenario asserts the baseline leg's own failure line (keyed by its SHA)
reaches gate-rejection.md.

Eight behavioral scenarios; mutation-tested 5 of 5: identity dropped,
typecheck re-enrolled, package tests re-enrolled, evidence merge
dropped, fixed tail restored.

* Address review round 4: sharpen identity, stage the git failures, sync prose

Nine findings, all refinements — the design held, the edges did not.

Identity now keeps the diagnostic MESSAGE (file + code collide: two
unrelated TS2339s in one file compared equal, skipping a repair that
could have shipped — probe-reproduced by the review), and the fixture
emits a SHIFTED position on the baseline leg so the position strip is
load-bearing instead of decorative (deleting the sed survived every
test before; it fails one now). vite/esbuild failures still yield an
empty signature by design — documented as the fail-closed limit rather
than half-widened.

The fail_signature assignments take `|| true`: grep exits 1 on the
normal no-match case and survives errexit today only because the caller
sits in an if-condition — a future unconditional call site would crash
the gate verdict-less.

The restore-failure branch is now stageable and staged: the baseline
leg recreates (untracked) a file the branch tracks, the checkout back
refuses, and the test pins retryable-not-preexisting with the
'could not restore' label. Relaxing the branch to `|| true` fails it.

Prose synced to the mechanisms that replaced it: the render-cap
invariant restates against the dynamic tail budget (the old 3000-based
arithmetic would misguide the next retune), the no-round-commit guard
comment names the core rebuild (schema/contracts left the A/B last
round), the describe wording counts both A/B-eligible builds, and the
pre-existing clauses no longer claim "the repair pass was skipped" —
with REPAIR_PREEXISTING forwarded, repair may have RUN; they now state
the invariant that is true either way: repair may only amend the
round's own fix, so it cannot reach this failure.

Mutation-tested, 3 of 3 caught: position strip dropped, message dropped
from the identity, restore rejection relaxed.

* fix(ci): watchdog silent sandbox hangs and reap the containers they leak

Four autofix rounds have died the same way (QwenLM#8663 twice, QwenLM#8761 r3,
QwenLM#8763 r4): the agent's last output is the sandbox wrapper's
"ContainerName (regular): …" line at docker container entry, then
nothing — not one event — until the 2-hour absolute budget kills the
round. Four different runners, two image versions: systemic, not a bad
machine. Where exactly the container wedges is still unknown (that
needs docker state on the runner); what is certain from the logs is the
shape — a wedged sandbox produces NOTHING, and a legitimate run is
never silent for long (the fleet's longest tolerated quiet is the
review pipeline's 10-minute stream-idle window for thinking phases).

Two mitigations, each aimed at a measured half of the damage:

- run-agent.mjs gains an idle watchdog (QWEN_IDLE_TIMEOUT_MS, default
  20 minutes = 2x that longest legitimate silence): zero output for the
  window kills the agent with a distinct "idle-timeout … the sandbox
  likely hung at startup" detail, so the failure comment names the
  right knob and a hung round costs 20 minutes instead of 120. Polled,
  not reset-per-chunk — a busy stream should not spend its time
  re-arming timers.

- Both sandboxed jobs reap stale qwen-code-* containers at job start:
  a budget kill reaps the HOST-side docker client, not the container,
  so every killed sandbox keeps running on the persistent runner —
  observed directly when a later leg's container-name counter found
  qwen-code-0.21.8-0 already occupied and picked -1. One job per runner
  at a time makes any container alive at job start stale by definition.

Tested by executing the real run-agent.mjs end to end with stub agents:
the hang shape (one line, then silence) dies at the idle window naming
the idle limit, and a slow-but-talking agent that outputs every 400ms
across a 1500ms window survives to a clean exit — the test that
distinguishes a watchdog from a disguised absolute timer. Mutation-
tested, 3 of 3 caught: watchdog disabled, last-output tracking dropped
(the disguised-timer regression), cleanup dropped from a job.

* Address review round 5: the gate's verdict defects and the reaper's live kill

Budget-warning round — the five Criticals from both reviewers, no
suggestions (each deferred with a recorded reply).

fail_signature: `[^\n]*` in an ERE bracket expression does not mean
"rest of line" — in POSIX bracket expressions `\` is literal, so it
matched "neither backslash nor the letter n" and truncated every tsc
message at its first n. Nearly every real message has an early n
("Cannot find name", "is not assignable"), so distinct same-file
failures collapsed into identical signatures and a round-caused failure
could be labeled pre-existing, skipping the repair. grep is
line-oriented: `.*` is exactly the rest of the line. New fixture: two
messages differing only after their first n.

Pre-existing verdict: the intersection test mislabeled in both
directions. A round that ADDS a diagnostic sharing one normalized line
with the baseline was called pre-existing (repair skipped for a
round-caused, repairable failure); and `comm -12 | grep -q` under
`set -eo pipefail` SIGPIPEs comm (exit 141) once the shared output
outruns the pipe buffer, charging true pre-existing failures to the
round — the exact 18-minute repair waste the gate exists to kill.
Pre-existing now means the round's failing set is a SUBSET of the
baseline's, and the difference is captured before testing. New fixture:
a round adding a second diagnostic to a failing baseline.

Restore failure after the baseline leg: was retryable=true with HEAD
still detached at the baseline commit — the repair agent works in that
very checkout and does no git recovery, so its commit would land on the
baseline and be orphaned. Now rejected non-retryable (reject_fix grows
a third arg); the next round starts clean from the trusted checkout.
The restoreClash test pins the new semantics.

Stale-container reap: the premise "a runner runs one job at a time, so
any live qwen-code-* container is stale" holds per runner registration,
but the filter queries the docker daemon, which is per host — and this
pool runs several registrations on one OS. With per-issue/PR
serialization only, a concurrent job's sandbox is a substring match
away from `docker rm -f`. The reap now takes only provably-dead
containers (--filter status=exited/dead, both jobs) and the comment
says why a running one is left alone.

Preamble printf: the `\`` escapes sat inside a single-quoted format
where backslash is literal, so every pre-existing rejection rendered
raw backticks instead of code spans (shellcheck SC2016). Backticks
need no escaping there. Also syncs the side-log comment to the dynamic
tail_budget it actually renders.

Verified: scripts suite 140/140 (was 138; the two new fixtures and the
rewritten restoreClash test all fail against the pre-fix script),
npm run build / typecheck / lint pass, bash -n clean.

* Address review round 6: reap the kill's own orphan, tolerate the reaper

* Address review: hang-bound the reaper, unblock the kill path, pin the unpinned arms

- Wrap every docker call in the stale-container reap with timeout 30: an
  alive-but-wedged daemon blocks docker ps indefinitely, and the existing
  || guards only catch nonzero exits, not hangs (R3-1).
- Make the kill-path container removal async in run-agent.mjs: the
  spawnSync blocked the event loop between SIGTERM and the 10s SIGKILL
  backstop for up to its 30s timeout — in exactly the wedged-daemon
  scenario the watchdog exists for. The main flow awaits the removal so
  the leak warning stays deterministic (R3-6).
- Split the pre-existing gate clause for an empty CMP_R: a transient
  compare-API failure is "never measured", not "measured not-behind", and
  must not assert the branch's own code is at fault (R3-7).
- Swap the timeout breaker's closing remedy to the sandbox investigation
  when every counted timeout was idle, mirroring the round-level split
  (R3-11).
- Tests: pin the budget kill path separately from the idle kill path
  (R3-3), parameterize the idle-window parse guard over -1/0/NaN (R3-5),
  add a stderr-only liveness case (R3-12), pin the strict-subset A/B arm
  via a baseline-superset fixture knob (R3-15), and pin the breaker's
  current-round idle increment (R3-18).

---------

Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-ci-bot <qwen-code-ci@service.alibaba.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
qwen-code-dev-bot added a commit that referenced this pull request Aug 11, 2026
…await (#8764)

* fix(external-context): read the response body with a reader, not for-await

Async-iterating a ReadableStream needs [Symbol.asyncIterator] on the
TYPE, and whether it is there depends on which lib set the program
resolves — @types/node's stream has it, the DOM lib's needs
lib.dom.asynciterable. That resolution flipped underneath this file on
2026-08-08: #8693 installed @types/jsdom at the root, vitest's types
pull the jsdom types in wherever they exist, and jsdom's carry
/// <reference lib="dom" />. #8693 shipped the tsconfig `types` guard in
the same commit, so main stayed green — but the guard travels with the
BRANCH while node_modules travel with the TRUSTED BASE in the autofix
verification build, so every managed branch behind #8693 failed that
build with TS2504 on this line. Two legs measured on run 31276008548:
63 minutes of accepted agent work discarded per round, 18 more minutes
burned by a repair step that cannot fix a failure outside the PR's diff
(#8614 reached attempt 13 that way; #8616 died identically).

Reproduced locally in both directions before changing anything:
@types/jsdom installed + guard removed = the gate's exact error,
character for character; with the reader loop the same poisoned setup
builds clean. The guard stays — belt and suspenders — but the build no
longer depends on it, or on which lib set any future environment
resolves.

Behavior is unchanged and now pinned by tests the file never had:
multi-chunk assembly, the exact MAX_RESPONSE_BYTES boundary (bound is
strictly-greater), invalid-UTF-8 rejection, and the easy one to drop in
this rewrite — cancelling the stream on early exit, which `for await`
did implicitly via iterator return(). Mutation-tested: removing the
cancel fails exactly that test against an endless producer.

The package's other for-awaits iterate process.stdin (a Node stream,
async-iterable in every lib set) and are untouched.

* fix(external-context): await stream cancellation before rejecting the request

On early exit from the reader loop (the oversize throw) cancellation was
started fire-and-forget, so postJson() rejected while the stream's
teardown was still settling — `for await` had awaited its implicit
iterator return() before propagating. An immediate retry could overlap
the previous response transport's unfinished cancellation. Await
reader.cancel() before releaseLock(), and pin the sequencing with a
deferred-cancel regression test that fails against the fire-and-forget
form.

Also cover read() rejecting after a partial chunk was received: the
error maps to the request-did-not-complete transport error rather than
EOF-then-parse of the partial JSON, and the reader lock is still
released.

* fix(external-context): drop the types guard the reader rewrite made obsolete

The `"types": ["node"]` override existed solely to keep @types/jsdom's
lib.dom out of this program while http-client.ts read the response body
with `for await` — the DOM lib's ReadableStream is not async-iterable,
and the flip broke the build with TS2504 (#8693). The reader loop that
replaced the `for await` types identically in every lib set, so the
guard is no longer load-bearing: with it removed, lib.dom re-enters the
program and the package still builds cleanly. Drop it with its stale
comment instead of leaving maintainers two contradicting stories about
whether it is needed.

Also export MAX_RESPONSE_BYTES and import it in the boundary tests
instead of re-declaring it locally, so the tests pin the real constant
rather than a copy that can silently drift.

* test(external-context): make the invalid-UTF-8 test pin fatal decoding

---------

Co-authored-by: verify <verify@local>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
qwen-code-dev-bot added a commit to ZevGit/qwen-code that referenced this pull request Aug 12, 2026
* fix(core): resolve Qwen 3.8 reasoning budget conflicts

* fix(core): cover unconfigured Qwen 3.8 conflicts

* chore: preserve latest main formatting

* fix(core): harden DashScope thinking precedence

* fix: honor DashScope thinking knob precedence

* test(core): assert same-layer thinking knob drop warning for request pairs (QwenLM#8525)

* fix: align effort override reporting with wire resolution

* fix: resolve thinking knob review findings

* fix: sort Python SDK test imports

* fix(core): ignore null thinking knobs

* fix(core): register enable_thinking true in thinking knob selection (QwenLM#8525)

selectFromLayer only registered enable_thinking === false, so a
higher-priority enable_thinking: true was invisible to cross-layer
resolution: a lower-priority samplingParams disable won selection and
rewrote the shipping tier to reasoning_effort 'none', inverting the
documented extra_body > samplingParams precedence. Register the
on-switch as the weakest knob in its own layer (an off-switch rewrites
the tier, an on-switch never does) and make the drop branch
value-aware: true keeps the shipping tier and drops only the redundant
knobs, false keeps the canonical 'none' disable.

getReasoningEffortOverride no longer reports an on-switch as shadowing
the tier (the wire drops the switch and ships the tier), except for a
request-level effort override that still shadows from under it.

Also corrects the dropConflictingThinkingKnobs contract comment (only
effort tiers ship alone; the 'none' disable and a winning budget keep
a co-present enable_thinking) and the model-providers.md precedence
callout, which overstated samplingParams precedence for older qwen
hybrids where the reasoning-derived enable_thinking: true overrides it.

* fix(core): preserve budget beneath thinking on-switch

* fix(core): canonicalize disabled thinking knobs

* fix: resolve round-6 thinking knob review findings (QwenLM#8525)

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

* fix(external-context): read the response body with a reader, not for-await (QwenLM#8525)

Port of QwenLM#8764 (10621b3) to this branch. Async-iterating a
ReadableStream needs [Symbol.asyncIterator] on the TYPE, and whether it
is there depends on which lib set the program resolves — @types/node's
stream has it, the DOM lib's needs lib.dom.asynciterable. This branch
predates QwenLM#8693, whose tsconfig "types" guard keeps @types/jsdom's
lib.dom out of the package program; the autofix verification build
resolves node_modules from the trusted base (which has @types/jsdom),
so the guardless branch fails the build with TS2504 on the `for await`.

The reader loop types identically in every lib set, so the build no
longer depends on that resolution. Behavior is unchanged and pinned by
the regression tests ported from the same commit: multi-chunk assembly,
the exact MAX_RESPONSE_BYTES boundary, oversize rejection with stream
cancellation, deferred-cancel sequencing, mid-stream read failure, and
invalid-UTF-8 rejection. The Mem0-related changes that share main's
http-client.ts (QwenLM#8507) are intentionally not ported.

* fix(sdk-python): expose effort status reason from CLI (QwenLM#8525)

The CLI emits a human-readable reason on effort_status and the
TypeScript SDK surfaces it, but the Python EffortStatus TypedDict and
_parse_effort_status dropped it, leaving Python callers to reconstruct
the reason from override. Add reason as an optional field and pass it
through, mirroring the TypeScript parser.

* test(core): add direct unit tests for selectDashScopeThinkingKnob (QwenLM#8525)

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

---------

Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev-bot@users.noreply.github.com>
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+) review/self-reported The linked issue was opened by the PR author (self-reported)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

integration-tests/ has never been type checked: TS5063 in its tsconfig aborts tsc before it starts

5 participants