# 宿主兼容性:验收标准与升列车 SOP dsh 宿主频繁发布 rc 列车(如 `0.1.0-rc.x` → `0.1.1-rc.x`),本插件的兼容性靠**自动化验收**保证,不靠手工记忆。本文是唯一判据。 ## 验收判据(全绿 = 兼容通过) | # | 判据 | 谁跑 | 命令 | |---|------|------|------| | 1 | peer ranges 匹配目标列车(npm semver prerelease 规则) | 人 + CI | 对照 `package.json` peerDependencies | | 2 | 零依赖桩 smoke 全绿(host 114 项 + client 20 项) | CI / 本地 | `node scripts/smoke.mjs && node scripts/smoke-client.mjs` | | 3 | 真实宿主 e2e 全绿(boot、HTML 预加载、settings seam、持久化) | compat 巡检 / 本地 | `node scripts/e2e-host.mjs` | | 4 | 浏览器面板渲染 + 核心动作可用(备份/保存/reset) | 发版前人工抽检一次 | 隔离环境 boot 后浏览器操作 | e2e 脚本的断言清单见 `scripts/e2e-host.mjs` 头注释;其中 **HTML 预加载官方 client 包**这条专门防"客户端列车陷阱"(见下)。 ## 版本矩阵 | dsh-backup | 宿主列车 | 状态 | 备注 | |---|---|---|---| | ≤0.6.x | 0.1.0-rc.6+ | 历史版本,不再维护 | | | 0.7.0–0.7.1 | 0.1.0-rc.8 | ⚠️ 仅 node 侧可用 | web 客户端在旧列车上报 "HTML did not preload"(陷阱②) | | 0.7.2 | 0.1.1-rc.2 | 历史版本 | peers `^0.1.1-rc.2` | | 0.8.0 | 0.1.1-rc.2 | 历史版本 | settings seam | | 0.9.0 | 0.1.1-rc.2 | 历史版本 | doctor 体检/救援通道/智能备份三件/恢复保护;发版前全量兼容实测:9 个历史版本真实归档恢复 + 0.7.2/0.8.0→main 真实宿主升级 + 自动备份真定时全绿(598 断言) | | 0.11.3(2026-09-12 已发 npm) | 0.1.5-rc.1 / rc.2 | ✅ 本地全量验收绿(2026-09-12) | peers 追加 `^0.1.5-rc.1`(semver 同元组规则覆盖 rc.2)。rc.2 适配面实测为零:六个 node 侧 peer 包 rc.1↔rc.2 **逐字节相同**(tarball diff,导出面零增删),client 包列车未动(dsh-client-runtime 最新仍为 0.1.1-rc.2)。rc.2 真机 e2e 32/32;跨列车原地升级 e2e(rc.1 宿主 + 0.11.2 → rc.2 宿主 + 0.11.3)14/14,设置与归档无损 | | 0.12.0(2026-09-12 已发 npm) | ✅ 本地全量验收绿(2026-09-12:smoke 256 + e2e-host 34 + 跨列车升级 14) | 新增迁移预检(拒绝规则校准自宿主 0.1.5-rc.2 的冻结清单:v0 51 类 / v2 51 类 / 来源 kind 15 类)与凭据哨兵;peers 不变,声明 `engines.dsh` | | 0.13.0(2026-09-21 已发 npm,**当前 latest**) | 0.1.5-rc.1 / rc.2 | ✅ 本地全量验收绿:smoke 283 + e2e-host 37 + settings 47 + client 25 | 更新感知(`/backup check-update` + 面板卡)与一键更新(`/backup update`,更新前自动留升级前快照);peers 不变 | | 0.13.1+(main,未发版) | 0.1.6-alpha.1 / 0.1.7-alpha.1(含 **0.1.7-rc.1**) | ✅ **真机实测通过(2026-09-23,#94 修复;0.1.7-rc.1 于 09-24 补测)** | 修 0.1.6+ 面板标签静默消失(#94):① strict codec 补 `create()` 惰性工厂(0.1.6 起强制,缺失时 `$mount` 抛 "strict codec has no create() factory");② `$mount` 改为声明式等待 `remote` 服务就绪;③ 挂载失败注册可见降级标签页。peers 追加 `^0.1.6-alpha.1 \|\| ^0.1.7-alpha.1`。验证:0.1.6-alpha.2 / 0.1.7-alpha.2 / **0.1.7-rc.1** 隔离环境实机(Built-in plugins 标签页出现、面板各卡片可用)。`^0.1.7-alpha.1` 语义覆盖同元组 rc.1,peer 无需再改 | ## 归档格式兼容(插件自身) 归档格式变更必须双向兼容:**新版本能读旧归档**(新 meta 字段缺省视为旧行为,如 `meta.types` 缺省 = 全量归档)、**旧版本遇新归档安全降级**(新前缀归档如 `dsh-t-` 不进旧版 listBackups/轮换——看不见、不误删)、meta/边车字段只增不改。smoke.mjs 的"老归档无边车兼容"与分类型场景是这一节的回归防线。 ## 已知陷阱(升列车前先读) 1. **semver prerelease 陷阱**:`^0.1.0-rc.6` 匹配不了 `0.1.1-rc.2`——npm 只允许同 `[major,minor,patch]` 元组的 prerelease 互相满足。每发新 rc 列车,peerDependencies 必须跟着升。 2. **客户端列车陷阱**:插件 web 面板依赖宿主 HTML 预加载 `/plugins//client.js`。0.1.1-rc+ 的 webserver 才生成预加载;旧列车上 node 侧一切正常但浏览器报 `client-modules: HTML did not preload @deepseek-ai/dsh-client-modules/client.js`。peerDependencies 表达不了这个约束——e2e 判据 #3 的 HTML 断言就是它的回归防线。 3. **pnpm 默认 24h 冷却期**:pnpm 10 在 CI 默认启用 `minimumReleaseAge`(供应链保护),新列车发布后 24h 内日常 CI 装 peers 会红。这是**有意保留的防线**:等满即可,不要在日常 CI 加豁免。compat 巡检 job 因职责是追新,显式豁免。 4. **strict codec 必须带 `create()`(0.1.6 起)**:客户端 Remote 贡献的 strict codec 在 0.1.5 只校验 `schema` 字段,0.1.6 起强制要求惰性工厂 `create()`(缺失时 `$mount` 抛 `strict codec has no create() factory`,且失败是静默的——面板标签直接消失,见 #94)。本仓库 `src/client.js` 的 `strictCodec()` helper 同时提供 `schema`(0.1.5 读)与 `create: () => schema`(0.1.6 读),两代共用同一 zod 实例。 5. **客户端模块依赖图结算(0.1.6 起)**:插件 client 半在 apply 时不能假设 `ctx.remote` 已就绪——须 `ctx.inject(['remote'], …)` 声明式等待后再 `$mount`;挂载失败必须落可见降级(本仓库用 `BackupTabFallback`),否则用户只看到"设置里没有这一项"。`window.__ModuleLoader__.load({id, factory})` 经典脚本体两代通用,不要改成 closure 返回形态(0.1.5 的 script 标签语义下顶层 `return` 是语法错误)。 ## 升列车 SOP 1. 确认上游变更面:diff 新旧列车各依赖包(重点 dsh-commands / dsh-settings / typert-protocol 的导出面)。 2. 升 `package.json` peerDependencies 到新列车 → 开 PR。 3. 等 pnpm 冷却期满(≤24h),CI 绿。 4. 手动触发 compat workflow(Actions → Host compat → Run workflow)或等每日巡检 → e2e 绿。 5. 合并 → 打 tag `vX.Y.Z` 自动发 npm(publish.yml)。 6. 更新上面的版本矩阵。 若 compat 巡检红了而仓库代码未变:优先怀疑上游列车破坏,看 [host-compat issue](../../issues?q=label%3Ahost-compat) 里的 run 链接定位。