Back to home@Shizuku-keop

dsh-compat-guard

Compatibility governance for DeepSeek Harness: upgrade pre-flight gate, storage-format fingerprint, backup, session migration, per-profile lockfile, plugin x DSH compat matrix.

Stars
1
Language
JavaScript
Created
Aug 25, 2026
Updated
Aug 25, 2026
GitHub repo

Introduction

dsh-compat-guard

兼容性治理插件包:升级前置闸门 + 存储格式指纹 + 自动备份 + 会话迁移 + profile 级锁文件 + 插件×DSH 兼容矩阵。一个 npm 包,六个能力,全部零运行时依赖(纯 Node ≥ 20)。

dsh-guard status            # 当前版本 / dist-tags / 存储指纹 / 锁文件状态
dsh-guard preflight         # 升级前检查(闸门):插件兼容 × 存储格式 × 自动备份
dsh-guard upgrade           # 闸门 + 执行 dsh 升级 + 事后复检
dsh-guard upgrade-plugins   # 闸门 + 更新 profile 插件 + 重写锁文件
dsh-guard snapshot          # 备份 $DSH_HOME + 写入 dsh.guard.lock.json
dsh-guard restore/rollback  # 一键回滚(先自动做安全快照)
dsh-guard verify            # 与提交的锁文件比对(团队漂移检测)
dsh-guard migrate           # 会话数据迁移(sqlite -> zstd JSONL,先备份)
dsh-guard dsh <args...>     # 透传模式:dsh plugin add/update 前自动过闸门

四个缺口的对应设计

缺口 1 — 升级前置检查(闸门)→ preflight

一次 dsh-guard preflight 回答三个问题,任何一个是"破坏性"就 exit 1 拒绝升级(有警告则 exit 2):

  1. 插件兼容:对目标 DSH 版本,逐个已装插件查
    • 兼容矩阵注册表(机器跑出来的 compat.json,缺口 2 的输出)
    • 作者元数据(插件 package.json 里的 dsh.compat.tested/requires
    • 都没有 → untested 警告,不硬拦
  2. 存储格式破坏性变更:对 $DSH_HOME 做指纹(见下),与 lib/formats.json + 注册表里目标版本的事实比对。格式不同 → BLOCKED(这正是 rc.8 会话全丢事故的闸门)。
  3. 自动备份:闸门通过时先拍 $DSH_HOME 快照(tar + sha256 + manifest)。

为什么闸门只能靠 wrapper + 引导期探针,而不是插件树内钩子? 这是读源码后的事实约束:

  • dsh plugin 是 launcher 里的薄 pnpm 转发器(bin.js 的 switch 分支),没有前置钩子——没有任何插件能挂进 pnpm update 之前。
  • 树内插件解析命令行的路也被堵死:dsh-web-appweb-startup无条件调用 parseCmdline,commander 拒绝未知命令,第二个解析行在同 profile 里必然炸掉整个启动树。

所以设计是:

  • 真正的工作在 独立 bin dsh-guard(不 boot 任何 profile,纯文件检查 + spawn)。
  • cordis.patch.yml 里只挂一个被动行guard-drift):每次 boot 记录 dsh 版本到 $DSH_HOME/.guard-state.json,发现版本变了就打印一行"你没过闸门就升级了"的告警。所有逻辑 try/catch,绝不 fail-loud。
  • 日常纪律用别名:alias dsh='dsh-guard dsh'(PowerShell 里包一层 function)。dsh-guard dsh plugin add/update/install 先过闸门再转发真实 dsh

缺口 2 — 自动化兼容矩阵 → compat/

把"作者有空才写文章"变成机器数据,三层:

  1. 元数据契约:插件在 package.json 声明 dsh.compat
    "dsh": {
      "bundle": { "patch": "./cordis.patch.yml" },
      "compat": {
        "requires": ">=0.1.1-rc.1",
        "tested": ["0.1.0-rc.7", "0.1.1-rc.1"],
        "storageFormats": ["zstd-jsonl"],
        "kind": "tooling"
      }
    }
    
  2. CI 矩阵compat/compat-matrix.yml(可复制到任意插件仓库或中央调度仓库)+ compat/report.mjs。每个 job 在全新 profile(隔离 DSH_HOME)里 npm i -g @deepseek-ai/dsh@<ver>dsh plugin --profile ci add <plugin>dsh --profile ci --dump-config(boot 冒烟:树能组装出来就是过了)→ 输出一行 JSON。collector 合并成 compat.json 并提交。
  3. 注册表 + 徽章compat.jsoncompat/schema.json 组织,托管在 Blue-Whale-Harness 的 compat/ 目录 (已推送,2026-08-25 首版含 8/8 实测数据),CDN 源 cdn.jsdelivr.net/gh/Shizuku-keop/Blue-Whale-Harness@main/compat/compat.jsonlib/registry.js 默认,含 raw + GitHub API base64 回退)。徽章用 shields.io dynamic JSON 直接指 CDN 文件。preflight 消费同一份数据—— 货架上的"保质期标签"。

首版实测数据(2026-08-25,compat/local-matrix.ps1,隔离 DSH_HOME + pnpm 11):

插件 \ DSH0.1.0-rc.70.1.0-rc.80.1.1-rc.10.1.1-rc.2
dsh-better-sidebar 0.15.2✅ pass✅ pass✅ pass✅ pass
dsh-mnemon 0.2.16✅ pass✅ pass✅ pass✅ pass

注意:矩阵验证的是插件 API 兼容(安装 + mount)。rc.8 的存储格式变更 (社区报告的数据丢失事故)在数据层——注册表 storageFormats 里 rc.8 仍是 unknown,升级闸门靠存储指纹拦截,不依赖插件 pass。

