第 10 章 看不见却异常结实的骨架
代码基线:commit
8f86b979a1(2026-10-01)|0.2.0-rc.1| 本系列讲次:M9
本章导读
这一章讲这个仓库凭什么可信。
前三章讲"它是什么",这一章讲"它为什么值得信"。三个数量级:42 个门禁脚本、17 个生成器、38 个发布运行时不变式的包。贯穿的纪律是一句话:凡是靠人自觉的约定,都会被写成一个 verify-*.ts。
先看七层测试阶梯:Unit、覆盖率门禁、真实 API e2e、期望输出、性能基准、快照重放、浏览器快照。然后是本章第一处深刻——覆盖率门禁的官方立场:
An uncovered line is often dead code the gate flags for deletion, not a missing test to bolt on.
Line coverage is necessary, never sufficient.
大多数团队把覆盖率当成"要补的洞",dsh 把它当成"该删的尸"。 而且它自己声明覆盖率是最弱的一层,上面还叠了六层。
本章还有一件正事:兑现方法论 L-12。 第 8 章发现那张生成的接缝表之后,我们说过"手写脚本应改造成对生成表的交叉验证"。本章就做了,结果同时暴露了手写表的三处错和生成表的一处漏——如果你只是改用生成表,会一直相信那张表;如果只是继续用手写表,会带着三处错错下去。交叉验证比任选其一更有价值。
学习要点
- 覆盖率门禁是用来删死代码的,不是用来补测试的
- 门禁可以豁免,但豁免必须环境性、且在更强的环境补齐
- 运行时不变式只发布给"独立观察会分叉"的关系,不是每个包都该有
- 手写与生成都不是免检的,必须互为对照
本章目标
- 摸清工程纪律的三个数量级:门禁、生成器、invariant
- 理解 per-file 100% 覆盖率门禁的真实立场(不是为了补测试,是为了删死代码)
- 看一个真的运行时不变式长什么样
- 兑现 L-12:把 M6 的手写体检表拿去和
docs/capability-seams.md交叉验证
一、工程纪律的三个数量级
| 类别 | 数量 | 说明 |
|---|---|---|
门禁脚本 scripts/verify-*.ts |
42(另有 28 个配套 .spec.ts,共 70 个文件) |
每条约定都有一个可执行检查 |
生成器 scripts/gen-*.ts |
17 | 产物受新鲜度门禁(verify-* 之一)保护 |
发布 ./invariant 的包 |
38 | 运行时关系也有守门人 |
一条贯穿的纪律:凡是靠人自觉的约定,都会被写成一个
verify-*.ts。 从 M2 的verify-cordis-config(!!js只能在disabled)到 M5 的包级规范,全是这个模式。
二、测试阶梯的七层
从 docs/testing.md §Tiers 实测抽出:
| 层 | 命令 | 覆盖什么 |
|---|---|---|
| Unit | pnpm run test |
包级 tests/** + scripts/**/*.spec.ts;每个注册表都要有 HMR 安全测试(dispose 贡献 fiber,断言清理) |
| Coverage gate | pnpm run test:coverage |
packages/*/*/src 按文件 100% |
| Real-API e2e | pnpm run test:e2e |
带 key 打真实供应商 API,缺 key 自跳过 |
| Owner-local expected output | pnpm run test:expected |
无 key 的组装 CLI/进程期望 |
| Performance benchmarks | pnpm run test:bench |
Linux PR 门禁;计时代码跑纯 Node,绝不 TSX |
| Snapshot | pnpm run test:snapshot |
录制会话重放;父文件名 session[.vN].jsonl |
| Web browser snapshot | pnpm run test:web |
Chromium 比对 snapshots/web/;模型选择器另跑 WebKit |
三、★ 覆盖率门禁的立场:它是为了删死代码
这是本章第一处深刻。docs/testing.md 的原文(脚本第 3 项抽出):
An uncovered line is often dead code the gate flags for deletion, not a missing test to bolt on.
Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped.
大多数团队把覆盖率当成"要补的洞",dsh 把它当成"该删的尸"。 这个立场差异是根本性的:
- 补测试的思路:未覆盖 → 写用例 → 覆盖率上升
- 删代码的思路:未覆盖 → 这行可能是没人走的路 → 删掉 → 覆盖率自然上升
第二条更狠:行覆盖证明"这行跑过",不证明"这个功能以发布形态работает"。 所以 dsh 在覆盖率之上还叠了七层阶梯 —— 覆盖率只是其中一层,而且是最弱的那层。
3.1 一个诚实的例外
Per-file 100% on
packages/shell/pwsh-local/srcneeds a realpwsh: without one its executor suites self-skip andvitest.config.tsexempts the file so pwsh-less hosts stay green, while CI runners ship pwsh and enforce the full bar.
没有 pwsh 的机器豁免这个文件,有 pwsh 的 CI 执行全量标准。 门禁可以豁免,但豁免必须是环境性的、且在更强的环境里补上,不是永久降低标准。
四、运行时不变式:session-invariant
packages/core/session/src/invariant.ts:
模块文档: Package-owned relational invariants for the session event log.
Load this companion beside `@deepseek-ai/dsh-invariants` to enable the checks.
name : session-invariant
inject: ['invariants']
行数 : 259
三个设计点:
- 它是 companion,不是主包——必须显式加载。38 个包各自发布一个
inject: ['invariants']——它自己也是一个 Cordis 插件,遵守 M1 讲的那套- AGENTS.md 的纪律:"Publish
./invariantonly when independent observations can diverge... Otherwise omit wiring and give the package-specific README reason."
不是每个包都该有不变式。 只有"两个独立观察会分叉"的关系才配。
跨讲回调:M3 §3.9 讲的"派生历史必须等于实际请求"、M4 讲的 turn/step 配平检查,都住在这类 companion 里。它们不是测试断言(跑一次就完),而是在活的运行时持续比对两个本该相等的观察。
五、★★ 兑现 L-12:交叉验证抓到了我自己的三处错
M8 发现 docs/capability-seams.md 是 scripts/gen-doc-graphs.ts 生成的 94 行接缝权威表。我当时立了 L-12:手写脚本应改造成对生成表的交叉验证。
M9 就做了。结果(脚本第 5 组):
5.1 逐键查证结果
| ctx 键 | Role | Owner | 实现 | 消费者 |
|---|---|---|---|---|
ctx.shell |
seam | shell |
3 | 4 |
ctx.subprocess |
seam | subprocess |
2 | 7 |
ctx.terminals |
seam | terminal |
1 | 1 |
ctx.sandbox |
seam | sandbox |
2 | 2 |
ctx.lsp |
seam | lsp |
1 | 1 |
5.2 ★ 三处分歧
① 我的错:tool-bash-persistent 不是 ctx.shell 的消费者
实测 export const inject = ['tools', 'terminals'] —— 它消费的是 ctx.terminals。我在 M6 E-24 把它算成 shell 的 Consumer,错了。
② 我的错:pwsh-sandbox 是替换者,不是并列实现
它自述:"Registers as ctx.shell in place of the local pwsh executor"。生成表也确实只列了 bash-local / bash-sandbox / pwsh-local 三个实现。"4 个 Provider 并存"的说法不准确。
③ 我的错(最严重):ctx.subprocess 与 ctx.sandbox 的 Provider/Consumer 数全错
| 接缝 | 我 M6 写的 | 实测 |
|---|---|---|
ctx.subprocess |
Provider=1,Consumer=0 | Provider=2(含 subprocess-ssh),Consumer=7 |
ctx.sandbox |
Provider=2(sandbox-local + sandbox-windows-acl) |
Provider=2(sandbox-local + sandbox-ssh) |
ctx.subprocess 的 7 个消费者里包括 bash-local、terminal-bash、lsp-stdio、subagent-acp、subagent-codex、subagent-claude-code —— 我在 M6 写"Consumer=0",等于对这条接缝的理解完全错了。
而且暴露了一个我根本没看过的包组:packages/ssh/(subprocess-ssh、sandbox-ssh)。M6 我逐个组枚举时跳过了它。
④ 生成表的漏:tool-bash-persistent 在整张表里出现 0 次
它确实是 ctx.terminals 的消费者(实测代码),但 ctx.terminals 那行的 Direct consumers 只列了 tool-terminal。生成表在这一处不完整。
5.3 这一节的结论
交叉验证比"任选其一"更有价值——它同时暴露了手写表的三处错和生成表的一处漏。
如果我只是改用生成表(M8 的做法),我会一直相信那张表;如果我只是继续用手写表(M6 的做法),我会带着三处错一直错下去。两个都不够,必须对质。
M6 讲义的勘误见下表。
本章实验(附录)
六、动手验证
export PATH="/opt/homebrew/bin:$PATH"
cd /Users/ygs/ygs/deepseek-harness
node --import tsx/esm "ygsdoc/学习笔记/labs/M9-quality-lab.ts"
五组输出:① 三个数量级;② 七层测试阶梯;③ 覆盖率门禁的三句官方立场原文;④ session-invariant 的形态;⑤ 交叉验证与三处分歧。
M9 通过标准:能说出 ctx.subprocess 的 7 个消费者里为什么会有 lsp-stdio 和 subagent-codex——因为它们都需要拉起真实子进程,接缝之间互相消费(这正是 M6 E-25 记录的那条,只是我当时找错了例子)。
运行(在仓库根目录):
export PATH="/opt/homebrew/bin:$PATH"
cd /Users/ygs/ygs/deepseek-harness
node --import tsx/esm "dsh-源码精读/labs/M9-quality-lab.ts"
预期输出(本机实测,任何一行对不上就说明基线变了):
1) 工程纪律的三个数量级
────────────────────────────────────────────────────────────────────────
门禁脚本 scripts/verify-*.ts : 42
生成器 scripts/gen-*.ts : 17
发布 ./invariant 的包 : 38
2) 测试阶梯(docs/testing.md §Tiers)
────────────────────────────────────────────────────────────────────────
Unit pnpm run test
Every registry gets an HMR-safety test (dispose the contributing fiber, as…
Coverage gate pnpm run test:coverage
An uncovered line is often dead code the gate flags for deletion, not a mi…
Real-API e2e pnpm run test:e2e
…
Owner-local expected output pnpm run test:expected
Drivers use `*.expected.e2e.ts` beside `tests/expected/`; CI runs built ex…
Performance benchmarks pnpm run test:bench
It builds libraries and workers; timed code runs under plain Node, never T…
Snapshot pnpm run test:snapshot
Parent filenames are `session[.vN].jsonl`; child roles are `session.<ordin…
Web browser snapshot pnpm run test:web
CI enforces read-only `DSH_SNAPSHOT=replay`; record/refresh stay local, wi…
共 7 层
3) per-file 100% 门禁的官方立场(原文抽取)
────────────────────────────────────────────────────────────────────────
An uncovered line is often dead code the gate flags for deletion, not a missing test to bolt on.
Line coverage is necessary, never sufficient — it proves lines ran, not that the feature works as shipped.
Per-file 100% on `packages/shell/pwsh-local/src` needs a real `pwsh`: without one its executor suites self-skip and `vitest.
4) 运行时不变式:session-invariant
────────────────────────────────────────────────────────────────────────
模块文档: Package-owned relational invariants for the session event log. Load this companion beside `@deepseek-ai/dsh-invariants` to enable the checks. @module @deepseek-ai/dsh-session/invariant
name : session-invariant
inject: ['invariants']
行数 : 259
发布形态: package.json exports["./invariant"] → 构建产物已存在
→ 38 个包各自发布一个 ./invariant companion;AGENTS.md 规定只在"独立观察会分叉"时才发布
5) ★★ 交叉验证:M6 手工体检表 vs docs/capability-seams.md(生成物)
────────────────────────────────────────────────────────────────────────
生成表列: ctx key | Role | Owner | Implementations | Direct consumers | Companion plugins | Note
全量接缝行数: 94
M6 手工枚举的 5 个执行接缝,逐个到生成表里查证:
…(完整输出见运行脚本)
本章的勘误条目见 附录 A · 勘误总表。