Skip to content

fix(ci): restore Windows test baseline - #11787

Merged
yiliang114 merged 12 commits into
mainfrom
codex/fix-windows-ci-baseline
Sep 14, 2026
Merged

yiliang114 merged 12 commits into
mainfrom
codex/fix-windows-ci-baseline

Conversation

@yiliang114

@yiliang114 yiliang114 commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

Restores the Windows CI baseline by making runtime paths and filesystem identity checks portable, keeping POSIX-only permission and filesystem semantics out of Windows assertions, and installing the tokenizer's supported WASI fallback when the native Windows binding cannot load.

It also configures the isolated test home consistently for Git line endings and updates Windows-only fixtures to use standard file-URL conversion and deterministic missing-environment values while preserving the behavior each test is intended to cover.

Why it's needed

The self-hosted Windows lane completed setup and dependency installation but failed across multiple workspaces. The failures came from a small set of shared portability assumptions: raw drive paths passed to Node's ESM loader, omitted TEMP values that Windows libuv restores from the parent environment, lossy numeric inode comparisons on NTFS, POSIX-only path, permission, symlink, and deleted-working-directory fixtures, and an unavailable native tokenizer DLL with no installed fallback. This left the scheduled Windows baseline continuously red and unable to provide useful regression signal.

Reviewer Test Plan

How to verify

Dispatch the Windows self-hosted runner validation workflow for this branch and confirm that dependency installation succeeds, the complete workspace and root-script test command runs to completion, and the former code-mode, Live tokenizer, filesystem identity, path, UI shortcut, and fixture failures are absent. On a non-Windows host, confirm that the focused filesystem, code-mode, Live, ACP bridge, CLI, and VS Code companion suites continue to pass.

Evidence (Before & After)

Before: the scheduled Windows job failed after setup with failures across ACP Bridge, CLI, Core, Qwen Live, and the VS Code companion: https://github.com/QwenLM/qwen-code/actions/runs/34713863264

After: the manually dispatched Windows validation passed at d7b8b97c68f8 in 23m16s. Setup, dependency installation, the complete npm run test:ci command, report generation, and cleanup all succeeded; the root-script segment alone completed 75 files and 1,522 tests with no failures: https://github.com/QwenLM/qwen-code/actions/runs/34806714807

Tested on

OS Status
🍏 macOS
🪟 Windows
🐧 Linux

Environment (optional)

macOS local development checkout with Node.js 22; Windows x64 self-hosted ECS runner through the repository validation workflow; Ubuntu hosted runner through the normal PR checks.

Risk & Scope

  • Main risk or tradeoff: Windows skips only assertions that depend on POSIX semantics or inode values the platform cannot represent safely; the supported WASI tokenizer fallback adds a small dependency footprint.
  • Not validated / out of scope: Windows ARM64 was not exercised.
  • Breaking changes / migration notes: None.

Linked Issues

No issue is closed by this PR. One is filed out of it: #11848 tracks two pre-existing
production gaps that this PR's Windows skips removed the only CI signal for
(isSameFile failing open on a hard link, and the deletion journal's swap detection
going inert on 64-bit NTFS file ids). Both are unchanged code, so they are tracked
rather than fixed here; the gates now say so at the code site.

中文说明

本 PR 做了什么

恢复 Windows CI 基线:让运行时路径和文件系统身份校验具备跨平台兼容性,不再在 Windows 上断言仅适用于 POSIX 的权限与文件系统语义,并在 Windows 原生分词器无法加载时安装其官方支持的 WASI 回退实现。

同时统一隔离测试 HOME 下的 Git 换行符配置,并将仅在 Windows 失败的测试夹具改为使用标准 file URL 转换和确定性的环境变量缺失值,同时保留每个测试原本要覆盖的行为。

为什么需要

自托管 Windows 流水线能够完成环境准备和依赖安装,但随后在多个 workspace 中失败。根因集中在少量共享的跨平台假设:把盘符路径直接传给 Node ESM loader、假定省略的 TEMP 不会被 Windows libuv 从父环境补回、在 NTFS 上用有精度损失的 number 比较 inode、依赖仅适用于 POSIX 的路径、权限、符号链接和已删除工作目录夹具,以及原生分词器 DLL 不可用且未安装回退实现。这使定时 Windows 基线持续失败,无法提供有效的回归信号。

Reviewer 测试计划

如何验证

为本分支触发 Windows 自托管 runner 验证工作流,确认依赖安装成功、完整 workspace 与根级脚本测试运行结束,并且此前的 code mode、Live 分词器、文件身份、路径、UI 快捷键和测试夹具失败均不再出现。在非 Windows 主机上,确认文件系统、code mode、Live、ACP Bridge、CLI 和 VS Code companion 的相关测试继续通过。

证据(修复前后)

修复前:定时 Windows 任务在环境准备后失败,ACP Bridge、CLI、Core、Qwen Live 和 VS Code companion 均有失败:https://github.com/QwenLM/qwen-code/actions/runs/34713863264

修复后:手动触发的 Windows 验证在提交 d7b8b97c68f8 上以 23m16s 全部通过。环境准备、依赖安装、完整 npm run test:ci、报告生成和清理均成功;其中根级脚本阶段共运行 75 个测试文件、1,522 个用例且无失败:https://github.com/QwenLM/qwen-code/actions/runs/34806714807

已测试平台

OS 状态
🍏 macOS
🪟 Windows
🐧 Linux

环境(可选)

macOS 本地开发检出,Node.js 22;Windows x64 自托管 ECS runner,通过仓库验证工作流运行;Ubuntu 托管 runner,通过常规 PR checks 运行。

风险与范围

  • 主要风险或取舍:Windows 仅跳过依赖 POSIX 语义或平台无法安全表示的 inode 值的断言;官方 WASI 分词器回退会增加少量依赖体积。
  • 未验证 / 范围外:未验证 Windows ARM64。
  • 破坏性变更 / 迁移说明:无。

关联 Issue

本 PR 不关闭任何 issue,但从评审中提交了一个:#11848 跟踪两个既有的生产缺陷——本 PR 的 Windows 跳过移除了它们唯一的 CI 信号(isSameFile 对硬链接向失败方向敞开,以及删除日志的替换检测在 64 位 NTFS 文件 id 上失效)。两者都是未改动的既有代码,因此只做跟踪、不在本 PR 内修复;门禁处已就地写明。

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

⚠️ Qwen Triage ended earlyview run. It stopped before finishing; check the run log.

⚠️ Qwen Triage 提前结束 —— 查看运行。未跑完,请查看运行日志。

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Thanks for taking on the Windows lane — a scheduled baseline that has been red for days is exactly the kind of thing that quietly costs everyone regression signal.

Template ✓ — every required section is there, bilingual, and the Risk & Scope block actually names the tradeoffs instead of waving at them.

Problem — observed, not theoretical, and I checked rather than taking the description's word for it. Run 34713863264 really is Qwen Code CI, event=schedule, head_branch=main, conclusion=failure. So the premise holds: the Windows baseline on main is red.

Direction — aligned. Restoring CI signal is maintenance of the thing that protects everything else, and none of this reaches auth, model selection, telemetry, release, or a public contract. Two caveats I'd want on the record, because the description undersells both:

  • packages/qwen-live/src/proactive/monitor-debug-store.ts is production code, not a test assertion. privateDirectory stops checking mode & 0o077 and the uid match on Windows, so the "reject a shared or foreign-owned debug root" guard is gone there. The PR body says Windows "skips only assertions that depend on POSIX semantics" — this one isn't an assertion. I think relaxing it is defensible (Node can't read ACLs portably, and 0o40777-style synthesized modes made the check throw unconditionally on Windows), but it's a real reduction in defense-in-depth that deserves to be stated as such rather than filed under test portability.
  • @node-rs/jieba-wasm32-wasi lands in dependencies, not optionalDependencies, and pulls @emnapi/core@2.0.0-alpha.4 and @emnapi/runtime@2.0.0-alpha.4 into the production tree on every platform. Linux and macOS users install an alpha-versioned WASI tokenizer chain they will never load, to fix a Windows-only gap. Was optionalDependencies (or gating on os/cpu) considered?

