diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e1251fa..adaf5e6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,16 +92,26 @@ jobs: run: pnpm typecheck - name: Lint run: pnpm lint - # `test:windows` caps the fork pool at 2 instead of `pnpm test`'s - # unbounded default. Five specs in this suite really start processes - # (tests/agent-pty, pty-deps, pty-helpers, install-powershell, - # smoke): node-pty's ConPTY helper and a cold `powershell.exe` are - # both console-bound, and this 2-core runner made parallel spawning - # the amplifier behind every red `Test` step in the 2026-09-09/10 - # window — six real 5000 ms timeouts (agent-pty `:71`/`:106`, - # install-powershell `:105`, the latter measuring 12.1 s) and two - # silent vitest worker deaths. The test bodies now carry their own - # explicit timeouts as well; this just stops them starving each other. + # `test:windows` runs the whole suite in ONE fork (`--maxWorkers=1`), + # not `pnpm test`'s unbounded pool. Two distinct Windows-only failures + # motivated it, both recorded in docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md: + # + # 1. Six red runs were real 5000 ms vitest timeouts in specs that + # really start a process (tests/agent-pty:71/:106, + # tests/install-powershell:105 — a cold powershell.exe start + # measured 12.1 s, tests/git.spec.ts:85 at 9.6 s). The suite now + # declares its own budgets (global testTimeout 15 s, 30 s in the + # two heaviest describes), and serial execution stops the + # process-spawning specs starving each other on a 2-core runner. + # 2. Four runs died at teardown with `[vitest-pool]: Worker forks + # emitted error / Worker exited unexpectedly` while reporting + # N-1 of N files passed and zero FAIL lines — a worker recycling + # race, not a test failure. It survived --maxWorkers=2, so the + # lane now recycles nothing at all. + # + # No retries: they would hide a real regression instead of removing the + # race. The cost is wall-clock (the Windows step runs several minutes + # against a 30-minute job budget), not signal. - name: Test run: pnpm test:windows - name: Build diff --git a/docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md b/docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md index d8c4195..c3e6a06 100644 --- a/docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md +++ b/docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md @@ -44,7 +44,8 @@ DSH `dsh-v0.1.5-rc.1..dsh-v0.1.5-rc.2`(上游 4 个 commit / 300 个变更文 - 证据:run `34500290681`(295615abb)、`34448911025`(bbf953b8a) 报 `[vitest-pool]: Worker forks emitted error / Caused by: Error: Worker exited unexpectedly`(`Errors 1 error`),同一份日志里 `0 FAIL`、125 个文件全过;后者静默吞掉一个文件的 10 条结果。同一 commit `8ae527170` 的另一次 run 里 `ci-windows` 是**绿的** → 该步骤可证伪地非确定。 - 归因(**推断,非确证**):真起进程的 spec 有 5 个(`agent-pty` / `pty-deps` / `pty-helpers` / `install-powershell` / `smoke`),node-pty 的 ConPTY 辅助进程与冷启 `powershell.exe` 都绑定控制台,在默认**无界 fork 池**下并行抢占;本地 macOS 无法复现该模式,因此不做无证据的猜测性改动(不加 `retry`,不改默认 pool)。 -- 修法:`package.json` 新增 `test:windows`(`vitest run --maxWorkers=2`),`ci-windows` 的 `Test` 步骤改跑它——**只在 Windows CI 收敛并发,`pnpm test` 的本地语义不变**。若真实 Windows 结果仍复现,按"升级而非掩盖"的原则继续收敛(同 PR 内串行化这些 spec,仍不加 retry),并在 PR 中如实标注。 +- 修法(两次迭代,第一次被真实结果证否):先上 `test:windows`(`vitest run --maxWorkers=2`)——**真实 Windows 上仍复现**(run `34574847774`:126/127 文件全过、`0 FAIL`、`Unhandled Error: Worker exited unexpectedly`,丢结果的那个 worker 正是跑 `tests/native-surface.spec.ts` 的)。该次日志里 `native-surface.spec.ts` **零结果**,与历史两次崩溃同形(`34500290681` 125/126、`34448911025` 123/124——都是「N-1 个文件有结果 + teardown 期 worker 静默退出」)。 +- 迭代后的修法:`--maxWorkers=1`(整个套件跑在一个 fork 里,**零 worker 回收**)。理由:跨 4 次崩溃的一致形态是「worker 回收/退出竞态」,而不是某个测试失败;单 worker 从根上取消这个竞态,代价只是墙钟时间(本地 14s → 41s,Windows 步骤仍在 30 分钟 job 预算内)。**仍不加 retry**:retry 会把真实回归一起吞掉。 ### C. `plugin-mount` 的 npm 安装失败(4 次:2× ETARGET + 2× OOM) @@ -84,7 +85,7 @@ DSH `dsh-v0.1.5-rc.1..dsh-v0.1.5-rc.2`(上游 4 个 commit / 300 个变更文 - **真机挂载冒烟(rc.2)**:`DSH_CMD` 指向 `npx -y --package @deepseek-ai/dsh@0.1.5-rc.2 dsh`;见"落地"节的实测计数。 - **真实 Windows 收敛**:`ci-windows` **连续两次全绿**(同一 commit `5451f5c`)——`pull_request` 自动触发的 run [34561189320](https://github.com/omdsh-dev/DSH-better-sidebar/actions/runs/34561189320) 与随后 `workflow_dispatch` 的 run [34561457067](https://github.com/omdsh-dev/DSH-better-sidebar/actions/runs/34561457067),两次都是 **126 files / 1318 passed / 3 skipped / 0 errors**(`Unhandled Errors` 与 `Worker exited unexpectedly` 出现次数均为 **0**;同环境的 2026-09-09/10 窗口里这两行出现过 2 次)。同一分支的 `plugin-mount` 两次都绿:`added 521 packages in 24s`(**0 条 ERESOLVE**,与历史 OOM run 的 3062 条对比)、`test:mount` **7 passed**、聚合双挂载通过。 - **首次真实结果的证伪(已修正)**:本 PR 的第一版修复在同一 commit 上仍红两处,`.github/workflows/ci.yml` 的改动因此被真实证据改过一轮——见 C 节的「一次被真实结果否掉的中间方案」与 A 节第三个文件(`tests/git.spec.ts:85`,9607ms)。 -- **B 类的实测结论**:连续两次 Windows 全绿、0 次 worker 死亡,但这**不等于证明了 B 类的根因**(只有 2 次历史样本、无可比对照);记为「并发收敛 + 超时预算到位后未再复现」,若将来复现按 §B 的升级路径继续收敛。 +- **B 类的实测结论(已按"升级而非掩盖"走完两轮)**:`--maxWorkers=2` 下仍崩溃(run `34574847774`),故升级为 `--maxWorkers=1`(单 fork、零回收)。这**不等于证明了根因**(4 次崩溃的一致形态是「N-1 文件有结果 + teardown 期静默退出」,只能说明是 worker 回收竞态而非测试失败);收敛判据是单 worker 下连续两次 `ci-windows` 全绿。 - **本地 3080 真机**(DSH 0.1.5-rc.2 + 插件 0.19.1):部署与路由探针已完成(见"落地"节),GUI 人工验证由用户执行。 ## 不做 diff --git a/package.json b/package.json index 8d3c7d5..a2c4de2 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "watch": "tsdown --watch", "prepare": "tsdown", "test": "vitest run", - "test:windows": "vitest run --maxWorkers=2", + "test:windows": "vitest run --maxWorkers=1", "test:mount": "bash scripts/e2e-mount.sh", "test:mount:aggregate": "bash scripts/e2e-aggregate-mount.sh", "clean": "node -e \"require('node:fs').rmSync('lib',{recursive:true,force:true})\"",