docs: record the 0.1.7-rc.1 adaptation and resync the user docs

New design record `docs/plans/2026-09-23-dsh-0.1.7-rc.1-adaptation.md`
(method, the version-bump-only delta, the code-card break and the mermaid
regression it exposed, the over-yield recovery, the live 3080 upgrade with
its boot-log findings, and the evidence — including the note that the
mount lane first ran against a stale tarball because `e2e_resolve_tarball`
picks the newest existing one instead of packing).

AGENTS.md §3 gains the rc.1 rules: the boot-time peer preflight (a new
§3.4 item 12) with its four gotchas, the vendor hoist lesson, and the
code-card/toolbarLabels contract. Two claims are corrected while we are
here: the guide's four-entry description cap was attributed to rc.1 but
`GuideBody.tsx` and the host's terminal guide entry are byte-identical
across alpha.1 / alpha.2 / rc.1, and the icon surface is 25 named imports
plus a type-only `IconProps`, not 26.

README / README_EN / guide: version, badges, peer floor, the pinned-DSH
advice (`alpha` is now 0.1.7-alpha.2 while rc.1 rides `next`), the reduced
refusal list, and the correction that npm `latest` is 0.1.5-rc.3.
This commit is contained in:
Menghuan1918
2026-09-23 22:36:05 +08:00
parent 1d01ad942c
commit 6805ab51a2
6 changed files with 155 additions and 42 deletions
@@ -3,6 +3,8 @@
> 适配对象:`dsh-better-sidebar` v0.20.0 开发线(支持 DSH 0.1.6-alpha.2)→ **v0.21.0-alpha.1(仅支持 DSH 0.1.7-alpha.1+)**。
> 基线标签:`dsh-v0.1.6-alpha.2`(`ddefc45fbc`)→ `dsh-v0.1.7-alpha.1`(`c36a83ff6b`),相隔 **1299 个 commit / 4754 个文件变更**。
> 本文记录**为什么这么改**与**改完的证据**;规则性结论沉淀在仓库根 `AGENTS.md` §3。
>
> ⚠️ **后续**:`v0.21.0-alpha.1` **从未发布到 npm**——这条开发线随后又适配到 **0.1.7-rc.1**,peer 下限收到 `^0.1.7-rc.1`,整个 0.21 线的变更一并随 **v0.21.0-rc.1** 出货。本文保留为 alpha.1 阶段的原始记录(结论仍然有效),最新状态见 [2026-09-23-dsh-0.1.7-rc.1-adaptation.md](2026-09-23-dsh-0.1.7-rc.1-adaptation.md)。
## 1. 怎么做的(方法与边界)
@@ -0,0 +1,106 @@
# DSH 0.1.7-rc.1 适配记录
> 适配对象:`dsh-better-sidebar` v0.21.0-alpha.1(支持 DSH 0.1.7-alpha.1)→ **v0.21.0-rc.1(仅支持 DSH 0.1.7-rc.1+)**。
> 基线标签:`dsh-v0.1.7-alpha.1`(`c36a83ff6b`)→ `dsh-v0.1.7-rc.1`(`46a7f68b09`),相隔 **318 个 commit / 1361 个文件变更**(中间还跨过一个没适配的 `0.1.7-alpha.2`)。
> 本文记录**为什么这么改**与**改完的证据**;规则性结论沉淀在仓库根 `AGENTS.md` §3(新增 §3.4 第 12 条)。上一阶段见 [2026-09-22-dsh-0.1.7-alpha.1-adaptation.md](2026-09-22-dsh-0.1.7-alpha.1-adaptation.md)。
## 1. 怎么做的(方法与边界)
先 `git fetch` 把 `dsh-v0.1.7-alpha.2` / `dsh-v0.1.7-rc.1` 两个 tag 取进本地 DSH 克隆,然后按「与本插件相交的 surface」分五路并行侦察:启动预检与展示元数据、`ui-primitives` 导出面、会话/会话快照/流式契约、平台契约(file-address / settings / webserver / gateway / 客户端 bundle 装载)、宿主文档预览能力盘点。**五路的结论一致指向「本版是发行候选收敛版,不是新一轮重写」**:绝大多数「变更文件」是 `package.json` 的版本号与 `workspace:^` → `workspace:*|~` 改写,`src/` 逐字未变。
侦察子的报告一律当线索处理,本轮**亲自复核**了四件事:`DiffBlockLabels` 的字段增删(读两个 tag 的 `DiffBlock.tsx`)、宿主渲染器覆盖的扩展名集合(读 `documentpreview/src/client/*/index.ts` 与 `document/unviewable.ts`)、`MAX_DESCRIBED_ENTRIES` 的真实归属(三 tag 逐字比对 `GuideBody.tsx`)、以及 icon 具名导入的真实条数(脚本提取导入面,得 25 而不是文档里记的 26)。
## 2. 本版到底改了什么
**零 `src/` 变化**(只有版本号与依赖面改写):`ui-sidebar-right`、`ui-slots`、`ui-settings`、`dsh-settings` 与 loader 的 volatile 机制、`webserver` + `client/connection` 的鉴权、`packages/session` 全线(**会话格式仍是 v4**,没有 v4→v5)、`llm` 的 `MessageSourceMap`、`subagent`(descriptor 仍是 v3)、`ui-conversation` 的槽锚点与 `turnTail`、客户端 externals 白名单(9 个 specifier 逐字不变)、`util/workspace-path` 的 `fileAddressFor`/`parseFileAddress`、`plugin-artwork.tsx` / `package-meta.ts` / `scripts/verify-package-meta.ts`(后两者逐字相同,所以图标与 locale 的三条硬规则、以及「没有任何尺寸校验」都仍然成立)。
真实变化只有两类:
1. **`ui-primitives` 的代码卡**(唯一会编译失败的一处):新增 `CodeToolbar.tsx`(alpha.1 上不存在),其 `CodeToolbarLabels` 的 `codeLabel` / `wrapLabel` / `unwrapLabel` **三个必填**;`DiffBlockLabels` / `ReadBlockLabels` 改为继承它,且**删除了 `DiffBlockLabels.files`**。icon 模块只**新增**了 `IconUsersOutline{Artwork,Regular,Medium}`,无删除、无改名、既有 icon 默认尺寸也没动。
2. **启动期 peer 预检**(rc.1 新文件 `packages/boot/app-boot/src/plugin-compatibility.ts`):见 §4。
另外 rc.1 把 **vendor 补丁**一起抬了:`@deepseek-ai/cordis` 4.0.3 → 4.0.4、`schemastery` 3.18.3 → 3.18.4、`cordis-plugin-group` 1.0.3 → 1.0.4、`cordis-plugin-include` 1.0.8 → 1.0.9(loader 已是 1.0.5)。这是上游 workspace-release-ranges 政策的直接后果——包内 DSH 引用发布成**精确版本**,vendor 用 `~`。
## 3. 插件侧改了什么
### 3.1 版本与依赖面
`package.json` / `dsh.plugin.json` / `src/client/service.ts` 的 `SIDEBAR_SERVICE_VERSION` 同步到 `0.21.0-rc.1`;14 个 DSH peer 从 `^0.1.7-alpha.1` 收到 `^0.1.7-rc.1`;28 个 devDependency 钉 `0.1.7-rc.1`。
**只改这两处不够**:`pnpm peers check` 一次报出四类 unmet peer——`cordis-plugin-group` / `-include` 是 `dsh-app-boot` / `dsh-config-editor` 的**传递 peer**,pnpm 会继续沿用 lockfile 里的旧副本。按 AGENTS.md §3.4 第 8 条的既有做法,把 `@deepseek-ai/cordis@4.0.4`、`@deepseek-ai/cordis-plugin-group@1.0.4`、`@deepseek-ai/cordis-plugin-include@1.0.9` 三个**显式钉进 devDependencies**(并抬 `schemastery` 到 `^3.18.4`)后收敛。
### 3.2 代码卡标签(唯一的编译破坏)
`src/client/SideChatView.tsx` 的 diff / read 两个字典补上三个新键、删掉 `files`;`sideChatBlockFiles` 词条从 20 份词典中删除。**不放进 `shared`**:`TerminalBlockLabels` 不继承 `CodeToolbarLabels`,而 `terminal: { ...shared }` 是新鲜对象字面量,会触发多余属性检查。
**红证据**:切换 devDeps 到 rc.1 后 `pnpm typecheck` 报
`Type '{ files: …; copy: string; … }' is missing the following properties from type 'DiffBlockLabels': codeLabel, wrapLabel, unwrapLabel`(`src/client/SideChatView.tsx(337,40)`);改完转绿。
### 3.3 全面采用宿主的新代码卡
`src/client/markdown-labels.tsx` 的 `labels.code` 补 `toolbarLabels`,措辞照抄宿主(`codeBlock.title|wrap|unwrap`),四个 markdown 渲染点一并从旧的 `data-code-block-banner` info banner 切到新的 code card。`MarkdownCopyLabels` 由 2 字段扩到 5 字段,三个构造点(`TextEditor.tsx` / `DiffPane.tsx` / `SideChatView.tsx`)与两个测试 fixture 同步。
**这个改动带出一个真实回归,且当场被测试抓到**:新 card 对没有语法高亮的语言显示 `codeLabel`,而 mermaid 正是没有 shiki grammar 的语言——**info string 不再进 DOM**,于是 `src/client/mermaid.tsx` 里那条「读 banner 第一个元素的文本是不是 `mermaid`」的探测恒假,三个 `tests/mermaid-markdown.spec.tsx` 用例立刻红(`the mermaid fence must be swapped for a diagram: expected null not to be null`)。改为按**围栏正文**匹配:`splitMermaidBlocks(text)` 取出 mermaid 正文集合,与 `block.querySelector('code')` 的文本两侧 `trimEnd()` 后比对;`language-mermaid` 类名作为第一探针保留。
### 3.4 收回 9 个过度让出的扩展名
上一版把 27 个扩展名整体让给宿主,但其中 9 个宿主**根本没有渲染器**:`avif` / `ods` 落在 `document/unviewable.ts` 的「已知二进制、无渲染器」表里直接走空状态,`xlsb` / `xlt` / `xltx` / `xltm` / `ots` / `dot` / `dotx` 落回 text 兜底后按二进制判定失败、同样是空状态。**这些文件在让出之前是走插件兜底显示下载面板的**,所以这是上一版自己引入的用户可见回归(rc.1 没有修复它)。现从 `HOST_OWNED_EXTS` 移除这 9 个,剩 18 个;`fods` 继续让出(宿主用纯文本显示这段扁平 XML,比下载面板有用)。
`canOpen` 此前**零测试覆盖**,这正是回归没被发现的原因;补了双向守护(`tests/native-surface.spec.ts`)。**红证据**:把旧的 27 项清单临时还原后,新用例报 `AssertionError: macro.xlsb: expected false to be true`。
### 3.5 peer 范围守护
`tests/market-manifest.spec.ts` 增加一条:每个 `@deepseek-ai/dsh*` peer 必须是 `^0.1.7-<预发布>` 形式的浮动范围。理由见 §4——**失败是静默的**,而 `pnpm peers check` 看不到它(它校验解析出来的 dev 树,不校验宿主会读的范围形状)。
### 3.6 钉版与文档
CI 的 `npm install -g` 与 `scripts/e2e-common.sh` 的 `DSH_NPX_SPEC` / `DSH_EXPECT_VERSION` 都钉到 `0.1.7-rc.1`,并在注释里写明**必须写精确版本号**:rc.1 上线时 npm `alpha` 指的是 **0.1.7-alpha.2**,rc.1 走的是 `next`。README / README_EN 的 6 处 shields badge、支持线段落、让出扩展名清单与「基线」段同步;指南头部版本行同步;AGENTS.md 见 §6。
## 4. rc.1 新增的启动期 peer 预检(本版最值得记住的一处)
`plugin-compatibility.ts` 遍历 profile 行的 `peerDependencies`,对名字为 `@deepseek-ai/dsh` 或以 `@deepseek-ai/dsh-` 开头的条目跑 `semver.satisfies(宿主版本, range, { includePrerelease: true })`;不满足就把整行 `row.disabled = true`,只在 stderr 留一行。四个要点:
- **只读 peer**,`engines.dsh` 与 `dsh.manifestVersion` 仍然零读者(`package-manifest` 的 README 在 rc.1 逐字未变,它自己的措辞已经落后于代码);
- `peerDependenciesMeta.optional` **没有任何豁免**——我们那条 optional 的 `@deepseek-ai/dsh-client-ui-sidebar-right` 照样参与判定;
- `includePrerelease` 被强制打开,判定比 npm **宽松**(`^0.1.6-alpha.2` 也能满足 `0.1.7-rc.1`),但**精确版本号或没有预发布子句的范围(如 `^0.1.7`)一定不过**;
- 豁免只能写 profile 自己的 `compatibility.json`,没有全局开关也没有环境变量。
**因此本版的 peer 下限是刻意收紧的**:`0.21.0-alpha.1` 从未发布到 npm(真实用户为零),而修掉 `DiffBlockLabels` 之后同一份代码在 alpha.1 上会崩(alpha.1 的 `DiffBlock` 仍读已被上游删除的 `labels.files`)。声明与真机验证过的宿主一致,比声明一条跑不通的支持线更安全;rc.1 起「声明过宽」的代价还从「可能报错」变成了「静默不加载」。
## 5. 真机升级(把 3080 的 web 实例从 0.1.7-alpha.1 升到 0.1.7-rc.1)
改动四处,全部带 `.bak-20260923-141956-pre-rc1` 备份:启动器 `~/.dsh/dsh-web.sh` 的 npx 钉版、profile 的 `dsh-better-sidebar` tarball 指向、`@deepseek-ai/dsh-experimental-agent-team-profile` 0.1.7-alpha.1 → 0.1.7-rc.1、profile 的 `pnpm-workspace.yaml` override(`@deepseek-ai/dsh-settings` → 0.1.7-rc.1)。随后在 profile 目录跑**纯 `pnpm install`**(不能走 `dsh plugin install`,它对内容变化会报「Already up to date」短路),再重启 pm2 app `dsh-web`。
**`@deepseek-ai/dsh-experimental-agent-team-web-profile` 保持 0.1.6-alpha.2 不动**——计划阶段曾打算移除它,核对发布元数据后推翻:它唯一的 peer 是 `@deepseek-ai/cordis ^4.0.2`,被预检的**名称过滤器直接跳过**,不会被 disable;其 `dependencies` 的 `^0.1.6-alpha.2` 今天就已经在解析一份独立的 0.1.6-alpha.2 副本,与 alpha.1 时的状态同构。实测 boot 日志确认它没有被跳过。
**boot 日志的实测结论**:只有一行 stderr,且不是我们的插件——
```
dsh: skipping profile bundle "@huanlin/dsh-plugin-better-sidebar-plugin-office": Error: Plugin
@huanlin/dsh-plugin-better-sidebar-plugin-office@0.1.2 is incompatible with dsh 0.1.7-rc.1:
peerDependencies {"@deepseek-ai/dsh-client-runtime":"^0.0.1-rc.1"}. …
```
`@deepseek-ai/dsh-client-runtime` 是 0.1.7 早就删掉的包,所以这个三方 Office 预览插件在 alpha.1 上本来就是「加载了但跑不对」(上一轮真机排查时那个拖进旧 `dsh-settings@0.0.1-rc.1` 的影子副本也来自它)。rc.1 现在把它**干净地跳过**而不是带着坏 peer 加载,属于状态改善;**它没有 `allow-version` 豁免之外的自愈路径**。npm 上已有 `0.2.0`,其 peer 面(`react` / `dsh-better-sidebar ^0.17.0` / `@deepseek-ai/cordis ^4.0.1`)不含死包,升级即可恢复——**这一条留待用户拍板,本轮未动**。
重启计数 22334 → 22335(只 +1,无崩溃循环)。
## 6. 验证证据
**仓库门禁**(全部退出码 0):`pnpm peers check`(No peer dependency issues found)、`pnpm typecheck`、`pnpm lint`、`pnpm build`、`pnpm check:consumer-types`、`pnpm test` = **1132 passed | 9 skipped / 113 文件**。
`tests/fs-watch.spec.ts` 的 `treats a repeat add as already-watched` 在并行跑全量时超时过一次;单跑与该 spec 连跑三次都是 ~1.2s 通过(预算 15s),判为 FSEvents 在满负载下的既有抖动,随后重跑全量即绿。
**挂载冒烟**:`DSH_CMD='npx -y --package @deepseek-ai/dsh@0.1.7-rc.1 dsh' pnpm test:mount` → 真实 rc.1 宿主上 **7/7 通过**(含「挂载 + 内建 tab 全扫」「无错误条 / pageerror / console 错误」「经原生指南逐个打开插件 tab」「经文件树打开 seed 文件强制加载 editor chunk」「mermaid / README 预览」「sidechat 宿主路由」以及两条 perf 度量)。
⚠️ **这一条差点变成假证据**:`scripts/e2e-common.sh` 的 `e2e_resolve_tarball` 是从仓库根 glob 里**选 mtime 最新的 tarball**、而不是自己打包;第一次跑时根目录还留着上一版的 `dsh-better-sidebar-0.21.0-alpha.1.tgz`,于是那一轮其实是**拿旧产物做的冒烟**(输出里 `tarball:` 一行会写出版本号,现在知道要看它)。删掉旧包、`pnpm build && pnpm pack` 出 `0.21.0-rc.1` 的包后重跑,才是上面这份证据。
**CI**:`ci` / `ci-windows` / `plugin-mount` 三个 job 全绿(`35874792197`)。这是本分支**第一次** CI 全绿——上一轮那两份提交(09-22 与 09-23)在 `Test` 步红,而本地 `pnpm test` 一直是绿的。根因是上一版新增的 `tests/primitives-exports.spec.ts` 用 `execFileSync('rg', …)` 取文件清单:**ripgrep 在开发机上装了、CI runner 上没有**,于是整个 spec 在**收集阶段**就 `spawnSync rg ENOENT`。已换成 `readdirSync` 走一遍 `src/` + `tests/`(几百个小文件,无外部二进制依赖)。
**真机(3080)**:宿主跑 DSH 0.1.7-rc.1、插件 0.21.0-rc.1;一次性 token 交换 303 + 签名 cookie 正常;`[data-dsh-better-sidebar]` 挂载;**零 pageerror**;浏览器实际加载的 bundle 里含新的 `codeBlockUnwrap` 且**不含**已删除的 `sideChatBlockFiles`(证明是真机在跑新产物,不是旧缓存)。
**用户设置的 volatile 提交在 rc.1 上仍然成立**(这是上一轮最难的一处,而本轮抬了 schemastery / cordis):贴边设置表单读到 15 个偏好键、9 键 `tabsEnabled`、用户自己的 `pluginSettings.editor.openWith.pinned = ["vscode"]` 全部保留;把 `autoOpenJobs` 由 `false` 翻成 `true` → `settings.update` 200、revision 0 → 1、重新 `settings.get` 读到新值,再写回 `false` 复原。**一个真机观察(未改)**:表单里仍有 `viewersEnabled.{pdf,binary-download}` 这两个已被删除的 viewer 的键,属于历史遗留的配置面。
## 7. 落地偏差与未做
- **回收扩展名的条数**:侦察报告一处写「9 个」、列举却是 10 项;亲自比对宿主渲染器表后确认是 **9 个**(`fods` 会被宿主当纯文本显示,不算死路),最终按 9 个回收、`fods` 保留让出。
- **`@deepseek-ai/dsh-experimental-agent-team-web-profile` 未移除**(计划里曾打算移除,核对发布元数据后推翻,理由见 §5)。
- **未采用** rc.1 新增的 markdown `fileImages` 委托 / `ImageLightbox` / 本地图片悬浮预览:插件自绘的图片 URL 一律带 `?sessionId=`,而 `parseFileLink` 拒绝含 `?` 的目标,宿主这条新分支拦不到我们的 URL;要用就是新做 UI,不属于适配。
- **未把宿主 primitives 抄进插件**:rc.1 新增的官方插件实践文档(`packages/preset/agent-preset/skills/cordis-plugin-development/references/practices.md`)明确建议插件不要 import 宿主 client 包、而是「抄一份并只依赖设计令牌」。我们**有意保留 import**(25 个 icon + 13 个组件)并在 AGENTS.md §3 记下这条分歧:该建议的理由之一是「纯 JS 插件没有类型检查、组件抛错会静默清空槽位」,而本仓库是 TS + `pnpm typecheck` + `check:consumer-types`,并且 `tests/primitives-exports.spec.ts` 已经把「符号消失」从静默失败变成红测试。
- **未按同一份实践文档把 `jobs` 路由重构成 `ctx.sessionProjections` 单元**:那是行为等价重构,超出本轮范围。
- **`src/client/conversation-draft.ts` 的锚点缺陷未动**:它只认 `[data-slot="conversation"]`(`center-column.ts` 已经是 `main.conversation` 优先的双臂选择器)。经核对这是 **0.1.7-alpha.1 起就存在**的既有缺陷、不是 rc.1 引入,且与本轮变更不同源;记在这里待单独处理。
- **未重跑真机 Plugins 页的图标/文案断言**:那条路径(`readPluginMeta` + `ui-plugin-manager` 渲染)在本轮逐字未变,且上一轮已真机验证过;本轮的真机证据集中在「升级后插件仍挂载、无错误、跑的是新产物、设置 volatile 提交仍成立」。