# DSH 0.1.5-rc.2 适配(v0.19.1 基线)+ CI 三类失败修复 日期:2026-09-10(复核 2026-09-11) 分支:`feat/dsh-0.1.5-rc.2` ## 背景 DSH `dsh-v0.1.5-rc.1..dsh-v0.1.5-rc.2`(上游 4 个 commit / 300 个变更文件,npm 发布 2026-09-10T14:57Z,dist-tag **`next`**;`latest` 仍是 rc.1)是 0.1.5 线的第二个候选版。本计划记录该 delta、插件对它的结论(**无需任何运行时/类型层适配**,只推进钉版),以及同一时间窗内 `ci-windows` / `plugin-mount` 两个车道真实存在的三类 CI 失败与修法。上一版(rc.1)记录见 [2026-09-10-dsh-0.1.5-rc.1-adaptation.md](./2026-09-10-dsh-0.1.5-rc.1-adaptation.md);仓库级适配要点同步到 [AGENTS.md](../../AGENTS.md) §2/§3。 ## 上游 delta(rc.1 → rc.2,影响本插件的部分:无) 300 个文件里绝大多数是各包 `package.json` 的 `version` 单行改动(`1+1-`)。逐文件过完后的真实代码改动只有四处: | # | 上游变更 | 文件 | 对插件的影响 | |---|---|---|---| | 1 | 消息反馈改为对称提交(弹窗、动作按钮、控制器、槽位、i18n) | `packages/client/ui-message-feedback/src/client/*` | 无——插件不注册反馈槽、不读该控制器 | | 2 | 产物卡片内边距与呈现微调 | `packages/client/ui-deliverables/src/client/*`、`ui-chat/src/client/chat/TurnTailNodeView.module.css` | 无——插件不消费这两个包的组件,也不做会话尾迹布局 | | 3 | `CodeFileIcon` 的 477 行内联 SVG 拆进新增的 `code-file-icon-artwork.ts`(另有 `.manifest.json` 记录设计导出摘要) | `packages/client/ui-primitives/src/CodeFileIcon.tsx` | 无——`CodeFileIcon` 的导出面与 props 未变,且插件**本来就不用它**(插件文件树用 `IconCodeOutline16`;`#611` 的文件图标主题还在 PR #594,未进 main) | | 4 | 其余全部为版本号 | 各 `package.json` | 无 | **明确未变(不必再核)**: - `packages/api/*`(session-controller / workspace-controller / gateway / remotes)、`packages/host/*`、`packages/session/*`、`packages/agent/*`——**零**变更;除 `packages/feedback/message-feedback/src/types.ts`(1 行)外**零**类型文件变更。 - 斜杠 RPC 方言、一次性 token 鉴权、会话事件模型、`sidebarRight` / `sidebarRightTabs` 承载面、`SidebarRightGuideEntry.description` 的 ≤4 渲染规则:全部沿用 rc.1 结论。 - `@deepseek-ai/dsh-client-ui-primitives` 仍不声明 `dependencies`(bundle 仍裸 import `anser` / `shiki` / `@shikijs/langs/*` / `mdast-util-*` / `micromark-*` / `katex`)——提升进 devDependencies 的那组包继续不得回退。 ## 决策 - **peer 下限保持 `^0.1.5-rc.1` 不动**:rc.2 无契约变化,`^0.1.5-rc.1` 天然容纳它,rc.1 用户无需升级即可用新版插件。因此本版是**纯 patch 的 0.19.1**,只推进 devDependency 钉版、CI 挂载钉版与 `SIDEBAR_SERVICE_VERSION`。 - **`engines.dsh` 保持 `>=0.1.5-rc.1`**:与 peer 下限同源(同一个支持范围,两处不能分叉)。 - **CI 挂载车道钉 rc.2**:真机证据要落在最新版上;`latest` 仍是 rc.1 只是上游的发布节奏,与本插件支持范围无关。 - **不为此版写运行时兼容层**:上游 delta 里没有本插件触达的面,写兼容层就是无据的复杂度。 ## CI 三类失败(2026-09-09/10 窗口,60 次已完成 run 的取证) 窗口内 `ci-windows|Test` 失败 8 次、`plugin-mount|Install dsh CLI (pinned)` 失败 4 次。逐条读日志后归为三类,其中两类是**真实缺陷**、一类是**运维噪音**: ### A. Windows 真实超时(6/8 次 `ci-windows` 失败,主因) - 证据:run `34494370187`、`34490215638`、`34459499945`(**3 次 attempt 全红**)、`34372999975`、`34322271074`、`34319666611` 均为 `Error: Test timed out in 5000ms`,位置 `tests/agent-pty.spec.ts:71:3` / `:106:3`;`tests/install-powershell.spec.ts:105:41` 的 `powershell.exe` 单测自身耗时 **12151ms** 后才超时。 - 根因:`vitest.config.ts` 没有任何 `testTimeout` 配置(默认 5000ms),而这些 spec 每个用例都要真起一个 `PowerShell + ConPTY` 对;`tests/agent-pty.spec.ts` 的 `waitForTranscript` 默认预算**也**是 5000ms——内层轮询吃掉整个对外预算,结构上必然超时。 - 修法:**全局** `testTimeout: 15_000`(`vitest.config.ts`)+ 两个最重的 describe 显式 `{ timeout: 30_000 }`(vitest 4 的 describe 级选项,已实测生效)+ `waitForTranscript` 默认预算降到 15s。**为什么是全局而不是逐文件**:本 PR 第一次跑真实 Windows 时,同一个 5000ms 默认又在**第三个文件**上翻车——`tests/git.spec.ts:85`「caps status entries at the truncation limit」耗时 **9607ms**(构造病态 untracked 集合 + 真实 git 调用)。三个不同文件、同一个根因,逐文件加超时是打地鼠;15s 是默认值的 3 倍、也是 Windows 上实测最大单测耗时的 ~1.5 倍,而 pty / powershell 两类最重的用例各有自己的 30s 预算。 ### B. Windows worker 静默死亡(2 次,非确定性) - 证据: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)。 - 修法(两次迭代,第一次被真实结果证否):先上 `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) - 证据:run `34492160123` / `34491256626` 报 `npm error notarget No matching version found for @deepseek-ai/dsh-tool-bash-persistent@^0.1.5-rc.2` / `@deepseek-ai/dsh-sandbox-windows-acl@^0.1.5-rc.2`;run `34490912649` 先刷 **3062 条 ERESOLVE**、9 分钟后 `FATAL ERROR: Ineffective mark-compacts near heap limit` exit 134。同一机制在 2026-09-09 的 alpha 线也复现过一次(run `34363627184`,1808 条 ERESOLVE → OOM);同期成功的安装是 `added 521 packages in 19s`、**0 条 ERESOLVE**。 - 根因:**钉版并没有真的钉住**。`@deepseek-ai/dsh@0.1.5-rc.1` 的传递依赖声明为浮动 `^0.1.5-rc.1`,会被上游**分阶段发布**中的 rc.2 满足(rc.2 的 14:43–14:57 逐个包上线),于是 npm 组出 rc.1/rc.2 混合 peer 图 → 解析器爆炸(OOM),或对尚未发布的兄弟包硬报 ETARGET(两个 ETARGET 分别在该包上线前 5.5 分钟与 25 秒)。 - 修法(`.github/workflows/ci.yml`):钉一个**已完整发布**的 rc.2(修 ETARGET),并加 `NODE_OPTIONS=--max-old-space-size=4096`(修 OOM)。 - **一次被真实结果否掉的中间方案(值得记)**:第一版修复还加了 `--legacy-peer-deps`(想法是「跳过 peer 协商即与发布窗口解耦」)。真实 Windows 结果表明它只是把安装**弄坏**而不是弄稳:它跳过的正是全局安装必须提供的 peer,而 `@deepseek-ai/dsh-app-boot` 在 boot 时 require 的 `@deepseek-ai/cordis-plugin-group` **是 peer 而不是 dependency**,于是 CLI 在冒烟开始前就以 `ERR_MODULE_NOT_FOUND` 死掉(run 34560785101 的 `plugin-mount`)。本地复现并验证:同一命令去掉该 flag 后安装树完整(`dsh/node_modules/@deepseek-ai/cordis-plugin-group` 存在),且 `DSH_CMD` 指向这个 classic 安装的 `test:mount` **7 passed**——宿主自带依赖,插件侧看不到差别。 - 顺带修掉一处过时注释(原文断言「rc.1 同时是 `latest` 与 `next`」——现在 `latest=rc.1`、`next=rc.2`);这条注释正是浮动范围隐患长期不可见的来源。 ## 改动清单(子系统级) | 子系统 | 文件 | 变更 | |---|---|---| | 基线 | `package.json` | `version` → `0.19.1`;dsh-* **devDependencies** 全部钉 `0.1.5-rc.2`(含 `dsh-client-locale`,上游仍与 DSH 同 tuple 发版);**peerDependencies 一律保持 `^0.1.5-rc.1`** | | 基线 | `pnpm-lock.yaml` | 锁文件对齐 rc.2 | | 基线 | `dsh.plugin.json` | `version` → `0.19.1`;`engines.dsh` 保持 `>=0.1.5-rc.1`(与 peer 下限同源) | | 基线 | `src/client/service.ts` | `SIDEBAR_SERVICE_VERSION` → `0.19.1`(`tests/service.spec.ts` 断言它 === `package.json` 的 version) | | CI | `.github/workflows/ci.yml` | mount 车道钉 `@deepseek-ai/dsh@0.1.5-rc.2` + `NODE_OPTIONS=--max-old-space-size=4096`(C 类;**不用** `--legacy-peer-deps`,理由见 C 节);`ci-windows` 的 `Test` 改跑 `pnpm test:windows`(B 类);注释同步事实 | | CI | `package.json` | 新增 `scripts.test:windows`(`vitest run --maxWorkers=2`) | | CI | `vitest.config.ts` | 全局 `testTimeout: 15_000`(A 类;覆盖 git / powershell / pty 三类真起进程的 spec,逐文件打地鼠的做法在真实 Windows 上被证否) | | CI | `tests/agent-pty.spec.ts` | `AgentPtyRegistry` describe 显式 `{ timeout: 30_000 }`;`waitForTranscript` 默认预算 5000 → 15_000(A 类) | | CI | `tests/install-powershell.spec.ts` | `PowerShell installer entry points` describe 显式 `{ timeout: 30_000 }`(A 类) | | 文档 | `docs/plans/2026-09-10-dsh-0.1.5-rc.2-adaptation.md` | 本计划 | | 图标 | `src/client/file-icons.tsx` / `src/client/builtins/tab-icons.*` / `src/client/native/tab-adapter.tsx` | 文件图标改用宿主官方 `FileTypeIcon`;内置 tab 与原生芯片图标彩色——独立记录见 [2026-09-11-file-icons-and-tab-glyphs.md](./2026-09-11-file-icons-and-tab-glyphs.md) | | 文档 | `AGENTS.md` | §2 CI 基线戳与 mount 车道说明、§3 标题/基线段(rc.2 仍无契约变化,只推进钉版) | | 文档 | `README.md` / `README_EN.md` | 支持版本徽章与说明、新增 `### v0.19.1` 段 | | 文档 | `docs/external-plugin-guide.md` | 顶部基线行 → v0.19.1 / DSH 0.1.5-rc.1+(已在 rc.2 上验证) | | 文档 | `tests/native-surface.spec.ts`、`tests/builtins.spec.ts`、`tests/e2e/mount.e2e.ts`、`src/client/service.ts`、`src/client/native/index.ts` | 注释里的 `DSH 0.1.5-rc.1` → `0.1.5-rc.1+`(同一行为在 rc.2 未变) | ## 验证 - **未执行**的门禁与真机步骤都留空,由主代理实施后回填(本文件不预填未经执行的结论)。 ### 回填(主代理实测) - **门禁(rc.2 钉版,macOS arm64)**:`pnpm install` 成功且锁文件对齐 rc.2;`pnpm peers check` = `No peer dependency issues found`;`pnpm typecheck` / `pnpm lint` / `pnpm check:consumer-types` 全绿;`pnpm test` = **126 files / 1312 passed / 9 skipped**(与 rc.1 基线同数,无新增 skip)。 - **真机挂载冒烟(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 类的实测结论(已按"升级而非掩盖"走完两轮)**:`--maxWorkers=2` 下仍崩溃(run `34574847774`),故升级为 `--maxWorkers=1`(单 fork、零回收)。这**不等于证明了根因**(4 次崩溃的一致形态是「N-1 文件有结果 + teardown 期静默退出」,只能说明是 worker 回收竞态而非测试失败);收敛判据是单 worker 下连续两次 `ci-windows` 全绿——**已达成**:run [34575721734](https://github.com/omdsh-dev/DSH-better-sidebar/actions/runs/34575721734)(PR 触发)与 run [34576146970](https://github.com/omdsh-dev/DSH-better-sidebar/actions/runs/34576146970)(同 commit `4fe67e3` 的 `workflow_dispatch`)都是 **127 files / 0 errors**,`Unhandled Errors` 与 `Worker exited unexpectedly` 出现 0 次。 - **本地 3080 真机**(DSH 0.1.5-rc.2 + 插件 0.19.1):部署与路由探针已完成(见"落地"节),GUI 人工验证由用户执行。 ## 不做 - 不为 rc.2 写任何运行时/类型层兼容层(上游 delta 未触及本插件的面)。 - 不把 peer 下限抬到 `^0.1.5-rc.2`(会无谓地把 rc.1 用户挡在上一版)。 - 不为 B 类做无证据的猜测性改动:不加 vitest `retry`、不改默认 pool、不关掉真实 pty 测试。 - 不改 DSH 源码(仓库硬约束 §1);不改 `release.yml`(0.19.1 无 prerelease 后缀 → 自动发 `latest`,符合 patch 语义)。 ## 落地 - PR:[#626](https://github.com/omdsh-dev/DSH-better-sidebar/pull/626)(`feat/dsh-0.1.5-rc.2`)。 - 本地真机挂载冒烟(rc.2,macOS arm64):`pnpm test:mount` **7 passed**(含 tab 体填充断言、指南 ≤4 条说明渲染断言、mermaid / README 预览与 sidechat 路由烟测);`pnpm test:mount:aggregate` 通过(聚合双挂载下插件自动退让,`/sidebar/api` 未知方法 404 与 `terminal.deps` 契约均符合)。 - 本地 3080 实例部署(`~/.dsh/profiles/web` + `~/.dsh/dsh-web.sh`):profile 依赖从 `dsh-better-sidebar-0.19.0.tgz` 改指 0.19.1 tarball 并 `pnpm install`(`node_modules/dsh-better-sidebar/package.json` = **0.19.1**);`dsh-web.sh` 的 npx 钉版改 `@deepseek-ai/dsh@0.1.5-rc.2`,旧进程(PID 99521/99509/99507)定向 kill 后由 LaunchAgent `com.pm2.dshweb.resurrect` 自动拉起(新 PID 36025,npx 缓存 hash 因版本变更从 `1da1392061ab1944` 变为 `c40503fdf38a82ea`)。**已核**:新缓存里的 `@deepseek-ai/dsh` = **0.1.5-rc.2**,`/sidebar/api/shell.get` → `{"ok":true,"value":{"shell":"zsh","name":"zsh"}}`、`settings.get` 回默认设置、`terminal.deps` → `{"ok":true}`(三条插件路由在生产实例上工作);GUI 人工验证由用户执行,结论回填到本节的下一步。 - PR:[#626](https://github.com/omdsh-dev/DSH-better-sidebar/pull/626)(`feat/dsh-0.1.5-rc.2`)。PR 三个 check(`ci` / `ci-windows` / `plugin-mount`)在 run 34561189320 与 run 34561457067 上均绿。 - 待回填:3080 人工验证结果(用户执行)、发版情况(v0.19.1 的 tag / npm 发布由用户决定)。