注册表条目示例:

{ "schema": 1, "updated": "2026-08-25T03:00:00Z",
  "plugins": { "dsh-better-sidebar": { "0.1.1-rc.2":
    { "status": "pass", "testedAt": "2026-08-25T03:00:00Z",
      "by": "run 1234", "evidence": "dsh-install:0 plugin-install:0 boot:0" } } },
  "storageFormats": { "0.1.1-rc.2": { "sessionFormat": "zstd-jsonl", "projcacheVersion": 3 } } }

缺口 3 — 会话数据迁移 → migrate

安全优先管线:detect → backup → transform → verify → checkpoint

  • detectLegacy 扫描 $DSH_HOME/sessions/** 的文件头:zstd(28 B5 2F FD)/ sqlite(SQLite format 3)/ gzip / 未知。不知道的格式拒绝转换,只备份——绝不猜。
  • sqlite → zstd JSONL:读用 Node ≥ 22.5 内置 node:sqlite(零原生依赖),写 zstd 帧用外部 zstd CLI 或可选 fzstd;两个都没有就拒绝(裸 .jsonl DSH 读不了)。
  • 原文件在验证通过后才改名 .legacy.bak,新文件先写 .migrating 再原子改名。
  • 诚实边界:每版 DSH 的 session JSONL 记录 schema 必须对照目标版本读文件确认——lib/formats.json 里逐版本登记,没登记就是 unknown(preflight 会因此警告,不会静默放行)。

缺口 4 — profile 级锁文件 → snapshot / verify / rollback

profiles/<name>/dsh.guard.lock.json(随团队仓库提交):

{ "schema": 1, "profile": "web",
  "dsh": { "version": "0.1.1-rc.2", "integrity": "sha256:…" },
  "plugins": { "dsh-better-sidebar": { "version": "0.15.2", "integrity": "sha256:…", "bundle": true } },
  "storage": { "sessionFormat": "zstd-jsonl", "sessionCount": 74, "projcacheVersion": 3 },
  "configHash": { "cordis.patch.yml": "sha256:…", "pnpm-workspace.yaml": "sha256:…", "settings.yaml": "sha256:…" },
  "backup": "backups/2026-08-25T03-00-00-000Z/snapshot.tar" }
  • dsh-guard snapshot:拍快照 + 写锁文件(锁里记录备份路径)。
  • dsh-guard verify:把本机实况与锁文件比对——插件版本、内容完整性、配置 hash、存储格式逐项 diff,输出"你跑得了我跑不了"的具体差异。
  • rollback / restore:解 tar 回写,恢复 pnpm-lock.yaml 后自动 pnpm install --frozen-lockfile;恢复前先做安全快照(永远有回头路)。
  • 快照默认排除凭据文件.credentials.yamlpet.json.gh_*.env),--include-secrets 显式开启——备份是可交给同事的东西,不是泄露源。

关键技术事实(源码核实)

事实影响
dsh plugin = 薄 pnpm 转发器,launcher 无前置钩子闸门只能 wrapper/别名 + 引导期探针
dsh-web-app 无条件 parseCmdline,commander 拒绝未知命令同树内不能有第二个解析命令行的插件 → CLI 必须独立 bin
sessions = session-<uuid>/session.jsonl.zstd(zstd 魔数 28 B5 2F FD,本机实测)格式指纹 = 魔数扫描,廉价可靠,不用解码
storages/session_projcache.jsonunit.version(本机 = 3)缓存格式版本号可进指纹,版本变化 = 警告(会重建,非数据丢失)
bundle 插件 = npm 包声明 dsh.bundle.patchmain 导出 {name,inject,apply},loader 取 exports.default插件包可同时是 CLI + 被动 cordis 行(default 导出插件,命名导出库 API)
$DSH_HOME = $DSH_HOME 环境变量 → ~/.dshdsh-home-paths 源码)路径解析完全对齐官方
版本号权威来源 = launcher package.jsondsh --version);dist-tags 每周在变永远运行时解析 next/latest,绝不硬编码(本文档引用的 rc 号已经过时)

安装与使用

# 作为 CLI(不装进 profile 也能用)
npm i -g dsh-compat-guard        # 或 pnpm add -g

# 装进 profile(可选:获得 boot 期漂移探针)
dsh plugin --profile web add dsh-compat-guard

# 日常纪律:把 dsh 包一层
# bash:  alias dsh='dsh-guard dsh'
# pwsh:  function dsh { dsh-guard dsh @args }

已知边界(诚实声明)

  1. 闸门不是强制性的——launcher 没有钩子,纪律靠别名/团队约定;探针只能事后告警。上游要根治需给 dsh plugin 加 pre-hook,本包是社区侧能做的全部。
  2. 注册表已托管:默认指向 Blue-Whale-Harness 的 compat/(jsDelivr CDN,多源回退),lib/registry.jsDEFAULT_REGISTRY_URL 可换;离线时用 $DSH_HOME/.guard-cache/ 缓存并降级为"只警告"。
  3. lib/formats.json 是种子数据:本机只实测过 0.1.1-rc.2(zstd-jsonl / projcache v3)。rc.7/rc.1 的存储布局必须有人实测登记(或等注册表 storageFormats 补上)——未知 = 警告而非静默放行。
  4. 迁移的 JSONL schema 必须对照目标版本读文件确认;工具对未知格式只备份不转换。
  5. verify 的 integrity 是 sha256(插件 package.json)——检测内容漂移够用,不是 npm integrity 的替代。

开发

node --test test/        # 单元测试(node:test,零依赖)
node lib/cli.js status   # 本机实况(只读)
node lib/cli.js preflight --target next   # 对真实 $DSH_HOME 干跑(会备份!)

License

MIT