Size — core paths are touched (packages/core/src/**, packages/cli/src/services/**, and a cross-package spread over five packages), so I ran the numbers. Title type is fix, not refactor, so the Stage 0 hard block doesn't apply. Of 516 changed lines: ~78 production logic, ~298 test, ~140 lockfile. Comfortably under the 500-line escalation threshold. 39 files looks broad but only ~11 are non-test, and the test edits are a uniform portability sweep rather than depth — breadth without size, so no escalation on that axis either.

Approach — the portability work itself is mostly disciplined and I want to call out what's good: Number.isSafeInteger(inode) || inode <= 0 skip guards instead of deleting inode assertions, differentIdentity() / stat.ino === 1 ? 2 : 1 because +1 on a lossy NTFS float is a no-op that would silently stop perturbing anything, BigInt() coercion in assertSameIdentity so mixed number/bigint shapes still compare correctly, and pathToFileURL(require.resolve('tsx')) for the --import specifier. That last one is a genuine product bug fix, not a test accommodation — a raw C:\... path handed to the ESM loader throws ERR_UNSUPPORTED_ESM_URL_SCHEME. git config --global core.autocrlf false is also safely contained: the job exports HOME/USERPROFILE to ${{ runner.temp }}/qwen-ci-home, so --global writes to the throwaway CI home, not the runner's.

Three things I'd push back on, none of them large:

  1. artifact-snapshots.ts hoists await fs.readdir(references) out of the short-circuit, so it now runs unconditionally — including when operationId is set, where it previously never ran. I traced it and it's close to inert (snapshotDirectory already realpath'd references, and a missing dir would have thrown the same ENOENT from the following writeFile down the identical catch path), but that's precisely why I'd drop it: it costs an extra syscall on the owned-restore path, has no test change alongside it, and nothing in the description ties it to a Windows failure. Which failure was it fixing?
  2. run-qwen-serve.test.ts changes hostname: '0' to '0.0.0.0', which makes the assertion vacuous — details in the code review comment.
  3. The pnpm-workspace.yaml minimumReleaseAgeExclude addition breaks an existing repo test — this is the blocker, also in the code review comment.

Risk — no elevated risk signals: the Stage 1e high-risk path scan matched nothing in the production file set (the acp-integration hit is a .test.ts file, which the scan excludes by design).

One structural note worth flagging, since it shapes how much of this anyone can actually verify: Test (windows-latest) and Test (macos-latest) are both skipped on normal PR CI, and Test (ubuntu-latest) runs test:ci:workspaces, which excludes test:scripts. So the only lane in this PR that executes the repo-level script tests is the dispatched Windows validation run — still in flight. That's how a broken repo test can sit behind an all-green-looking check list.

Moving on to code review. 🔍

中文说明

感谢接手 Windows 流水线——定时基线连续红了好几天,正是那种会悄悄让所有人失去回归信号的问题。

模板 ✓ ——必填小节齐全、双语,Risk & Scope 也真实写出了取舍,而不是含糊带过。

问题——是已观测到的,不是理论性的,而且我核实过,没有只采信描述。Run 34713863264 确实是 Qwen Code CIevent=schedulehead_branch=mainconclusion=failure。所以前提成立:main 上的 Windows 基线是红的。

方向——对齐。恢复 CI 信号是在维护那个保护其他一切的东西,而且这些改动都没有触及 auth、模型选择、telemetry、发布或对外契约。有两点希望明确记录,因为描述对两者的说法都偏轻:

  • packages/qwen-live/src/proactive/monitor-debug-store.ts生产代码,不是测试断言。privateDirectory 在 Windows 上不再检查 mode & 0o077 和 uid 匹配,因此"拒绝共享或属主异常的 debug 根目录"这道防护在 Windows 上消失了。PR 正文说 Windows "只跳过依赖 POSIX 语义的断言"——这一处不是断言。我认为放宽是可以接受的(Node 无法跨平台读取 ACL,而合成出来的 0o40777 之类 mode 会让该检查在 Windows 上无条件抛错),但这是纵深防御的实质削弱,应当如实说明,而不是归入测试可移植性。
  • @node-rs/jieba-wasm32-wasi 落在 dependencies 而非 optionalDependencies,并把 @emnapi/core@2.0.0-alpha.4@emnapi/runtime@2.0.0-alpha.4 带进了所有平台的生产依赖树。Linux 和 macOS 用户会装上一条他们永远不会加载的 alpha 版 WASI 分词器依赖链,只为补一个仅存在于 Windows 的缺口。是否考虑过 optionalDependencies(或用 os/cpu 限定)?

规模——确实触及核心路径(packages/core/src/**packages/cli/src/services/**,以及跨五个包的改动),所以我算了行数。标题类型是 fix 而非 refactor,Stage 0 硬拦截不适用。516 行改动中:约 78 行生产逻辑约 298 行测试约 140 行 lockfile。远低于 500 行升级阈值。39 个文件看着面广,但非测试文件只有约 11 个,测试改动是统一的可移植性扫尾而非深度重写——属于"面广而量小",这一维度也不需要升级。

方案——可移植性工作本身大体是有章法的,好的部分我想点出来:用 Number.isSafeInteger(inode) || inode <= 0 做跳过守卫而不是删掉 inode 断言;用 differentIdentity() / stat.ino === 1 ? 2 : 1,因为在有精度损失的 NTFS 浮点 inode 上做 +1 是个空操作,会悄无声息地不再产生任何扰动;在 assertSameIdentity 中用 BigInt() 强转,使 number/bigint 混合形态仍能正确比较;以及用 pathToFileURL(require.resolve('tsx')) 生成 --import 说明符。最后这一处是真正的产品缺陷修复,而不是测试上的迁就——把裸的 C:\... 路径交给 ESM loader 会抛 ERR_UNSUPPORTED_ESM_URL_SCHEMEgit config --global core.autocrlf false 也是安全受控的:该 job 把 HOME/USERPROFILE 导出为 ${{ runner.temp }}/qwen-ci-home,所以 --global 写入的是一次性 CI home,而不是 runner 自身的配置。

有三点我要提出异议,都不算大:

  1. artifact-snapshots.tsawait fs.readdir(references) 从短路条件里提了出来,于是它现在无条件执行——包括在 operationId 有值、此前根本不会执行它的分支上。我跟了一遍,它接近于无副作用(snapshotDirectory 已经对 references 做过 realpath,而目录缺失时后续的 writeFile 会抛出同样的 ENOENT,走完全相同的 catch 路径),但这恰恰是我建议去掉它的理由:它在 owned-restore 路径上多花一次 syscall,旁边没有任何测试改动,描述里也没有把它和某个 Windows 失败关联起来。它到底修的是哪个失败?
  2. run-qwen-serve.test.tshostname: '0' 改成 '0.0.0.0',使断言变成空断言——细节见代码审查评论。
  3. pnpm-workspace.yamlminimumReleaseAgeExclude 的新增会打破一个既有仓库测试——这是阻塞项,同样在代码审查评论中。

风险——无升级风险信号:Stage 1e 高危路径扫描在生产文件集合中没有命中(acp-integration 那次命中是 .test.ts 文件,扫描按设计已排除)。

有一点结构性说明值得提出,因为它决定了这些改动实际能被验证到什么程度:常规 PR CI 上 Test (windows-latest)Test (macos-latest) 都是 skipped,而 Test (ubuntu-latest) 跑的是 test:ci:workspaces,不包含 test:scripts。所以本 PR 中唯一会执行仓库级脚本测试的通道,是那个 dispatch 触发的 Windows 验证 run——目前仍在运行中。一个被打破的仓库测试,就是这样藏在一串看起来全绿的检查后面的。

进入代码审查 🔍

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

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

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Code review

I read the title and the "Why it's needed" section first and wrote down what I'd do before opening the diff: fix the two real product bugs (--import needs a file:// URL, and dev/ino identity checks need to survive 64-bit NTFS file IDs), then gate the genuinely POSIX-only fixtures behind platform guards that skip rather than delete assertions, and keep the tokenizer fallback out of the shared dependency tree if at all possible. The PR matches that on the first two and does the third better than I would have — the Number.isSafeInteger(inode) || inode <= 0 guards and the differentIdentity() helper are the right instinct, and recognizing that ino + 1 is a silent no-op on a lossy float inode is a genuinely sharp catch. It diverges on the dependency placement, and it carries one change that breaks an existing repo test.

🔴 Blocker — the minimumReleaseAgeExclude addition breaks a repo test that isn't in this PR

pnpm-workspace.yaml gains a second entry:

minimumReleaseAgeExclude:
  - '@qwen-code/channel-base'
  - '@tybys/wasm-util@0.10.4'

scripts/tests/package-scripts.test.js:386 pins that array exactly:

it('keeps the internal release-age exception independent of the version', () => {
  const workspace = parse(readFileSync(path.join(root, 'pnpm-workspace.yaml'), 'utf8'));
  expect(workspace.minimumReleaseAgeExclude).toEqual(['@qwen-code/channel-base']);
});

toEqual, not toMatchObject — a two-element array fails it. That file is not among the PR's 39 changed files, so nothing updates the expectation. root is path.resolve(__dirname, '../..'), i.e. the repo root, so it reads the file this PR edits.

The reason it isn't showing up red yet is lane coverage, and it's worth being explicit about: the test runs under npm run test:scripts, which is part of npm run test:ci but not of npm run test:ci:workspaces. Test (ubuntu-latest, Node 22.x) runs the latter (ci.yml:764). The two lanes that run test:ciTest (windows-latest) (ci.yml:1653) and Test (macos-latest) (ci.yml:1556) — are both skipped on this PR. So the only thing that will execute it is the dispatched Windows validation run, which is still in flight.

There's a second layer here beyond the mechanical failure. The test's name is the policy: the release-age exception list is meant to hold internal packages and to be version-independent. @tybys/wasm-util@0.10.4 is an external registry package pinned to an exact version, so it violates both halves. Adding the string to the expectation would make CI green while deleting the invariant the test exists to hold.

Two ways out, and I'd prefer the first:

  • Drop the exception. @napi-rs/wasm-runtime@1.2.4 declares @tybys/wasm-util: ^0.10.3, and the overrides pin to 0.10.4 is what forces the too-new resolution. If 0.10.3 (or whatever the range admits) clears the release-age policy, removing both the override and the exclude entry fixes this with no policy change. Worth checking whether the pin was load-bearing at all.
  • If the exception is genuinely required, it's a supply-chain policy decision, not a Windows CI detail — update the test deliberately and say in the description why an external, version-pinned entry is acceptable now.

🟡 run-qwen-serve.test.ts — the assertion became vacuous

-          hostname: '0',
+          hostname: '0.0.0.0',
...
-      expect(arg.bind).toBe('0');
+      expect(arg.bind).toBe('0.0.0.0');
       expect(arg.boundAddress).toBe('0.0.0.0');

The comment directly above it still reads "The operator spelling and the socket address differ here, so this pins that boot reports what the socket bound, not what was typed." After this change they no longer differ — typed, bind, and boundAddress are all '0.0.0.0', so the assertion passes whether production reports what was typed or what was bound. The thing the test was built to catch is gone, and the comment now describes a situation that isn't there.

Needing to move off '0' is fair — it's a POSIX inet_aton-style shorthand that Windows' resolver won't take. But the intent is portable: keep a spelling that differs from the bound address ('localhost'127.0.0.1 is the obvious cross-platform pair), or guard the '0' case to non-Windows and keep it. At minimum the stale comment has to go, because right now it tells the next reader the test is stronger than it is.

🟡 web-shell-brand.test.ts — the inode perturbation no longer drives the assertion

Production now takes expectedStat from fs.lstatSync(filePath, { bigint: true, throwIfNoEntry: false }), so it's a BigIntStats. The one-off mock in "refuses a file swapped between the lstat and the open" still calls fsActual.fstatSync(fd) with no options, returning a number-shaped Stats:

vi.mocked(fs.fstatSync).mockImplementationOnce(((fd: number) => {
  const stat = fsActual.fstatSync(fd);          // number Stats, not BigIntStats
  const fake = Object.create(Object.getPrototypeOf(stat)) as fs.Stats;
  Object.assign(fake, stat, { ino: stat.ino === 1 ? 2 : 1 });

readRegularFileNoFollow compares with bare !== and no coercion, so stat.dev !== expectedStat.dev is number !== bigint — always true — and the refusal fires on the dev term before ino is ever consulted. The test still passes, because it only asserts the generic 'changed while it was being read' warning. But its own comment says "Perturbing ino drives exactly that; deleting the re-check turns this red", and that's no longer true of the ino term specifically: you could delete the inode comparison and stay green.

One-word fix — fsActual.fstatSync(fd, { bigint: true }) — restores it. The contrast with no-follow-open.ts is instructive: there the PR widened assertSameIdentity to { dev: number | bigint; ino: number | bigint } and wrapped both sides in BigInt(...), so its equivalent perturbation tests still discriminate across mixed shapes. web-shell-brand.ts didn't get that treatment and its test didn't follow. Worth deciding which of the two conventions you want, since they now differ.

🟡 artifact-snapshots.ts — a drive-by with no stated Windows link

Hoisting await fs.readdir(references) out of the && makes it unconditional, so it now runs on the operationId path where it previously never did. I traced it and it's close to inert: snapshotDirectory has already realpath'd references by that point, and had the directory been missing the following writeFile would have thrown the same ENOENT into the same catch, whose !(code === 'ENOENT' && !operationId) branch rethrows it either way. So no observable behavior change — just an extra syscall per owned restore.

That's exactly why I'd drop it. It's the only production edit in the diff with no corresponding test change and nothing in the description tying it to a Windows failure. If it was fixing something, the description should say what; if it wasn't, it doesn't belong in a PR whose stated discipline is that every edit serves the baseline.

Notes (non-blocking)

  • monitor-debug-store.ts is production, not a test. privateDirectory now skips mode & 0o077 and the uid comparison on Windows. Defensible — Node reports synthesized mode bits there, so the check threw unconditionally — but it removes the "reject a shared or foreign-owned debug root" guard on that platform, and the PR body files it under skipped assertions. On the test side, "rejects shared or symlink archive roots without touching their contents" now has its entire shared-root half (chmod 0o755, initialize() → false, create() → undefined) inside the non-Windows guard, so on Windows it exercises only the symlink half while the name still claims both.
  • Dependency placement. @node-rs/jieba-wasm32-wasi in dependencies rather than optionalDependencies installs the WASI chain on Linux and macOS too, and that chain includes @emnapi/core@2.0.0-alpha.4 and @emnapi/runtime@2.0.0-alpha.4 — alpha prereleases now in the production tree on every platform, for a Windows-only fallback. Both lockfiles are updated together, which is what AGENTS.md asks for.
  • What's good, so it doesn't get lost: pathToFileURL(require.resolve('tsx')) fixes a real ERR_UNSUPPORTED_ESM_URL_SCHEME product bug rather than papering over it; git config --global core.autocrlf false is safely scoped because the job exports HOME/USERPROFILE to ${{ runner.temp }}/qwen-ci-home; the .replaceAll('\\', '/') normalizations target git's own always-forward-slash porcelain and config output rather than blindly rewriting assertions; overwriteGitfile reuses the file's existing helper instead of adding a new one; and opentui-footer.test.tsx encodes the real Windows bare-Tab fallback instead of skipping, which keeps coverage on both platforms.
Files changed — the 13 non-test files (of 39)
File What changed
.github/workflows/ci.yml Adds git config --global core.autocrlf false to the Windows test lane, inside the job's throwaway CI home
.github/workflows/windows-runner-smoke.yml Same autocrlf line for the self-hosted smoke lane
package-lock.json Locks the WASI tokenizer plus @napi-rs/wasm-runtime, @tybys/wasm-util, and two alpha @emnapi packages
pnpm-lock.yaml pnpm side of the same dependency addition; kept in step with npm as AGENTS.md requires
pnpm-workspace.yaml Pins @tybys/wasm-util in overrides and excludes it from the release-age policy — this is the blocker
packages/qwen-live/package.json Adds @node-rs/jieba-wasm32-wasi@2.0.2 to production dependencies
packages/core/src/code-mode/host-client.ts Wraps the tsx --import specifier in pathToFileURL — real product fix for Windows drive paths
packages/core/src/utils/no-follow-open.ts Moves to BigIntStats and coerces both sides with BigInt() so mixed shapes still compare
packages/core/src/services/worktreeSessionService.ts Sidecar identity checks switched wholesale to bigint: nlink !== 1n, ino === 0n, size !== BigInt(bytesRead)
packages/core/src/tools/artifact/artifact-snapshots.ts readdir hoisted out of the short-circuit — unconditional now, no stated Windows link
packages/cli/src/services/web-shell-brand.ts Logo guard moved to BigIntStats (nlink > 1n, size > BigInt(MAX)), but the fd re-check keeps bare !==
packages/cli/src/serve/fs/workspace-file-system.ts Three lstat calls gain { bigint: true }; safe because assertSameFile already coerces via toBigInt
packages/qwen-live/src/proactive/monitor-debug-store.ts Production guard: mode-bits and uid checks skipped on Windows
…and 26 test files Platform guards, safe-integer inode gates, native path fixtures, and bigint-aware stat mocks

Testing evidence

This is an unattended CI run, so per the triage rules I did not build, run, or execute anything from this PR — no npm, no vitest, no checkout. Everything below is read from the PR's own CI through the API at the reviewed commit. I also independently confirmed the two workflow runs the description cites rather than trusting the links: the "before" run 34713863264 is genuinely Qwen Code CI, event=schedule, head_branch=main, conclusion=failure; the "after" run 34772671680 is Windows Self-Hosted Runner Validation, dispatched on this branch at exactly the reviewed SHA — and it is still in_progress, so the headline claim is not yet substantiated by anything.

85 check-runs on the commit: 22 success, 58 skipped, 5 in progress, 0 failures. Nothing is red, but the absence of red is weaker evidence than it looks, because the two lanes that would exercise this PR's actual subject are skipped and the repo-level script tests don't run on Ubuntu:

  • Test (windows-latest, Node 22.x)skipped. This is the lane the PR exists to fix.
  • Test (macos-latest, Node 22.x)skipped. Also runs npm run test:ci, so it also covers test:scripts.
  • Test (ubuntu-latest, Node 22.x)in progress, and runs test:ci:workspaces, which excludes the scripts/tests suite that the blocker above lives in. A green result here will not clear it.
  • Validate runnerin progress. The dispatched Windows self-hosted validation, and the only lane in flight that runs test:ci.
  • Lint & Static (ubuntu-latest, Node 22.x)success, so formatting and typechecking are clean, including the Awaited<ReturnType<typeof fsp.lstat>> annotation on leaf now that the call returns BigIntStats.
  • Install (windows-latest), Install (macos-latest), Install (ubuntu-latest)success. The new dependency resolves and installs on all three platforms, which answers the lockfile-consistency question but says nothing about whether the tokenizer loads.
  • Desktop Shell (windows-2022), windows-latest / Java 21success.
  • Integration Tests (no-AK, No Sandbox), Real daemon E2E / Java 11, Serve A/B, OpenTUI no-flicker gate, TUI parity snapshots — success or in progress; none failing.

Not verified, and why: the Windows unit suite has not run to completion on this branch, so every Windows-side claim in the description remains the author's, not evidence. Linux is marked ⚠️ untested by the author and Test (ubuntu-latest) is still running, so the regression risk the bigint conversions carry on the platform that currently works is unmeasured. The tokenizer fallback has no test at all — nothing in the diff asserts that @node-rs/jieba actually resolves @node-rs/jieba-wasm32-wasi when the native DLL is absent, so a green Windows run would not distinguish "the fallback works" from "the import path never got exercised". The author reports macOS ✅ locally; that is their claim and I have not re-run it.

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

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

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

Sandboxed verification would settle part of this: @qwen-code /verify — the two test-efficacy regressions above mean the suite stays green without pinning the change, which is precisely the gap an A/B run against the base build closes. Specifically, it would show whether the web-shell-brand fd identity re-check and the worktreeSessionService sidecar dev/ino guards still fail closed when only the inode differs, or whether they now trip on the type-mismatched dev comparison first. The author has write access, so @qwen-code /tmux is also available, though this PR has no TUI surface and I wouldn't spend a run on it. Neither lane substitutes for the Windows validation run finishing — that one is already dispatched and is the only thing that can confirm the baseline is actually restored.

中文说明

代码审查

我先只读标题和"为什么需要",在打开 diff 之前写下了自己的方案:修掉两个真正的产品缺陷(--import 需要 file:// URL,dev/ino 身份校验需要能承受 NTFS 的 64 位文件 ID),然后把确实只适用于 POSIX 的夹具放到平台守卫后面,并且跳过而不是删除断言,同时尽量不让分词器回退进入共享依赖树。前两点这个 PR 与我的方案一致,第三点做得比我预想的更好——Number.isSafeInteger(inode) || inode <= 0 守卫和 differentIdentity() 辅助函数的思路是对的,而意识到在有精度损失的浮点 inode 上 ino + 1 是个静默空操作,是很敏锐的发现。它在依赖放置上与我设想的不同,并且夹带了一处会打破既有仓库测试的改动。

🔴 阻塞项 —— minimumReleaseAgeExclude 的新增打破了本 PR 未修改的仓库测试

pnpm-workspace.yaml 多了一个条目 '@tybys/wasm-util@0.10.4',而 scripts/tests/package-scripts.test.js:386toEqual(['@qwen-code/channel-base']) 精确锁定了这个数组(不是 toMatchObject),两元素数组必然失败。该测试文件不在本 PR 的 39 个改动文件里,所以没有任何地方更新这个期望值。测试里的 rootpath.resolve(__dirname, '../..'),即仓库根目录,读的正是本 PR 修改的那个文件。

之所以现在还没变红,是通道覆盖问题,值得说清楚:该测试跑在 npm run test:scripts 下,属于 npm run test:ci不属于 npm run test:ci:workspacesTest (ubuntu-latest, Node 22.x) 跑的是后者(ci.yml:764)。真正跑 test:ci 的两个通道——Test (windows-latest)(ci.yml:1653)和 Test (macos-latest)(ci.yml:1556)——在本 PR 上都是 skipped。所以唯一会执行它的,只有那个仍在运行中的 dispatch Windows 验证 run。

机械失败之外还有第二层。这个测试的名字本身就是策略:release-age 例外列表应当只放内部包,并且与版本无关@tybys/wasm-util@0.10.4 是外部 registry 包且精确锁版本,两条都违反。把这个字符串加进期望值能让 CI 变绿,但同时删掉了该测试存在的意义。

两条出路,我倾向于第一条:

  • 去掉这个例外。 @napi-rs/wasm-runtime@1.2.4 声明的是 @tybys/wasm-util: ^0.10.3,而 overrides 里锁到 0.10.4 正是导致解析到过新版本的直接原因。如果 0.10.3(或该 range 允许的版本)能满足 release-age 策略,那么同时移除 override 和 exclude 条目即可解决,无需改动策略。值得先确认这个 pin 本身是否必要。
  • 如果这个例外确实必需,那它属于供应链策略决定,而不是 Windows CI 细节——请有意识地更新测试,并在描述中说明为什么现在可以接受一个外部且锁版本的条目。

🟡 run-qwen-serve.test.ts —— 断言变成了空断言

hostname'0' 改为 '0.0.0.0' 后,输入值、bindboundAddress 三者完全相同,因此无论生产代码上报的是"输入值"还是"实际绑定地址",断言都会通过——这个测试原本要抓的东西没有了。而紧邻上方的注释仍写着"操作者的拼写与 socket 地址在此不同,因此这里锁定的是启动时上报实际绑定值而非输入值",现在这句描述的是一个不存在的情形。

需要放弃 '0' 是合理的——它是 POSIX 的 inet_aton 式简写,Windows 解析器不接受。但测试意图本身是可移植的:换一个与绑定结果不同的拼写('localhost'127.0.0.1 是最明显的跨平台组合),或者把 '0' 这一例限定在非 Windows 并保留。至少必须删掉那条过期注释,否则它会让后来的读者以为这个测试比实际更强。

🟡 web-shell-brand.test.ts —— inode 扰动已不再驱动断言

生产代码现在从 fs.lstatSync(filePath, { bigint: true, throwIfNoEntry: false })expectedStat,即 BigIntStats。而 "refuses a file swapped between the lstat and the open" 中的一次性 mock 仍然调用不带任何选项fsActual.fstatSync(fd),返回 number 形态的 Stats

readRegularFileNoFollow 用裸 !== 比较且不做强转,所以 stat.dev !== expectedStat.devnumber !== bigint——恒为真——于是拒绝在 dev 这一项就触发了,ino 根本没被比较到。测试仍然通过,因为它只断言了通用的 'changed while it was being read' 警告。但它自己的注释写着"扰动 ino 正是驱动这一点的手段;删掉这个复检就会变红",这对 ino 这一项已不再成立:把 inode 比较删掉也依然是绿的。

一个词的修复——fsActual.fstatSync(fd, { bigint: true })——就能恢复。与 no-follow-open.ts 的对比很说明问题:那边 PR 把 assertSameIdentity 放宽为 { dev: number | bigint; ino: number | bigint } 并对两侧都做了 BigInt(...) 强转,因此同类扰动测试在混合形态下仍然有区分力。web-shell-brand.ts 没有这样处理,其测试也没有跟上。值得决定采用两种约定中的哪一种,因为它们现在不一致。

🟡 artifact-snapshots.ts —— 与 Windows 无关的顺手改动

await fs.readdir(references)&& 中提出来使它变成无条件执行,于是在 operationId 有值的分支上它也会运行,而此前根本不会。我跟了一遍,它接近于无副作用:此时 snapshotDirectory 已经对 references 做过 realpath;即便目录缺失,后续的 writeFile 也会抛出同样的 ENOENT 进入同一个 catch,其 !(code === 'ENOENT' && !operationId) 分支在两种写法下都会重新抛出。所以没有可观测的行为变化——只是每次 owned restore 多一次 syscall。

这恰恰是我建议去掉它的理由。它是 diff 中唯一一处既没有配套测试改动、描述里也没有关联任何 Windows 失败的生产改动。如果它确实在修什么,描述应当说明;如果没有,它不该出现在一个自称"每处改动都服务于基线"的 PR 里。

说明(非阻塞)

  • monitor-debug-store.ts 是生产代码,不是测试。 privateDirectory 在 Windows 上跳过 mode 位与 uid 比较。可以理解——Node 在该平台报告的是合成的 mode 位,检查会无条件抛错——但这移除了该平台上"拒绝共享或属主异常的 debug 根目录"这道防护,而 PR 正文把它归入了被跳过的断言。测试侧,"rejects shared or symlink archive roots without touching their contents" 现在把整个 shared-root 部分(chmod 0o755initialize() → false、create() → undefined)放进了非 Windows 守卫内,所以在 Windows 上它只验证 symlink 那一半,而测试名仍然声称两者。
  • 依赖放置。 @node-rs/jieba-wasm32-wasi 放在 dependencies 而非 optionalDependencies,导致 Linux 和 macOS 也会安装这条 WASI 依赖链,而其中包含 @emnapi/core@2.0.0-alpha.4@emnapi/runtime@2.0.0-alpha.4——两个 alpha 预发布版本因此进入了所有平台的生产依赖树,只为一个仅 Windows 需要的回退。两个 lockfile 是一起更新的,符合 AGENTS.md 的要求。
  • 做得好的部分,免得被埋没: pathToFileURL(require.resolve('tsx')) 真正修掉了 ERR_UNSUPPORTED_ESM_URL_SCHEME 这个产品缺陷,而不是绕过它;git config --global core.autocrlf false 作用域是安全的,因为该 job 把 HOME/USERPROFILE 导出为 ${{ runner.temp }}/qwen-ci-home.replaceAll('\\', '/') 针对的是 git 自身始终使用正斜杠的 porcelain 与 config 输出,而不是盲目重写断言;overwriteGitfile 复用了文件里已有的辅助函数而非新增一个;opentui-footer.test.tsx 编码了真实的 Windows 裸 Tab 回退行为而不是跳过,使两个平台都保留了覆盖。

测试证据

这是一次无人值守的 CI 运行,因此按 triage 规则我没有构建、运行或执行本 PR 的任何代码——没有 npm、没有 vitest、没有 checkout。以下内容全部是在被审查的那个 commit 上通过 API 读取 PR 自身的 CI 得到的。描述中引用的两个 workflow run 我也独立核实过,而不是采信链接:修复前的 run 34713863264 确实是 Qwen Code CIevent=schedulehead_branch=mainconclusion=failure;修复后的 run 34772671680 是 Windows Self-Hosted Runner Validation,在本分支上、正好在被审查的那个 SHA 上 dispatch 触发——而它仍在 in_progress,所以那个核心主张目前还没有任何证据支撑。

该 commit 上共 85 个 check-run:22 成功、58 跳过、5 进行中、0 失败。没有红的,但"没有红"这件事的证明力比看上去弱,因为真正检验本 PR 主题的两个通道被跳过了,而仓库级脚本测试在 Ubuntu 上不跑。上表(英文部分)列出了逐项结论,此处不再重复。

未验证的部分及原因:Windows 单元测试套件尚未在本分支上跑完,因此描述中所有 Windows 侧的说法目前仍只是作者的陈述,不是证据。Linux 被作者标记为 ⚠️ 未测试,而 Test (ubuntu-latest) 仍在运行,所以 bigint 转换给当前正常工作的平台带来的回归风险还没有被测到。分词器回退完全没有测试——diff 中没有任何断言验证 @node-rs/jieba 在原生 DLL 缺失时确实会解析到 @node-rs/jieba-wasm32-wasi,因此即使 Windows run 变绿,也无法区分"回退生效了"和"这条 import 路径根本没被执行到"。作者称本地 macOS ✅;这是他们的主张,我没有重跑。

沙箱化验证可以解决其中一部分:@qwen-code /verify —— 上面两处测试效力退化意味着套件在不锁定该改动的情况下依然是绿的,而这正是与 base 构建做 A/B 所能填补的缺口。具体地说,它能表明 web-shell-brand 的 fd 身份复检与 worktreeSessionService 的 sidecar dev/ino 守卫在"仅 inode 不同"时是否仍然 fail closed,还是现在会先在类型不匹配的 dev 比较上触发。作者具备写权限,因此 @qwen-code /tmux 也可用,不过本 PR 没有 TUI 界面,我不建议为它花一次运行。两条通道都不能替代 Windows 验证 run 跑完——那一个已经 dispatch,也是唯一能确认基线是否真的恢复的手段。

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

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

@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

Confidence: 2/5 — the portability work is good and the problem is real, but the PR breaks an existing repo test it didn't update, and the only lane that would have caught it is skipped.

Stepping back: I verified the premise myself rather than accepting it. Run 34713863264 is genuinely a failed scheduled Qwen Code CI on main, so this isn't a solution looking for a problem — a Windows baseline that has been red for days means Windows regressions are shipping silently, and that's a real cost borne by Windows users even though none of them will ever see this diff.

Against my own independent proposal, the PR is better in the places I expected it to be worse. I assumed a "make CI green on Windows" PR would mostly be skip guards, and there are plenty of those — but they're the honest kind. Number.isSafeInteger(inode) || inode <= 0 gates rather than deletes; differentIdentity() replaces ino + 1 because on a lossy NTFS float inode +1 is a no-op that silently stops perturbing anything, which is a subtle trap I did not think of until I read it; opentui-footer.test.tsx encodes the real Windows bare-Tab fallback instead of skipping, so coverage is kept on both platforms. And pathToFileURL(require.resolve('tsx')) plus the bigint identity conversions are genuine product fixes, not test accommodations — a raw C:\... specifier really does throw ERR_UNSUPPORTED_ESM_URL_SCHEME, and dev/ino really do exceed 2^53 on NTFS. In six months I'd thank whoever wrote those.

What I can't get past is that it doesn't currently pass its own repo's tests. pnpm-workspace.yaml gains a second minimumReleaseAgeExclude entry; scripts/tests/package-scripts.test.js:386 pins that array with toEqual(['@qwen-code/channel-base']); that file is not in the diff. This isn't a judgement call or a matter of taste — it's a mechanical failure I can point at a line number, and the fix is somewhere between one and three lines.

The part that concerns me more than the failure is why it was invisible. Test (windows-latest) and Test (macos-latest) are both skipped on normal PR CI, and Test (ubuntu-latest) runs test:ci:workspaces, which excludes test:scripts. So a PR whose entire subject is the Windows lane cannot have its Windows lane run by the PR itself, and the repo-level script tests only execute on lanes that don't run. 85 checks, 22 green, 0 red — and the one suite that matters most here is in exactly none of them. That's a gap in the lane configuration, not something this author introduced, but it's why "CI is green" cannot be the argument for merging this.

Two secondary things I'd want fixed rather than merely noted. run-qwen-serve.test.ts changing hostname: '0' to '0.0.0.0' makes typed-value and bound-address identical, so the assertion passes either way and the comment above it now describes a situation that doesn't exist. And in web-shell-brand.test.ts, the mock returns number-shaped stats while production now expects BigIntStats, so the refusal fires on dev and the ino perturbation the test was written around never gets compared — its own comment ("deleting the re-check turns this red") is no longer true of the inode term. Neither breaks the build. Both are worse than that, in a quiet way: a test that cannot fail reports coverage it does not have, and this PR is specifically about restoring trustworthy signal.

Am I being unfair to a well-intentioned infra PR from someone with admin on the repo? I checked that instinct. The blocker is objective, the two efficacy regressions are one-line fixes each, and artifact-snapshots.ts — the one edit with no test change and no stated Windows link — is a question, not an accusation. Everything else I'd merge. The honest read is that this is 90% of a good PR with a certain, cheap failure sitting in it, and the right move is to send it back for that rather than approve on momentum or defer it to a human who will have to rediscover all of the above.

Requesting changes on the blocker plus the two assertion fixes. The Windows validation run is still in flight, so the headline claim is unverified either way — but that isn't what I'm gating on, and it doesn't need to finish before the YAML and test issues are addressed.

中文说明

Confidence: 2/5 —— 可移植性工作本身做得好,问题也真实存在,但本 PR 打破了一个它并未更新的既有仓库测试,而唯一能发现这一点的通道恰好被跳过了。

退一步看:我自己核实了前提,而不是直接采信。Run 34713863264 确实是 main 上一次失败的定时 Qwen Code CI,所以这不是"为方案找问题"——Windows 基线连续红了好几天,意味着 Windows 上的回归正在悄无声息地被发布,即使 Windows 用户永远看不到这个 diff,这个代价也是真实由他们承担的。

与我自己的独立方案相比,这个 PR 在我原以为会更弱的地方反而更好。我原以为一个"让 CI 在 Windows 上变绿"的 PR 主要是 skip 守卫,确实有很多——但它们是诚实的那一种。Number.isSafeInteger(inode) || inode <= 0 是做门禁而不是删除断言;differentIdentity() 取代了 ino + 1,因为在有精度损失的 NTFS 浮点 inode 上 +1 是个空操作,会静默地不再产生任何扰动,这个微妙的陷阱我在读到之前并没有想到;opentui-footer.test.tsx 编码了真实的 Windows 裸 Tab 回退行为而不是跳过,因此两个平台都保留了覆盖。而 pathToFileURL(require.resolve('tsx')) 与 bigint 身份转换是真正的产品修复,不是测试上的迁就——裸的 C:\... 说明符确实会抛 ERR_UNSUPPORTED_ESM_URL_SCHEMEdev/ino 在 NTFS 上也确实会超过 2^53。六个月后我会感谢写下这些的人。

我无法放过的是:它目前通不过自己仓库的测试。pnpm-workspace.yaml 多了第二个 minimumReleaseAgeExclude 条目;scripts/tests/package-scripts.test.js:386toEqual(['@qwen-code/channel-base']) 精确锁定了该数组;而那个文件不在 diff 里。这不是主观判断,也不是品味问题——这是一个我能指到具体行号的机械性失败,修复量在一到三行之间。

比这个失败本身更让我在意的是它为什么没被看见。常规 PR CI 上 Test (windows-latest)Test (macos-latest) 都被跳过,而 Test (ubuntu-latest) 跑的是 test:ci:workspaces,不含 test:scripts。于是一个主题就是 Windows 通道的 PR,无法由它自己触发 Windows 通道;而仓库级脚本测试只在不会运行的那些通道上执行。85 个检查、22 个绿、0 个红——而此处最关键的那个套件恰好不在其中任何一个里。这是通道配置的缺口,不是这位作者引入的,但也正因如此,"CI 是绿的"不能成为合并本 PR 的理由。

另有两点我希望被修掉,而不只是记录在案。run-qwen-serve.test.tshostname: '0' 改成 '0.0.0.0',使输入值与绑定地址完全相同,于是断言在两种实现下都会通过,而其上方的注释现在描述的是一个不存在的情形。web-shell-brand.test.ts 中,mock 返回 number 形态的 stats 而生产代码现在期望 BigIntStats,因此拒绝在 dev 上就触发了,该测试围绕 ino 写就的扰动根本没被比较到——它自己的注释("删掉这个复检就会变红")对 inode 这一项已不再成立。两者都不会让构建失败。但两者都比"构建失败"更糟,且是无声的:一个不可能失败的测试,会宣称它并不具备的覆盖率;而这个 PR 的主题恰恰是恢复可信的信号。

我是不是在苛待一个来自仓库 admin、用心良好的基础设施 PR?我检查过这个直觉。阻塞项是客观的,两处效力退化各自都是一行修复,而 artifact-snapshots.ts——唯一一处既无测试改动也无 Windows 关联说明的改动——是一个疑问,不是指控。其余部分我都会合并。诚实的判断是:这是一个完成了 90% 的好 PR,里面坐着一个确定且修复成本极低的失败,正确的做法是就此打回,而不是凭惯性批准,或者把它推给一个必须重新发现以上全部内容的人。

基于该阻塞项加上两处断言修复,请求变更(request changes)。Windows 验证 run 仍在进行,所以核心主张无论如何都还未被验证——但那不是我把关的依据,而且在 YAML 与测试问题被处理之前,它也不需要先跑完。

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

Reviewed at 6099768ffbec01650363ab9b5356fd2b59c4f287 · 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.

@yiliang114

Needs some rethinking — see my notes above. 🙏

One blocker and two one-line test fixes:

Blocker — breaks an existing repo test. pnpm-workspace.yaml adds '@tybys/wasm-util@0.10.4' to minimumReleaseAgeExclude, but scripts/tests/package-scripts.test.js:386 pins that array exactly with toEqual(['@qwen-code/channel-base']), and that test file isn't in this diff. It runs under npm run test:scripts (part of test:ci, not of test:ci:workspaces), so Test (ubuntu-latest) won't catch it and both lanes that would are skipped on this PR — only the still-running Windows validation run executes it.

Note the test's name is the policy: that list is meant to hold internal packages and be version-independent. @tybys/wasm-util@0.10.4 is external and version-pinned, so it breaks both halves — adding the string to the expectation would green CI while deleting the invariant. Preferred fix: @napi-rs/wasm-runtime@1.2.4 only asks for ^0.10.3, so check whether the overrides pin to 0.10.4 is load-bearing at all; dropping the override and the exclude entry together resolves this with no policy change.

run-qwen-serve.test.tshostname: '0''0.0.0.0' makes the typed value identical to bind and boundAddress, so the assertion passes whichever one production reports. The comment above it ("the operator spelling and the socket address differ here") now describes a situation that doesn't exist. 'localhost'127.0.0.1 keeps the intent portably.

web-shell-brand.test.ts — the fstatSync mock calls fsActual.fstatSync(fd) with no options while production now uses { bigint: true } for expectedStat. Since readRegularFileNoFollow compares with bare !==, stat.dev !== expectedStat.dev is number !== bigint and always trips first, so the ino perturbation the test was written around is never compared. It passes, but deleting the inode check would no longer turn it red. fsActual.fstatSync(fd, { bigint: true }) restores it.

Also worth a look, non-blocking: the artifact-snapshots.ts readdir hoist is the only production edit with no test change and no stated Windows link (I traced it as behaviorally inert, which is the argument for dropping it); and @node-rs/jieba-wasm32-wasi in dependencies rather than optionalDependencies pulls @emnapi/*@2.0.0-alpha.4 into the production tree on Linux and macOS for a Windows-only fallback.

The portability work itself is genuinely good — the Number.isSafeInteger inode gates, differentIdentity() replacing the lossy-float ino + 1 no-op, the BigInt() coercion in assertSameIdentity, and pathToFileURL(require.resolve('tsx')) as a real product fix rather than a test accommodation. That's why this is a send-back, not a rethink.

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

🩺 serve daemon A/B

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

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

Qwen Code · serve A/B

- pnpm-workspace.yaml: drop '@tybys/wasm-util@0.10.4' from
  minimumReleaseAgeExclude. scripts/tests/package-scripts.test.js pins
  that list to internal, version-independent entries. The exclusion is
  also inert: minimumReleaseAge is not configured anywhere in the repo,
  and pnpm-lock.yaml records `overrides` but not minimumReleaseAgeExclude
  -- so the '@tybys/wasm-util': '0.10.4' override stays, because dropping
  it would desync the lockfile and fail --frozen-lockfile.
- web-shell-brand.test.ts: build the swapped-file fake as BigIntStats.
  Production compares bigint fields with a bare `!==`, so a number-backed
  fake tripped `dev` first and the perturbed inode was never reached;
  removing the perturbation now turns the test red again.
- run-qwen-serve.test.ts: '0' is unresolvable on Windows (getaddrinfo
  ENOTFOUND 0), so the wildcard case keeps '0.0.0.0' and witnesses the
  socket rather than a spelling: the operator typed port 0, so the report
  must carry the ephemeral port the listener actually got. Spelling
  normalisation stays pinned platform-independently in
  remote-quickstart.test.ts.
- artifact-snapshots.ts: revert the readdir hoist to main. It is
  behaviourally inert (the catch rethrows ENOENT on the operationId path
  either way), has no test and no Windows link.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmu07e78o2w
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Addressed in 3cf180455f (one commit, 4 files).

1. Blocker — minimumReleaseAgeExclude breaks package-scripts.test.js. Fixed, but not the way you suggested.

Dropped '@tybys/wasm-util@0.10.4'; the list is back to ['@qwen-code/channel-base'] and npm run test:scripts✓ scripts/tests/package-scripts.test.js (50 tests | 1 skipped).

The overrides pin stays, because removing it is not free: pnpm mirrors overrides into the lockfile header (pnpm-lock.yaml:20 '@tybys/wasm-util': 0.10.4), so dropping it desyncs pnpm-lock.yaml from pnpm-workspace.yaml and fails --frozen-lockfile / check-lockfile. minimumReleaseAgeExclude is not recorded in the lockfile, so removing that one line has zero lockfile impact. It is also inert as things stand — minimumReleaseAge is not configured anywhere in this repo (grep -rn minimumReleaseAge hits only the yaml key and the test), so the entry bought nothing and cost the invariant. If the override turns out to be unnecessary too, that is a separate lockfile-regenerating PR.

2. run-qwen-serve.test.ts — agreed the assertion went tautological; 'localhost' would trade one problem for another.

'0' cannot come back: the Windows runner rejects it outright (windows-runner-smoke run 34769784953, head b7a3ed7eac: FAIL … reports the wildcard bound address, not the inet_aton spelling / Error: getaddrinfo ENOTFOUND 0). 'localhost' is resolver-dependent — the sibling case at :13235 exists precisely to pin this "without touching any resolver", and on hosts that prefer IPv6 the socket reports ::1, not 127.0.0.1.

So the wildcard case keeps '0.0.0.0' and now witnesses the socket instead of a spelling: production sends bind: opts.hostname (run-qwen-serve.ts:9777) while boundAddress and actualPort come from the listener's own address (:9772, :8414), and the operator typed port: 0, so expect(arg.port).toBe(socket.port) + expect(arg.port).not.toBe(0) reds if boot ever echoes its input. boundAddress is compared against started.server.address().address rather than a literal. Test renamed to reports what the socket bound, not what the operator typed and the comment rewritten, since "not the inet_aton spelling" was no longer true. Spelling normalisation ('0', '0.0', '::0', …) remains pinned platform-independently in remote-quickstart.test.ts:125.

3. web-shell-brand.test.ts — fixed, and mutation-checked.

The fake is now built from fsActual.fstatSync(fd, { bigint: true }) as fs.BigIntStats with ino: stat.ino + 1n (exact on a bigint, so the float-lossiness reason for === 1 ? 2 : 1 is gone). Evidence that the inode comparison is live again: with the perturbation removed (ino: stat.ino), the test now fails — AssertionError: expected 'data:image/svg+xml,…' to be undefined. Before this change it passed either way, exactly as you described (number !== bigint on dev tripped first).

4a. artifact-snapshots.ts readdir hoist — reverted.

Confirmed inert: on the operationId path the old code skipped the readdir and then hit writeFileENOENT, and the catch only swallows ENOENT when !operationId, so both shapes rethrow. The file is now byte-identical to main (git diff origin/main -- packages/core/src/tools/artifact/artifact-snapshots.ts is empty), which also removes the one production edit with no test and no Windows link.

4b. @node-rs/jieba-wasm32-wasi in dependencies — declined, leaving for a human.

Nothing in the repo imports it (packages/qwen-live/src/memory/tokenize.ts:7 imports Jieba from @node-rs/jieba); it is there so the loader's wasm fallback exists when the native binding cannot load, pinned to the same 2.0.2. Under optionalDependencies npm may omit it on a platform/libc mismatch or install error — precisely the case it exists for — so this is a deliberate availability/supply-chain trade, not a mechanical fix. It also cannot be done here: moving it changes the dep type recorded in both package-lock.json and pnpm-lock.yaml, and neither lockfile can be regenerated in this worktree. The @emnapi/*@2.0.0-alpha.4 point you raise comes from that upstream package's own graph, so it wants an owner decision (pin, override, or upstream bump) in a follow-up.

Verification

  • npm run test:scripts (root): package-scripts.test.js ✓ 50 tests / 1 skipped. Suite total 2367 passed, 16 failed in 8 files unrelated to this change and caused by this worktree having no built dist/ — e.g. check-i18n.test.ts fails at collection with Failed to resolve entry for package "@qwen-code/qwen-code-core". All of those files are green in the ubuntu lane at 6099768ffb.
  • packages/cliweb-shell-brand.test.ts: 90/90 pass. run-qwen-serve.test.ts: the renamed case passes; 409 passed / 18 failed, the 18 being local-only (e.g. workspace_voice_transcription capability, native-probe envelopes) in a file that is green in CI at the previous head.
  • packages/cli typecheck: only 2 × TS6305 (packages/core/dist/src/utils/toml-to-markdown-converter.d.ts has not been built) — composite-project staleness in this worktree, no errors in any changed file. No core typecheck run: the only core edit restores main byte-for-byte.
  • prettier --check and eslint clean on all changed files.

…ss fixes

The previous commit dropped '@tybys/wasm-util@0.10.4' from
minimumReleaseAgeExclude to satisfy scripts/tests/package-scripts.test.js:390,
on the reasoning that the entry was inert because minimumReleaseAge is not
configured in this repo. That reasoning was wrong and CI measured it: the gate
is enforced by pnpm itself, not by repo config, so removing the exclude failed
'Install frozen pnpm worktree dependencies' on all three platforms with

  Lockfile failed supply-chain policy check (1975 entries)
  @tybys/wasm-util@0.10.4 was published at 2026-09-13T08:00:13.464Z,
  within the minimumReleaseAge cutoff (2026-09-12T20:03:50.286Z)

(run 34779568628; Install was green at 6099768, which carries the entry).
Restoring the line makes pnpm-workspace.yaml byte-identical to that head again.

The three other changes from that commit are kept, since they are independent
of the exclude and were green in run 34779568625 (Test 18m58s, Lint & Static
12m36s, Integration no-AK 7m8s): the web-shell-brand.test.ts BigIntStats fake
that re-arms the inode witness, the run-qwen-serve.test.ts socket witness
replacing the tautological '0.0.0.0' assertion, and the artifact-snapshots.ts
revert to main.

This leaves the package-scripts.test.js assertion red exactly as it was before
this round. That is now a measured conflict rather than an open question: the
repo test pins the exclude list to internal, version-independent entries, while
pnpm needs a version-pinned exemption for a package published the same day.
Resolving it means either dropping the '@tybys/wasm-util': '0.10.4' override and
regenerating both lockfiles so an older, age-compliant version resolves, or
relaxing the invariant to admit external version-pinned excludes. Both are
supply-chain policy calls for the author/maintainer, so neither is made here.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-closeout/jmu07e78o2w
@yiliang114

Copy link
Copy Markdown
Collaborator Author

Correction to my previous comment — one of its claims was wrong, and CI proved it. Reverted in d1535642b0.

I wrote that the minimumReleaseAgeExclude entry was "inert as things stand — minimumReleaseAge is not configured anywhere in this repo". That inference was wrong. The gate is enforced by pnpm itself, not by repo config, so grep finding no minimumReleaseAge key proves nothing. Dropping '@tybys/wasm-util@0.10.4' failed Install frozen pnpm worktree dependencies on all three platforms (run 34779568628):

✗ Lockfile failed supply-chain policy check (1975 entries in 566ms)
  @tybys/wasm-util@0.10.4 was published at 2026-09-13T08:00:13.464Z,
  within the minimumReleaseAge cutoff (2026-09-12T20:03:50.286Z)

Install was green at 6099768ffb, which carries the entry. So the entry is load-bearing, and my change traded one red lane for three while making the branch uninstallable — worse on every axis. d1535642b0 restores that line, making pnpm-workspace.yaml byte-identical to 6099768ffb again.

Kept from the previous commit, since they are independent of the exclude and were green in run 34779568625 (Test 18m58s, Lint & Static 12m36s, Integration no-AK 7m8s):

  • web-shell-brand.test.ts — the swapped-file fake is built as BigIntStats, so the ino perturbation is actually compared. Before this, number !== bigint on dev tripped first and the test passed with the inode check deleted.
  • run-qwen-serve.test.ts — the wildcard case now witnesses the real socket (expect(arg.port).toBe(socket.port) + not.toBe(0)) instead of the tautological '0.0.0.0' comparison. '0' cannot come back (Windows: getaddrinfo ENOTFOUND 0) and 'localhost' is resolver-dependent, as you noted.
  • artifact-snapshots.ts — the readdir hoist is reverted; the file is byte-identical to main.

Where that leaves your blocker finding: still open, and now measured rather than argued. scripts/tests/package-scripts.test.js:390 is red exactly as it was before this round. The conflict is real and cannot be resolved by editing either side alone:

  • the repo test pins minimumReleaseAgeExclude to internal, version-independent entries;
  • pnpm needs a version-pinned exemption for @tybys/wasm-util@0.10.4, published the same day it landed in the lockfile.

The two ways out both need an owner decision, so I am not picking one in a maintenance pass:

  1. Drop the '@tybys/wasm-util': '0.10.4' override (and the exclude entry with it) and regenerate both lockfiles, letting @napi-rs/wasm-runtime@1.2.4's own ^0.10.3 resolve to an older, age-compliant version. This keeps the invariant intact and is the option I'd prefer, but it changes dependency resolution for the Windows wasm fallback this PR exists to add, and it needs a real install to regenerate package-lock.json + pnpm-lock.yaml consistently.
  2. Relax the invariant to admit external version-pinned excludes. That is a supply-chain policy change and it weakens the guard for every future dependency, so it should not ride along in a Windows-CI-baseline PR.

Flagging one thing I found while measuring, unchanged by any of this: a second latent bypass of the same guard sits at pnpm-lock.yaml:196 (importers key '@tybys/wasm-util@0.10.4': 0.10.4), invisible to the test because its lockfile parser only collects keys ending in :. Whichever option is chosen, the guard probably wants to cover the lockfile too.

yiliang114 and others added 3 commits September 14, 2026 06:04
Picks up ef0b8a6 (build heap cap 3072 -> 4096 MB) and the two other main commits since faa3958.

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

Patrol-Run: qwen-pr-conflict/jmu0ce8qb32
…exempting it

Test (ubuntu-latest, Node 22.x) failed at scripts/tests/package-scripts.test.js:390
(run 34781999892), which pins minimumReleaseAgeExclude to internal,
version-independent entries, while the previous head carried
'@tybys/wasm-util@0.10.4' there to satisfy pnpm's minimumReleaseAge gate. The two
looked mutually exclusive, but only because the pin chose the newest release.

@node-rs/jieba-wasm32-wasi pulls in @napi-rs/wasm-runtime@1.2.4, which accepts
^0.10.3, and 0.10.3 was published 2026-06-23 -- well outside the 24h window that
rejected 0.10.4 (published 2026-09-13T08:00:13Z). So the pnpm override now pins
0.10.3, the exclude entry is dropped, and both lockfiles follow: package-lock.json
must lock the same version or scripts/check-lockfile.js's "pnpm lockfile against
package-lock.json" agreement gate fails, which also reddens the two check-lockfile
fixture branches in package-scripts.test.js because they copy the real lockfiles.

The pin lives on the pnpm side only, mirroring how the typescript pins already
diverge; npm has no release-age gate, so no package.json override is added and the
nested @tailwindcss/oxide-wasm32-wasi copy of @tybys/wasm-util is left alone. Once
0.10.4 ages out of the window, a maintainer can drop the override from both sides if
nothing else needs the pin.

Verified locally: pnpm 11.24.0 `install --lockfile-only` reports "Lockfile passes
supply-chain policies (1975 entries)"; scripts/tests/package-scripts.test.js 49
passed / 1 skipped (was 3 failed); scripts/tests/check-lockfile.test.js 14 passed;
prettier --check clean.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
Patrol-Run: qwen-pr-conflict/jmu0ce8qb32
@yiliang114

Copy link
Copy Markdown
Collaborator Author

@qwen-code /triage

@qwen-code-ci-bot

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

Copy link
Copy Markdown
Collaborator

Sandboxed verification: ❌ not passed — findings reported (agent verdict) - workflow run

Ran the PR in an isolated, token-free container: A/B against the base build, mock-free harness assertions, targeted gates. Advisory evidence for human reviewers — not a review, an approval, or a CI check.

Scripted assertions: 19 passed · 0 failed · 19 total

Flakiness gate: ⚠️ timeout — only 3 of 5 rounds fit the 15-minute budget; the completed rounds agreed

中文 — 判定:❌ 不通过 · 报告了发现(agent 判定)

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

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

抖动门:⚠️ timeout — only 3 of 5 rounds fit the 15-minute budget; the completed rounds agreed

Verification report

PR 11787 — fix(ci): restore Windows test baseline

Verdict: findings — 19/19 executed verification assertions passed (assertions.json: pass=19, fail=0, total=19); project gates at head: 1960 tests passed / 0 failed across 5 workspaces plus scripts/tests/package-scripts.test.js 49 passed, and npm run check:lockfile green. No blocking defect found; four non-blocking findings below, two of them measured latent hazards. Verified head: c2c00a1011199046b5b6fffbd8a7c67ed1de6265 (git rev-parse HEAD^2); base tip f9534f4395226cb485661c6ec0c233b9b2acdcf2 (HEAD^1).

中文摘要
  • 结论: findings(无阻断问题)。本 PR 的核心机制——把文件身份校验从会丢精度的 number 改为 bigint——被 A/B 证明是有承载力的:对两个 64 位 ID 落在同一个 double 里的不同文件,base 的守卫误放行{"rejected":false}),head 以 ELOOP 拒绝;两侧的正/负对照均通过(见 01-identity-ab-base-false-accepts-head-rejects.png)。POSIX 行为无回归:5 个 workspace 共 1960 个测试全绿,package-scripts.test.js 49 通过,check:lockfile 通过。
  • A/B 结论: 见下表「Central claim」。base 误放行 / head 拒绝,各 8/8 断言通过。
  • Findings: (1) @tybys/wasm-util 的 0.10.3 年龄豁免只在 pnpm 侧 pin,npm 侧无对应 overrides;实测全新解析会得到 0.10.4(当前 latest),下次重解析 package-lock.json 将导致 pnpm/npm 分裂并使 check:lockfile 报错;已实测一行对称修复有效。(2) 仓库内没有任何测试钉住 >2^53 的精确性:整块还原 web-shell-brand.ts / no-follow-open.ts 的 bigint 改动后测试仍全绿(同文件阳性对照可转红,证明非空跑);本报告附的 harness/identity-ab.mjs 即缺失的 fixture。(3) monitor-debug-store.ts 新增的 win32 分支在 Linux 上不可观测,仅真实 Windows runner 覆盖。(4) 新增依赖在所有平台装 2.2 MB 并引入 @emnapi/*@2.0.0-alpha.4 预发布链,PR 描述的「small dependency footprint」低估了它。
  • 未覆盖: 未执行任何 Windows 运行(Windows 侧为机制证明 + 仓库自述的 NTFS 舍入,非端到端);浅克隆 depth 2,7 个 commit 中仅 1 个可达,逐 commit 归因不可行;run-qwen-serve.test.ts 仅运行了被改动的那 1 个用例(427 个按名称过滤跳过)。

Central claim and A/B

Central claim. The PR's production changes make file-identity security guards exact where they were previously lossy: Windows reports 64-bit file IDs, Stats.ino as a JS number rounds above 2^53, and an equality/+1 comparison on the rounded value silently collapses distinct files onto one identity. The repo already documents exactly this hazard (packages/cli/src/commands/review/lib/same-file.ts: "Stats.ino carries the 64-bit NTFS file index rounded at the JS boundary, so two DISTINCT Windows files whose indices land in one double-rounding bucket surface with equal ino"). The claim is therefore: with { bigint: true }, the guards detect swaps they previously could not, and POSIX behaviour is unchanged.

A/B cells. Both arms are the real production module compiled from source (git show HEAD^1:… vs working tree), differing only by the PR hunk, bound to an identical mutable-fs seam so the Windows fallback path (no O_NOFOLLOW) is reachable; the head arm's compiled identity logic is logic-identical to the shipped packages/core/dist/src/utils/no-follow-open.js. The fake fs models one physical pair of files with IDs 2^53 and 2^53+1 and answers each stat call in the representation the caller requested, so the same harness runs unchanged on both arms.

cell build oracle (openSyncNoFollow on a swapped file) result
base control HEAD^1 source, number stats must accept the swap (guard blind above 2^53) accepted {"rejected":false,"fd":18} — as predicted
head PR source, bigint stats must reject with ELOOP rejected {"rejected":true,"code":"ELOOP"}
base positive control base in-range swap (4242→4243) rejected rejected ELOOP
head positive control head in-range swap (4242→4243) rejected rejected ELOOP
base negative control base identical identity accepted accepted
head negative control head identical identity accepted accepted

8/8 assertions per arm, 0 unexpected. Witness: 01-identity-ab-base-false-accepts-head-rejects.png (both arms as printed; base line BASE control accepts the swapped file … {"rejected":false,"fd":18}, head line HEAD rejects the swapped file with ELOOP).

Secondary claim 1 — the re-armed witnesses are non-vacuous. Commit 3cf18045 claims the web-shell-brand.test.ts BigIntStats fake re-arms the inode witness. Tested directly: removing the + 1n perturbation turns the suite red (1 failed / 89 passed, AssertionError: expected 'data:image/svg+xml,…' to be undefined). Claim confirmed. The run-qwen-serve.test.ts socket witness (the renamed inet_aton case) passes and asserts arg.port !== 0 against the real listener address, so it is not tautological.

Secondary claim 2 — dependency/lockfile consistency. npm run check:lockfile passes all five sub-checks including "pnpm lockfile matches package-lock.json"; scripts/tests/package-scripts.test.js (the minimumReleaseAgeExclude internal-only invariant that commit d1535642 said was red) is green: 49 passed / 1 skipped. The @tybys/wasm-util: 0.10.3 pin satisfies pnpm's age gate and both lockfiles agree today — but see Finding 1 for the drift trap.

Corrections

None required. The two commit-message claims I tested (the re-armed BigIntStats witness; the pnpm age-gate enforcement) both held under measurement.

Findings

F1 — Suggestion: the @tybys/wasm-util age pin has no npm-side counterpart, so the next lockfile re-resolution splits the two graphs

pnpm-workspace.yaml pins '@tybys/wasm-util': '0.10.3' under overrides:. Root package.json has no overrides entry for it (.npmrc only sets the registry), yet package-lock.json now carries a new hoisted node_modules/@tybys/wasm-util@0.10.3 (base locked only the nested 0.10.2 under @tailwindcss/oxide-wasm32-wasi). @tybys/wasm-util@0.10.4 is published and is latest. A fresh npm resolution of the same requirement resolves 0.10.4, not the committed 0.10.3:

# in an empty dir: npm init -y && npm install --package-lock-only @napi-rs/wasm-runtime@1.2.4
grep -o 'wasm-util-0\.10\.[0-9]*\.tgz' package-lock.json   # -> wasm-util-0.10.4.tgz

Consequence: the first npm install that re-resolves that subtree (e.g. bumping @node-rs/jieba) locks 0.10.4 on the npm side while pnpm's override keeps 0.10.3, and scripts/check-lockfile.js then fails with its own anticipated message ("a pnpm-workspace.yaml overrides: entry is deciding the version … and no npm-side regeneration can match it"). Today the gate is green, so this is latent, not blocking.

Measured fix (applied in a scratch project, not in the PR): adding "@tybys/wasm-util": "0.10.3" to root package.json overrides makes the same fresh resolve produce wasm-util-0.10.3.tgz. One line, symmetric with the pnpm pin. Precedent note: the three existing typescript pins live pnpm-side only and survive for the same fragile reason, so this PR follows house pattern — the finding is about the pattern's blast radius, not about author error.

F2 — Suggestion: nothing in the repo pins the >2^53 exactness that is the point of the bigint change

Mutation matrix (whole-hunk revert to HEAD^1, suite re-run; raw in mutation-matrix.txt, witness 02-mutation-matrix-survivors-and-positive-controls.png):

# reverted hunk suite result
M1 web-shell-brand.ts BigIntStats lstat/fstat web-shell-brand.test.ts SURVIVED — 90/90 green
M2 worktreeSessionService.ts bigint sidecar identity worktreeSessionService.test.ts KILLED — 1 failed / 31 passed, AssertionError: expected { state: 'invalid', … } to deeply equal … (the reason field differs)
M3 no-follow-open.ts bigint identity compare no-follow-open.test.ts SURVIVED — 17/17 green
M4 monitor-debug-store.ts win32 permission skip monitor-debug-store.test.ts SURVIVED — 13/13 green (unobservable on Linux, see F3)

The two survivors are not vacuous-harness artifacts: same-file positive controls prove the suites do exercise those guards — disabling the fd identity re-check in web-shell-brand.ts turns 2 tests red, and no-op'ing assertSameIdentity in no-follow-open.ts turns 7 red (positive-controls.txt). Classification: coverage gap, not dead code and not redundant defence — the behaviour is correct (the A/B above proves the mechanism), but every existing fixture perturbs inodes inside the safe-integer range, where number and bigint comparisons agree, so a regression of the bigint change would ship green. The missing fixture is exactly harness/identity-ab.mjs: perturb the fake's ino to two values that differ only above 2^53 and assert the refusal.

F3 — Suggestion: the new win32 branch is untestable off a real Windows runner

monitor-debug-store.ts now skips the mode & 0o077 / uid checks when process.platform === 'win32'. M4 survives because on Linux the mutation is a no-op by construction, and no test stubs process.platform; the suite's own mode assertions are if (process.platform !== 'win32')-guarded (lines 228, 412). So the branch is covered only by the Windows lane this PR is trying to make green. Acceptable for a CI-baseline PR, but a vi.stubGlobal('process', {…platform:'win32'}) case would make it reviewable anywhere. Related accepted-tradeoff boundary, checked and correct: same-file.test.ts now skips when !Number.isSafeInteger(inode), matching production's deliberate degradation to canonical-spelling comparison — the skip is honest, not a coverage dodge.

F4 — Suggestion: the dependency footprint is understated, and it ships an alpha prerelease chain on every platform

@node-rs/jieba-wasm32-wasi@2.0.2 is a non-optional dependency of packages/qwen-live, so it installs on Linux and macOS too: 2.2 MB on top of the 11 MB native @node-rs/jieba (+20%), pulling @emnapi/core@2.0.0-alpha.4, @emnapi/runtime@2.0.0-alpha.4 (nested under @node-rs/jieba-wasm32-wasi/node_modules/), @emnapi/wasi-threads@2.0.1 and @napi-rs/wasm-runtime@1.2.4. The PR body calls this "a small dependency footprint". Reachability is real and verified in the shipped loader (node_modules/@node-rs/jieba/index.js:646,655 requires @node-rs/jieba-wasm32-wasi when the native binding fails to load), so the dependency is not dead — but the alpha-prerelease chain and the all-platform cost belong in the risk section, and an optionalDependencies entry keyed to win32 would avoid installing it elsewhere.

Checked and acceptable (not findings)

  • integrations/external-context-mem0/src/auto-recall.integration.test.ts adds socket.on('error', () => undefined) on the decoy stalled-TLS server (the "expected Windows socket reset" commit). The suppression is scoped to the fixture's own decoy socket; the test's oracle — the CLI child exiting successfully after the provider timeout — is untouched, and an aborted client resetting the decoy is the scenario by design. Information is not lost anywhere a reader looks.
  • git config --global core.autocrlf false in ci.yml/windows-runner-smoke.yml is load-bearing despite .gitattributes (* text=auto eol=lf): the attribute governs the main checkout, but the tests create temporary repos that inherit no .gitattributes, where a Windows-installed autocrlf=true would produce CRLF checkouts and break path/identity assertions. It is set inside the isolated HOME=${{ runner.temp }}/qwen-ci-home after that directory is created, and is a no-op on Linux where the same step also runs.

Not covered

  • No Windows execution. This container is Linux. The Windows half of the central claim is proven by mechanism (the lossy-double A/B above) plus the repo's own documented NTFS rounding, not by a Windows run; the PR's own Windows validation runs (34713863264, 34800332338) are cited by the author and were not independently observed.
  • Per-commit attribution impossible. The checkout is depth 2: git rev-list HEAD^1..HEAD^2 returns 1 commit while $QWEN_VERIFY_CONTEXT lists 7 (b7a3ed7e, 6099768f, 3cf18045, d1535642, adba6daf, 3bd8e9bbe2, c2c00a10). All verification is against the aggregate HEAD^1..HEAD diff.
  • run-qwen-serve.test.ts: only the one changed case ran (-t 'reports what the socket bound…', 1 passed / 427 skipped by filter); the remaining 427 cases in that 13k-line file were not executed here.
  • Full workspace suites and npm run typecheck were not re-run end to end; the CI image had already built every workspace at HEAD (which type-checks), and I ran the changed-file suites instead (counts above).
  • The fresh-resolve measurement in F1 used the public registry from this container; it proves what npm's resolver picks for ^0.10.3 today, not what it picked when the author generated the lockfile.
  • verify-capture.mjs was used as shipped; both evidence images are its output.

Methodology

Environment: the CI verify container (node:22-bookworm, no GitHub token), working tree = refs/pull/11787/merge at depth 2, npm ci + npm run build pre-run at HEAD. Harnesses live in harness/: identity-ab.mjs drives the real production module compiled per-arm by esbuild from HEAD^1 and working-tree sources behind fs-shim.mjs (a shallow mutable copy of node:fs with O_NOFOLLOW removed, mirroring Windows), asserting reachability (stat call counts) before the oracle; mutation-matrix.sh and positive-controls.sh revert or disable one hunk at a time via git show HEAD^1:<file> / python3 string edits and always restore (git status --porcelain empty at exit). Gates ran per workspace with npx vitest run <files> from the package directory. Raw logs: logs-head-suites.txt, logs-cli-suites.txt, logs-mutation.txt, logs-controls.txt; images in evidence/.

Flakiness gate log

rounds=5 files=27 skipped=0
file integrations/external-context-mem0/src/auto-recall.integration.test.ts: (cd integrations/external-context-mem0) npx --no-install vitest run ./src/auto-recall.integration.test.ts
file packages/acp-bridge/src/sessionAttachments.test.ts: (cd packages/acp-bridge) npx --no-install vitest run ./src/sessionAttachments.test.ts
file packages/cli/src/acp-integration/acpAgent.test.ts: (cd packages/cli) npx --no-install vitest run ./src/acp-integration/acpAgent.test.ts
file packages/cli/src/commands/review/cleanup.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/cleanup.test.ts
file packages/cli/src/commands/review/findings.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/findings.test.ts
file packages/cli/src/commands/review/lib/git.integration.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/lib/git.integration.test.ts
file packages/cli/src/commands/review/lib/same-file.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/lib/same-file.test.ts
file packages/cli/src/commands/review/lib/worktree.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/lib/worktree.test.ts
file packages/cli/src/commands/review/repo-context.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/repo-context.test.ts
file packages/cli/src/commands/review/scratch-tree.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/scratch-tree.test.ts
file packages/cli/src/commands/review/test-efficacy.test.ts: (cd packages/cli) npx --no-install vitest run ./src/commands/review/test-efficacy.test.ts
file packages/cli/src/serve/conversations/conversation-workspace.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/conversations/conversation-workspace.test.ts
file packages/cli/src/serve/conversations/standalone-deletion-journal.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/conversations/standalone-deletion-journal.test.ts
file packages/cli/src/serve/run-qwen-serve.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/run-qwen-serve.test.ts
file packages/cli/src/serve/session-attachments-root.test.ts: (cd packages/cli) npx --no-install vitest run ./src/serve/session-attachments-root.test.ts
file packages/cli/src/services/web-shell-brand.test.ts: (cd packages/cli) npx --no-install vitest run ./src/services/web-shell-brand.test.ts
file packages/cli/src/ui/opentui/input-prompt.test.tsx: (cd packages/cli) npx --no-install vitest run ./src/ui/opentui/input-prompt.test.tsx
file packages/cli/src/ui/opentui/opentui-footer.test.tsx: (cd packages/cli) npx --no-install vitest run ./src/ui/opentui/opentui-footer.test.tsx
file packages/core/src/services/session-sources.test.ts: (cd packages/core) npx --no-install vitest run ./src/services/session-sources.test.ts
file packages/core/src/services/worktreeSessionService.test.ts: (cd packages/core) npx --no-install vitest run ./src/services/worktreeSessionService.test.ts
file packages/core/src/tools/workflow/workflow.test.ts: (cd packages/core) npx --no-install vitest run ./src/tools/workflow/workflow.test.ts
file packages/core/src/utils/no-follow-open.test.ts: (cd packages/core) npx --no-install vitest run ./src/utils/no-follow-open.test.ts
file packages/qwen-live/src/language-preferences.test.ts: (cd packages/qwen-live) npx --no-install vitest run ./src/language-preferences.test.ts
file packages/qwen-live/src/memory/config.test.ts: (cd packages/qwen-live) npx --no-install vitest run ./src/memory/config.test.ts
file packages/qwen-live/src/memory/service.test.ts: (cd packages/qwen-live) npx --no-install vitest run ./src/memory/service.test.ts
file packages/qwen-live/src/proactive/monitor-debug-store.test.ts: (cd packages/qwen-live) npx --no-install vitest run ./src/proactive/monitor-debug-store.test.ts
file packages/vscode-ide-companion/src/diff-manager.test.ts: (cd packages/vscode-ide-companion) npx --no-install vitest run ./src/diff-manager.test.ts


per-file results (P=pass F=fail I=infra-exit, one letter per run):
  integrations/external-context-mem0/src/auto-recall.integration.test.ts: PPPP
  packages/acp-bridge/src/sessionAttachments.test.ts: PPPP
  packages/cli/src/acp-integration/acpAgent.test.ts: PPPP
  packages/cli/src/commands/review/cleanup.test.ts: PPPP
  packages/cli/src/commands/review/findings.test.ts: PPPP
  packages/cli/src/commands/review/lib/git.integration.test.ts: PPPP
  packages/cli/src/commands/review/lib/same-file.test.ts: PPPP
  packages/cli/src/commands/review/lib/worktree.test.ts: PPPP
  packages/cli/src/commands/review/repo-context.test.ts: PPPP
  packages/cli/src/commands/review/scratch-tree.test.ts: PPP
  packages/cli/src/commands/review/test-efficacy.test.ts: PPP
  packages/cli/src/serve/conversations/conversation-workspace.test.ts: PPP
  packages/cli/src/serve/conversations/standalone-deletion-journal.test.ts: PPP
  packages/cli/src/serve/run-qwen-serve.test.ts: PPP
  packages/cli/src/serve/session-attachments-root.test.ts: PPP
  packages/cli/src/services/web-shell-brand.test.ts: PPP
  packages/cli/src/ui/opentui/input-prompt.test.tsx: PPP
  packages/cli/src/ui/opentui/opentui-footer.test.tsx: PPP
  packages/core/src/services/session-sources.test.ts: PPP
  packages/core/src/services/worktreeSessionService.test.ts: PPP
  packages/core/src/tools/workflow/workflow.test.ts: PPP
  packages/core/src/utils/no-follow-open.test.ts: PPP
  packages/qwen-live/src/language-preferences.test.ts: PPP
  packages/qwen-live/src/memory/config.test.ts: PPP
  packages/qwen-live/src/memory/service.test.ts: PPP
  packages/qwen-live/src/proactive/monitor-debug-store.test.ts: PPP
  packages/vscode-ide-companion/src/diff-manager.test.ts: PPP

verdict: timeout
summary: only 3 of 5 rounds fit the 15-minute budget; the completed rounds agreed

--- per-invocation detail (full copy in the artifact) ---
round 1 · integrations/external-context-mem0/src/auto-recall.integration.test.ts: P (exit 0)
round 1 · packages/acp-bridge/src/sessionAttachments.test.ts: P (exit 0)
round 1 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/cleanup.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/findings.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/lib/git.integration.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/lib/same-file.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/lib/worktree.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/repo-context.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/scratch-tree.test.ts: P (exit 0)
round 1 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/conversations/conversation-workspace.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/conversations/standalone-deletion-journal.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/run-qwen-serve.test.ts: P (exit 0)
round 1 · packages/cli/src/serve/session-attachments-root.test.ts: P (exit 0)
round 1 · packages/cli/src/services/web-shell-brand.test.ts: P (exit 0)
round 1 · packages/cli/src/ui/opentui/input-prompt.test.tsx: P (exit 0)
round 1 · packages/cli/src/ui/opentui/opentui-footer.test.tsx: P (exit 0)
round 1 · packages/core/src/services/session-sources.test.ts: P (exit 0)
round 1 · packages/core/src/services/worktreeSessionService.test.ts: P (exit 0)
round 1 · packages/core/src/tools/workflow/workflow.test.ts: P (exit 0)
round 1 · packages/core/src/utils/no-follow-open.test.ts: P (exit 0)
round 1 · packages/qwen-live/src/language-preferences.test.ts: P (exit 0)
round 1 · packages/qwen-live/src/memory/config.test.ts: P (exit 0)
round 1 · packages/qwen-live/src/memory/service.test.ts: P (exit 0)
round 1 · packages/qwen-live/src/proactive/monitor-debug-store.test.ts: P (exit 0)
round 1 · packages/vscode-ide-companion/src/diff-manager.test.ts: P (exit 0)
round 2 · integrations/external-context-mem0/src/auto-recall.integration.test.ts: P (exit 0)
round 2 · packages/acp-bridge/src/sessionAttachments.test.ts: P (exit 0)
round 2 · packages/cli/src/acp-integration/acpAgent.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/cleanup.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/findings.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/lib/git.integration.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/lib/same-file.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/lib/worktree.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/repo-context.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/scratch-tree.test.ts: P (exit 0)
round 2 · packages/cli/src/commands/review/test-efficacy.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/conversations/conversation-workspace.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/conversations/standalone-deletion-journal.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/run-qwen-serve.test.ts: P (exit 0)
round 2 · packages/cli/src/serve/session-attachments-root.test.ts: P (exit 0)
round 2 · packages/cli/src/services/web-shell-brand.test.ts: P (exit 0)
round 2 · packages/cli/src/ui/opentui/input-prompt.test.tsx: P (exit 0)
round 2 · packages/cli/src/ui/opentui/opentui-footer.test.tsx: P (exit 0)
round 2 · packages/core/src/services/session-sources.test.ts: P (exit 0)
round 2 · packages/core/src/services/worktreeSessionService.test.ts: P (exit 0)
round 2 · packages/core/src/tools/workflow/workflow.test.ts: P (exit 0)
round 2 · packages/core/src/utils/no-follow-open.test.ts: P (exit 0)
round 2 · packages/qwen-live/src/language-preferences.test.ts: P (exit 0)
round 2 · packages/qwen-live/src/memory/config.test.ts: P (exit 0)
round 2 · packages/qwen-live/src/memory/service.test.ts: P (exit 0)
round 2 · packages/qwen-live/src/proac

...truncated -- full content in the run artifacts.

Evidence images

01-identity-ab-base-false-accepts-head-rejects

02-mutation-matrix-survivors-and-positive-controls

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

Qwen Code · sandboxed verification

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Partially reviewed — gaps disclosed.

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

  • R1-11 boot-level test no longer discriminates bind from boundAddress — already reported (review 5191690673, the open CHANGES_REQUESTED body, item 2 of 3)

Not reviewed: reverse-audit round 5's three incidental candidates — never verified, because the loop stopped on the time budget before a round could rule on them.

Not reviewed: build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

Not reviewed: build-and-test — Test (windows-latest, Node 22.x) was skipped by the PR-event matrix filter; Windows ground was covered instead by the dispatched validation run at ancestor 6099768, whose logs this review analyzed, not by a run at this head.

Not reviewed: build-and-test — Test (macos-latest, Node 22.x) was skipped in CI and no darwin run exists at this head; the diff's platform branches were walked analytically, not executed.

Not explored to full depth (tool budget reached): "agent reverse-audit (round 4)": executing any changed suite — a run needs npm run build in this shared worktree first, so every conclusion above is read-based, including both findings' Windo…; "agent reverse-audit (round 4)": standalone-deletion-journal.test.ts:348-430 (tail of the "same-session fence" body) and worktree.test.ts 's overwriteGitfile helper region beyond the diff …; "agent 6c": did not cross-check packages/vscode-ide-companion/src/diff-manager.ts production path resolution against the new path.join -based Uri.joinPath mock (only t…; "agent 6c": did not read managedConversationExpectation / getConversationDirectoryName behind the new expectation.child.inode === 0 skip in acpAgent.test.ts:7373-7378; "agent 6c": did not execute any of the changed suites or npm run typecheck / check:lockfile ; all findings are from reading the post-change sources, and 5 more.

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

Not linted (tool limitation, not a blocker): .github/workflows/ci.yml — actionlint embedded-shell source mapping is not yet supported; .github/workflows/windows-runner-smoke.yml — actionlint embedded-shell source mapping is not yet supported.

Test Plan (not a blocker): 325 tests passing — this review observed 2070, 31231, 26315, 1016, 587, 2021, 7990 passed.

中文说明

仅完成部分审查,审查缺口已披露。

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

未审查(原文为英文):reverse-audit round 5's three incidental candidates — never verified, because the loop stopped on the time budget before a round could rule on them.

未审查(原文为英文):build-and-test — Integration Tests (CLI, No Sandbox) was skipped in CI and its suite did not run locally.

未审查(原文为英文):build-and-test — Test (windows-latest, Node 22.x) was skipped by the PR-event matrix filter; Windows ground was covered instead by the dispatched validation run at ancestor 6099768, whose logs this review analyzed, not by a run at this head.

未审查(原文为英文):build-and-test — Test (macos-latest, Node 22.x) was skipped in CI and no darwin run exists at this head; the diff's platform branches were walked analytically, not executed.

未探索到全部深度(达到工具调用预算):"agent reverse-audit (round 4)"executing any changed suite — a run needs npm run build in this shared worktree first, so every conclusion above is read-based, including both findings' Windo…"agent reverse-audit (round 4)"standalone-deletion-journal.test.ts:348-430 (tail of the "same-session fence" body) and worktree.test.ts 's overwriteGitfile helper region beyond the diff …"agent 6c"did not cross-check packages/vscode-ide-companion/src/diff-manager.ts production path resolution against the new path.join -based Uri.joinPath mock (only t…"agent 6c"did not read managedConversationExpectation / getConversationDirectoryName behind the new expectation.child.inode === 0 skip in acpAgent.test.ts:7373-7378"agent 6c"did not execute any of the changed suites or npm run typecheck / check:lockfile ; all findings are from reading the post-change sources,另有 5 条。

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

未检查(工具限制,非阻断):.github/workflows/ci.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持;.github/workflows/windows-runner-smoke.yml——actionlint 对 workflow 内嵌 shell 的源映射尚未支持。

Test Plan(非阻断):325 tests passing — this review observed 2070, 31231, 26315, 1016, 587, 2021, 7990 passed

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

Comment thread packages/core/src/utils/no-follow-open.ts
Comment thread .github/workflows/ci.yml
Comment thread packages/qwen-live/package.json
Comment thread packages/cli/src/services/web-shell-brand.test.ts
Comment thread pnpm-workspace.yaml
Comment thread .github/workflows/windows-runner-smoke.yml
Comment thread packages/cli/src/commands/review/lib/worktree.test.ts
Comment thread packages/cli/src/commands/review/lib/worktree.test.ts Outdated
Comment thread packages/cli/src/serve/conversations/conversation-workspace.test.ts
@qwen-code-ci-bot

Copy link
Copy Markdown
Collaborator

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

中文说明

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

@yiliang114

Copy link
Copy Markdown
Collaborator Author

Windows ECS validation is green on the current head (d7b8b97c68f8): https://github.com/QwenLM/qwen-code/actions/runs/34806714807. Validate runner finished in 23m16s; setup, dependency installation, the complete npm run test:ci command, report generation, and cleanup all passed. The root-script segment completed 75 files and 1,522 tests with no failures.

Each of these was measured, not argued; the mutation named in every case is
the one that used to leave the suite green.

- no-follow-open.test.ts: add the >2^53 identity case. Removing
  `{ bigint: true }` from all four production stat call sites left 17/17
  green, so the Windows-precision conversion this PR exists for was
  unwitnessed. The fixture uses 2^60 offsets, where the double spacing is
  256 and both ids collapse to one value; 2^53+1 / 2^53+2 do NOT collapse
  (2^53+2 is exactly representable), so those constants cannot pin it.
  Two-arm measured: intact 18/18 pass, mutated exactly 1 fails.
- web-shell-brand.test.ts: stat the planted regular file with
  `{ bigint: true }`. Production compares BigIntStats with a bare `!==`, so
  the number-backed fake tripped `dev` on a type mismatch alone and would
  have refused the correct file too. Probed both arms: pre-fix
  `typeof expectedStat.dev === 'number'` with the `dev` clause true,
  post-fix both sides bigint with `dev` false and the refusal driven by a
  real inode mismatch.
- conversation-workspace.test.ts: move the `inodeVerifiable` gate below the
  two `toBe(identity)` assertions. They are inode-independent, so gating
  them deleted a witness the Windows lane was green on. Modelled an
  unverifiable-inode host: the test reports skipped, not failed, which is
  only reachable once both assertions have passed.
- worktree.test.ts: split `localFilterCommands` in two. The fail-closed
  branch is platform-free by construction and the base Windows log shows it
  passing there; only the `a\nb` fixture is Windows-impossible, so only that
  half keeps the gate.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
yiliang114 and others added 2 commits September 14, 2026 18:06
Both workflows now run `git config --global core.autocrlf false` twice: once
before the checkout and once inside the test step, where HOME/USERPROFILE are
redirected to a freshly created empty directory. The second line therefore
writes a different global config than its twin, and on ci.yml's hosted
windows-2022 fallback the pre-checkout step never runs at all because it is
gated on `runner.environment == 'self-hosted'`.

It was the only one of the three occurrences with no comment, so deduping it
looks free and silently returns CRLF conversion to every git operation the
suites perform. Replaying no-ak-integration-ci.test.js's own assertions with
the second occurrence deleted gives identical indices and still passes, so no
test notices the removal.

Comments only. The ordering assertions search for the FIRST occurrence and
none of the added text matches a needle they look for; the 14 assertions in
no-ak-integration-ci.test.js still pass.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
The Lint & Static lane's "Check lint gate freshness" step failed at
7c5b501: main advanced 16 commits past this branch's merge base
ef0b8a6 and changed two of the gate-defining files the check watches,
eslint.config.js and .github/scripts/check-lint-gate-freshness.mjs. The lane
checks out refs/pull/N/head alone, so a green run only proves the branch
passes the gate as the branch defines it — incorporating main is what makes
the branch validate against the base's current gate.

No conflicts: main did not touch ci.yml, so the autocrlf why-comments added
here are unaffected, and none of the four re-armed test files changed upstream.

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

@chiga0 chiga0 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Scope: Production changes (no-follow-open.ts, worktreeSessionService.ts, web-shell-brand.ts, native-lsp-service.ts, host-client.ts, artifact-snapshots.ts) read fully. CI: ci.yml, windows-runner-smoke.yml. Triage: Standard/Deep.

Not reviewed: Windows runtime behaviour; CI state.

Findings

No blocking findings.

Bot R1-1 (no-follow-open.ts, BigInt regression) — refuted. Both call sites now use { bigint: true } for both before and after. assertSameIdentity wraps both sides in BigInt() which is idempotent on bigints and converting on numbers, so mixed-type pairs still compare correctly. The 'refuses an identity that differs only above 2^53' test is the case that motivated this change. R1-1 does not hold against this diff.

Bot R1-2 (ci.yml, autocrlf) — unsettled. Cannot verify without a Windows runner. PR provides evidence of Windows CI passing at d7b8b97c68f8. Not added to approvalBlockers.

Bot R1-3 (WASI tokenizer) — unsettled; Windows CI evidence provided. The PR demonstrates the fallback path loads on Windows. Not blocking.

Bot R1-6 (second BigInt mock in web-shell-brand.test.ts) — minor. One of the two fstatSync mocks in the test drives the isFile() arm, not the dev/ino check, so no false negative — just a cleanup opportunity.

Own checks: pathToFileURL fix in host-client.ts correctly handles Windows drive paths for --import. BigInt comparisons in worktreeSessionService.ts consistent throughout. monitor-debug-store.ts uid guard now correctly type-checks process.getuid. vi.restoreAllMocks() additions prevent mock leakage. All correct.

No blocking findings. Approval blockers: none.

Reviewed with AI assistance.

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed head b36b347b against base e04f2ec5. No new blocker; two notes and one process caveat.

  • The ci-bot blocker is fixed at this head. pnpm-workspace.yaml's minimumReleaseAgeExclude now holds only '@qwen-code/channel-base'; the tokenizer pin lives under the overrides block with an explanatory comment about the age-compliant version, so scripts/tests/package-scripts.test.js is no longer broken by an added entry. I re-read both files at the head rather than trusting the resolved flags.
  • R1-26 still stands. The skips at standalone-deletion-journal.test.ts:144/261/309 are still it.skipIf(process.platform === 'win32') with no reason recorded, and the same-file.test.ts gate widened from ino === 0 to !Number.isSafeInteger(inode). I do not read the widening as a coverage loss worth blocking: same-file.ts documents that only safe positive values are exact identity proof and degrades to canonical spelling below that, and mainstream Linux inode values still run the assertion. What is missing is the one-line comment naming the volume class that now skips — which is what R1-26 asks for.
  • Process caveat. The Windows evidence in the PR body is at d7b8b97c, which predates this head (b36b347b, and the 21f40604 merge), so the head itself is unvalidated on Windows. A Linux-green run cannot cover that lane; a re-dispatch before merge would.

Not chased to a conclusion (non-blocking): native-lsp-service.ts:782 replaces fs.accessSync(filePath, R_OK) with an isUsableWorkspaceSymbolFile check that also requires isFile() and root-prefix containment — a behaviour change on every platform inside a Windows-baseline PR; and artifact-snapshots.ts:187 hoists a readdir out of the !operationId short-circuit, adding an unconditional syscall with no functional change.

Reviewed with AI assistance.

@yiliang114
yiliang114 added this pull request to the merge queue Sep 14, 2026
Merged via the queue into main with commit c666ec1 Sep 14, 2026
92 of 93 checks passed

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

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

APPROVE

已核对 head b36b347bf53dad0c1aad06e51b495d4a5648876c(vs base e04f2ec5d4)。required 全部完成且 success(Test (ubuntu-latest, Node 22.x)Lint & StaticIntegration Tests (no-AK, No Sandbox)web-shell E2E SmokeTUI parity snapshotsOpenTUI no-flicker gate;27 项 pass、0 fail,唯一 pending 是评审机器人自身档 review-pr)。

这不只是一次测试加跳过,生产侧改动我逐处读过并认为正确:

  • packages/core/src/utils/no-follow-open.tsassertSameIdentity 改收 { dev, ino }(number | bigint)并用 BigInt() 比较,两侧取统计信息都改为 { bigint: true }lstatSyncfstatSynchandle.stat({bigint:true}))。这修的是真实缺陷:NTFS 上 number 形式的 dev/ino 有精度损失,两个不同文件可能比出相同值;hasVerifiableInode 的「无法验证即拒绝」前置门保持不变,因此方向是收紧不是放宽。
  • packages/cli/src/serve/fs/workspace-file-system.tsresolveSameHostToolWriteTarget 的三处 lstat 同样改为 bigint: true,与上面的身份比较口径一致。
  • packages/core/src/code-mode/host-client.ts:dev 入口的 --import 参数由裸 require.resolve('tsx') 改为 pathToFileURL(...).href,这是 Node ESM loader 的正确形式,也正好是 Windows 盘符路径失败的那个成因;生产构建分支未动。
  • packages/core/src/lsp/native-lsp-service.ts:预热候选从 fs.accessSync(R_OK) 改为复用 isUsableWorkspaceSymbolFile 并抛出有信息的错误,仍在原 try 内,失败处理路径不变。
  • .github/workflows/ci.ymlwindows-runner-smoke.yml 各补一行 git config --global core.autocrlf false,注释说明为什么不是重复:该步骤重定向了 HOME/USERPROFILE,读到的是刚创建的空 profile,而预 checkout 那一处又只在 self-hosted 上生效 —— 删掉这行会让 CRLF 转换回到测试里且没有测试会报红。这个理由我按两处步骤的 gating 核过,成立。
  • pnpm-workspace.yaml@tybys/wasm-util 钉到 0.10.3:0.10.4 刚发布、被 minimumReleaseAge 门禁挡下,注释说明为什么选「钉版本」而不是「加豁免」(package-scripts.test.js 钉住该豁免列表只放内部包)。方向我认同:不去放宽门禁,而是绕开新鲜度问题。

记录三点(不阻塞)

  1. packages/qwen-live/src/proactive/monitor-debug-store.tsprivateDirectory() 现在只在非 win32 上检查 mode & 0o077 与 uid。判断我认同(Windows 上 POSIX mode/uid 不可信,原检查在 Windows 等于必然失败),但它确实是一次平台性放宽:Windows 侧现在只剩 isDirectory()isSymbolicLink(),目录可写性要靠 ACL。建议在同一处注释里点明「Windows 依赖 ACL,POSIX 位检查不适用」,并考虑后续补一条 ACL 校验。
  2. R1-26 仍挂着未解决,作者明确回复这是需要 owner 决策的安全边界问题(lib/same-file.ts:24statSync 不带 bigint 使硬链接身份退化成 realpath 拼写比较,findings.ts:1274/repo-context.ts:371 的抗别名门在 Windows 上失效;standalone-deletion-journal.ts:118-127 同理)。这两处都不是本 PR 引入的,本 PR 只是移除了它们在 CI 里的信号,所以我按你的处理签字;但请按你自己说的「需要 owner 决策」把它落成 issue(PR 描述现在是 Linked Issues: N/A),否则这条 visibly open 的线程会在合并后失去着力点。
  3. 平台跳过里有些点没有就地原因:commands/review/cleanup.test.ts:339serve/conversations/standalone-deletion-journal.test.ts:262:310if (process.platform === 'win32') { ctx.skip(); } 上面没有一行说明(对比 lib/same-file.test.ts:93findings.test.ts:1459 那种写清了 inode 退化原因的写法)。我逐个查了每个 ctx.skip() 前的条件,全部是 win32 或 inode 可验证性门(没有无条件跳过),因此 Linux 侧覆盖不损失,这里只差注释。补一行「为什么跳过」即可,读代码的人需要能区分「平台工件」与「真有生产缺口」。

yiliang114 added a commit that referenced this pull request Sep 14, 2026
The Lint & Static lane's step 8 (Check lint gate freshness) failed on 8e1a0b9 in 23s: .github/workflows/ci.yml changed on main at c666ec1 (fix(ci): restore Windows test baseline, #11787) after this branch last incorporated main at 351b5fc, and the compare reported diverged. The lane checks out the branch head alone, so its green would have validated a stale gate. No product change.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
qwen-code-dev-bot added a commit that referenced this pull request Sep 14, 2026
Resolve overlaps with #11787's Windows test-baseline restore:

- monitor-debug-store.ts: take main's win32 gate (skips POSIX mode+uid
  checks on Windows). Semantically equivalent to this PR's mode-only
  gate, and already verified on the Windows CI lane.
- memory/config.test.ts: take main's tmpdir-based fixtures, which
  subsume this PR's join->resolve assertion tweak for the same Windows
  path failure. File is now identical to main.
- language-preferences/service/monitor-debug-store tests: keep this
  PR's dedicated it.skipIf(win32) permission tests, which cover the
  same 0600/0700 assertions that #11787 gated inline, and report the
  skip instead of passing a test that asserted nothing.

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
wenshao pushed a commit to water-in-stone/qwen-code that referenced this pull request Sep 14, 2026
…QwenLM#11853)

Follow-up to QwenLM#11787, carrying review finding R1-26 from that PR. It charged
that several Windows skips turned a *measured* failure into a green skip with
no reason recorded at the code site, and that in two cases the red had been
witnessing a real production gap rather than a portability artifact. It split
the remedy in two and asked that they not collapse into one generic "add a
comment". Both halves are here; the production conversion itself is not, and
is tracked in QwenLM#11848.

(a) Fixture artifact — witness restored rather than annotated.
`cleanup.test.ts`'s "reads process.cwd() ONCE per run" was skipped on Windows
because the count came back 16 against an expected 1. The cause is that file's
module-level `node:path` posix mock: a real Windows cwd (`C:\…`) is not
posix-absolute, so every downstream `resolve()` re-reads `process.cwd()`.
Production uses win32 semantics there and has exactly one live
`process.cwd()` (`cleanup.ts:754`) with no platform branch, so the invariant
was never broken — only the measurement was. Pinning the spy to the fixture
root (`/repo`, the spelling every other fixture in that file keys on) makes
the count platform-independent, so the skip is deleted and the witness now
runs on every lane. Mutation-checked: adding one downstream `process.cwd()`
read reds it with "expected 1 times, but got 2 times".

(b) Real gaps — the gates now say so, and one issue owns them.
`findings.test.ts`, `repo-context.test.ts`, `same-file.test.ts` and
`standalone-deletion-journal.test.ts` each carry a comment stating that on a
volume whose ids exceed the safe-integer range the guard under test is INERT,
not merely untestable, name the production call site responsible, and point at
QwenLM#11848. `same-file.test.ts` previously conflated the two cases its single gate
covers — `ino === 0` (FAT/exFAT/SMB) degrades to canonical spellings BY DESIGN
and is pinned by the test immediately below it, while an NTFS 64-bit file index
degrades only because `tryStat` asks for a number-backed `Stats`. Those need
different responses and now read differently.

QwenLM#11848 carries the shared root cause, the two comparators to convert
(`lib/same-file.ts:24`, `standalone-deletion-journal.ts:678`), and two
constraints: do not widen `hasVerifiableInode`'s signature, because core's
looser predicate is deliberate and tightening it would flip
`assertVerifiableTranscriptIdentity` on >2^53 Windows transcript inodes; and
`refuses inode identity above the safe-integer range` must be rewritten as a
genuine BigIntStats fake rather than deleted, since its mock assigns
`stats.ino = 2 ** 60` as a *number* to two distinct files.

Comments plus one spy change; no production code touched.

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

Copy link
Copy Markdown
Collaborator

Released in v0.23.4.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants