从一个 commit 到下一版:这本书怎样更新
锁 commit 不是永远停在旧版——是让每个"当前实现"有可复核的具体对象。更新有严格的五维度比较流程和四类证据刷新机制,不是批量替换字符串。doc-sync gate 跑完整的文档验证链。
你读到这里,可能有个疑问:这本书锁死在 commit 47f9438,但 DeepSeek Harness 是活的项目,代码在前进。你现在读的内容会不会已经过时了?
这是所有技术文档的终极问题。这本书的答案不是”我们会持续更新”这种空话——它有一套严格的更新方法论和机器验证链。锁 commit 不是为了永远停在旧版,而是让每个章节的每条 SourceEvidence、每个 bash 实验命令、每张 mermaid 图都有一个可复核的具体对象。
这是全书最后一章。
锁 commit 的意义:可复核性
每个章节 frontmatter 里都有 sourceVersion 和 verifiedCommit,都是同一个哈希。这是一个承诺:这一章里所有 SourceEvidence 引用的路径、行号范围、符号名,在这个 commit 上都是可验证的。
你不信任我?三步验证:
git checkout 47f943859bef60e4160492346772ded9b24f765a- 跑章节里的 bash 命令(全是只读 grep/sed/cat,不修改任何东西)
- 对比 SourceEvidence 引用的行范围,看内容是否与注释一致
如果对不上,要么是我写错了,要么是你 checkout 了不同的 commit——不存在”可能是版本不同”的模糊地带。这就是 fixed commit 的全部意义。
repo="${DSH_SOURCE_DIR:?set DSH_SOURCE_DIR to the official fixed checkout}"
cd "$repo" && git rev-parse HEAD
输出应该是 47f943859bef60e4160492346772ded9b24f765a。如果不是,你在验证不同版本的代码。
更新不是批量替换字符串
很多文档更新的做法是:把所有 verifiedCommit: "旧哈希" 批量替换成新哈希,行号大概调调,完事。这是制造漂移的最快方式。
正确的更新第一步:
- 建立新的 detached checkout——记录新 commit 哈希、版本号、日期
- 确认 checkout 干净——没有未提交修改、没有 stash、工作目录清洁
- 不在老 checkout 上直接更新——你需要同时能访问新旧两个 commit 做对比
然后做五维度比较:
- package graph:包的依赖关系变了吗?有新增/删除的包吗?
- public symbols:每个包导出的 API 有变化吗?函数改名了吗?参数变了吗?
- event vocabulary:事件类型有增删吗?EventEnvelope schema 变了吗?surfaceOp 有新值吗?
- config schema:配置项有增删改吗?默认值变了吗?
- 产品入口:CLI flag、Web UI 入口、SDK API 有变化吗?
Owner 变化通常跨多章。如果 credentials 模块从一个包移到另一个包,不只是那一章的行号变了——所有引用 credentials 的章节都要更新。
flowchart TD
A[决定更新到新 commit] --> B[建 detached checkout,确认干净]
B --> C[五维度比较]
C --> D{有架构变化?}
D -->|是| E[重写受影响章节结构]
D -->|否| F[刷新四类证据]
E --> F
F --> G[源码引用验证]
F --> H[可复现实验重跑]
F --> I[叙事审计]
G --> K[最小更新闭包检查]
H --> K
I --> K
K --> L[doc-sync gate 全套通过?]
L -->|否| M[修复,回到 K]
L -->|是| N[真实 dogfood]
N --> O[提交更新]
style O fill:#2e7d32,stroke:#fff,color:#fff
三类声明的证据等级
阅读源码有三个层次,它们能证明的东西不一样:
配置声明只能证明”可以怎样组合”。 cordis.yml、package.json 里的配置项告诉你系统支持哪些组合,但不告诉你运行时实际用了哪些配置。
类型声明只能证明”允许传什么”。 TypeScript 类型告诉你函数接受什么参数形状,但不告诉你运行时参数实际是什么值、错误路径怎么处理。类型正确的代码可能运行时完全错误。
只有调用点与状态写入能证明这版实际怎样运行。 谁调用了这个函数?传入什么参数?事件在哪里 append?状态在哪里修改?失败路径留了什么痕迹?恢复从哪里开始?
写这本书的每一章,我都是从调用点和状态写入反推的,不是从类型签名和配置声明正推的。
doc-sync gate:机器验证文档一致性
DSH 项目自带 doc-sync 运行模式(pnpm run gates doc-sync),跑 20+ 个验证子任务。这不是 lint 空格——是验证文档与源码的结构一致性:
verify-cordis-catalog:文档里的 Cordis API 签名与源码 generated catalog 一致verify-type-equiv:文档里的ts type-equivfence 与源码类型结构等价verify-md-links:所有 markdown 内部链接指向存在的 targetverify-doc-refs:文档里引用的文件路径在仓库中存在verify-mermaid:所有 mermaid 图语法合法verify-export-jsdoc:导出函数的 JSDoc 与文档描述一致verify-tool-catalog:tool catalog 文档与 tool registry 一致verify-config-catalog:config catalog 文档与 config schema 一致verify-persistence-catalog:持久化 catalog 文档与实际 persistence key 一致
这些 gate 跑的是 已 generated 产物与源码的比对——不是 “看起来对不对”,而是 “机器能证明一致”。任何一个 gate 失败,doc-sync 整体 fail,PR 不能合入。
repo="${DSH_SOURCE_DIR:?set DSH_SOURCE_DIR to the official fixed checkout}"
sed -n '571,612p' "$repo/scripts/run-gates.ts"
四类证据分别刷新
更新不是全量重写,是四类证据各自验证:
第一类:源码引用。 所有 SourceEvidence 的 path、lines 在新 commit 上重新验证——文件还在吗?行号范围在文件长度内吗?note 描述的逻辑还成立吗?符号名变了吗?如果函数被 rename,不只是改 note 文本——要确认新名字在 codebase 里的使用场景和旧名字一样。
第二类:可复现实验。 所有 bash 实验命令在新 commit 上重跑——命令还能执行吗?输出还是预期模式吗?有没有新增/删除的输出行改变了结论?grep pattern 还能命中吗?sed 行号范围还在文件长度内吗?
第三类:叙事审计。 每章的核心叙事——“谁拥有状态”、“失败后留下什么”、“恢复从哪里开始”——这些架构性结论变了吗?如果 owner 变了、恢复路径变了、失败边界变了,整个故事要重写,不是改改行号。这是最容易忘、但对读者误导最严重的一类漂移。
第四类:最小更新闭包。 验证清单:
- 50 个章节的
sourceVersion/verifiedCommit/lastVerified全部指向新 commit - 所有 SourceEvidence 能在新 commit 上命中——路径存在、行号范围在文件长度内、note 与实际内容一致
- 8 部连续顺序保持——部与部之间的引出关系、章与章之间的前后承接正确
pnpm run gates doc-sync全套通过——包括 type-equiv、cordis-catalog、md-links、doc-refs、mermaid 等 20+ gate- 真实 dogfood:启动 Web 服务,实际操作几个场景,不碰源码项目
当新 commit 落地时发生什么
一个新 commit 合入 main。这本书的 fixed snapshot 现在落后了。什么时候该更新?怎么判断?
不是每个 commit 都需要更新。 如果新 commit 只改了测试文件、只修了一个 tool 的 output 格式、只加了一个新的 CLI flag——只要它没有改变本书任何一章的核心叙事(owner、边界、恢复路径),更新就是可选的。
必须更新的情况: event vocabulary 变了(新增/删除事件类型)、核心数据流变了(Session append 路径改了)、architecture 级重构(包被合并/拆分)、surface 语义变了。这些改变会让本书的叙事变成谎言。
更新的粒度不是”全书”: 如果只有第 18 章涉及的 bash subprocess 实现变了,只需要更新第 18 章。但所有章节的 verifiedCommit 要一起更新到新 commit——因为这个字段的含义是”在这个 commit 上已验证过”,不是”在这个 commit 上有变化”。这就是为什么需要最小更新闭包检查:确保你没有漏掉关联章节。
验证顺序: 先跑 doc-sync gate 看机器能查出什么。它能发现路径不存在、行号越界、类型不等价、链接断裂。它不能发现的是叙事漂移——“谁 own 这个状态”变了,但代码结构没变(比如一个注释被移到了另一个函数里),机器查不出来。叙事审计靠人。
全书完
你读完了全书 50 章。从 CLI 入口到 Cordis 组装,从 Turn 循环到工具调度,从事件账本到持久化恢复,从前端投影到扩展机制,到这一部的”证明与守卫”。
有件事最后还要再钉一下:这本书不是“教你怎么用 Harness”的用户手册。它是一本 internals——解剖运行时,让你看到每个决策背后的权衡、每个边界存在的理由、每个安全措施防的是什么故障。
这本书的可信度不来自作者的权威。它来自两样东西:
- 可复核性——你能 checkout 那个 commit,自己验证每一条证据
- 机器守卫——doc-sync gate 在 CI 里跑,任何可检测的漂移都无法合入
最好的验证不是相信我写的每一个字。最好的验证是:
git checkout 47f943859bef60e4160492346772ded9b24f765a
然后自己跑命令、自己看代码、自己得出结论。如果你发现我写错了——那就是我写错了,不是”版本不同”、不是”你理解不对”。Fixed commit 的意义就在于消灭这种模糊地带。
这本书只是地图。真正的理解在源码里。
全书完。