首页
/ deepseek-harness 的 Agent Note 归档机制:冻结双语三元组与追加式哈希封印

deepseek-harness 的 Agent Note 归档机制:冻结双语三元组与追加式哈希封印

2026-09-03 15:32:22作者:劳婵绚Shirley

deepseek-harness 用一套名为 Agent Note 的决策记录体系来保存"代码和文档承载不了的 why"。当一份已实现的记录失去未来指引价值后,它会被整体移入 .agents/notes/archived/ 归档树并永久冻结:本仓库通过 归档树规则文件内容封印清单校验脚本 三层机制,保证归档历史不可被悄悄篡改。读完本文,你将掌握这套归档体系的目录契约、六行冻结头部格式、追加式 manifest 的校验原理,以及标准的归档操作全流程。

归档树定位:冻结的历史快照,而非现行权威

.agents/notes/archived/AGENTS.md 是归档树的治理规则,全文仅数行但约束极其严格。其核心立场可以概括为三条:

  1. 冻结即权威失效:kind 目录下的归档 Agent Note 三元组是"冻结的历史快照,不是现行权威"。任何情况下都不得编辑、重排、翻译、修复、删除或移动已封印的产物;新的判断必须依据活动 Agent Note 或当前文档。
  2. 归档变更的允许面是封闭的:一次合法的归档变更只做四件事——整体搬迁完整的英文/中文/sidecar 三元组、在两处 Status: implemented 行下方插入相同的 Archived: YYYY-MM-DD 行、重新记录 sidecar 一致性哈希、修复或删除指向它的入站链接。除此之外的一切内容变更都是违规。
  3. 出站链接永不检查:明确禁止检查、验证或修复"从归档 note 出发"的链接——这是后面校验脚本中一个刻意保留的设计空隙。

这条规则与活动生命周期树形成对照:在 .agents/notes/README.md 定义的体系中,活动 note 位于 proposed/implemented/rejected/ 三种生命周期下,随状态在目录间迁移;只有 implemented 的记录才"有资格"进入归档树,且归档路径被编码为 archived/{class}/yyyy-mm-dd-topic-title.md——路径中刻意省略 implemented,因为"只有已实现的 note 才能进入归档"。

目录结构:封闭的 kind 集合与三元组命名

归档树的结构校验由 scripts/verify-archived-agent-notes.ts 执行,其目录契约是:

  • 归档根目录下只允许两个根文件:AGENTS.mdmanifest.json(见 verify 脚本 中的 allowedRootFiles);出现任何第三个根文件直接报错。
  • 根目录下只允许六个 kind 目录,且六个一个都不能缺,缺任何一类都报 required kind directory is missingverify 脚本)。
  • kind 集合是封闭的,定义在 scripts/agent-note-tree.tsAGENT_NOTE_CLASSES 中:featurebug-fixsimplificationarchitectureprocesstesting。未知目录会被判定为 unknown Agent Note kind
  • kind 目录内只允许普通文件(不允许子目录),文件名必须匹配正则 {kind}/yyyy-mm-dd-topic.{md,zh.md,i18n.yaml}——该正则在 scripts/archived-agent-notes.tsvalidateArchiveArtifacts 中实现。

因此每一份归档记录天然是一个三文件一组的完整单元:英文 foo.md、中文 foo.zh.md、双语一致性 sidecar foo.i18n.yaml。以现存归档为例:2026-06-11-custom-schema-dsl.md2026-06-11-custom-schema-dsl.zh.md2026-06-11-custom-schema-dsl.i18n.yaml 组成 architecture 类下的一个完整三元组。validateArchiveArtifacts 会对每一份三元组做完整性检查,缺任一文件即报 incomplete archived triplet 并列出缺失项(scripts/archived-agent-notes.ts)。

冻结的六行头部:每份归档 note 的强制格式

归档 note 的头部六行是逐行校验的封闭格式。scripts/archived-agent-notes.tsvalidateHeader 对英文版与中文版分别施加同一套规则(仅第 6 行语言切换器不同):

行号 强制内容 校验逻辑
1 # Agent Note: <标题> 必须以 # Agent Note: 开头且标题非空
2 空行 非空即报错
3 Status: implemented 归档 note 必须保留原始实现状态
4 Archived: YYYY-MM-DD 必须是真实历法日期(闰年感知),且不得早于文件名中的日期
5 空行 非空即报错
6 语言切换器 英文版固定为 English | 中文;中文版固定为 English | 中文

其中两个细节值得展开:

  • 日期合法性不是简单的正则匹配。validDateDate.UTC 构造日期再取回年/月/日比对,因此 2026-02-30 这类"看起来合法"的日期会被拒绝。
  • 归档日期必须晚于等于 note 首次提出日期:文件名本身编码了首次提出日期(yyyy-mm-dd-topic.md),校验会拒绝 Archived 日期早于该日期的三元组(scripts/archived-agent-notes.ts),从机制上杜绝"时间线倒挂"的伪造归档。
  • 英文与中文两侧的 Archived: 日期必须完全一致,否则报 English and Chinese archive dates differscripts/archived-agent-notes.ts)。

一个真实样例(2026-06-11-custom-schema-dsl.md 的头部):

# Agent Note: Custom typed tool-schema DSL instead of schemastery

Status: implemented
Archived: 2026-07-26

English | [中文](https://gitcode.com/gh_mirrors/de/deepseek-harness/blob/cd5ef8148158c3a752a658978873241fdf8e2bbc/.agents/notes/archived/architecture/2026-06-11-custom-schema-dsl.zh.md?utm_source=gitcode_repo_files)

Sidecar 一致性记录:双语双方的 Git Blob 哈希

三元组中的 .i18n.yaml 文件不是普通的 i18n 配置,而是一份双语一致性记录。以 2026-06-11-custom-schema-dsl.i18n.yaml 为例,注释行之后恰好是两行映射:

2026-06-11-custom-schema-dsl.md: e09fea6c4bb80e287b1b64471eee4c87f24fba4a
2026-06-11-custom-schema-dsl.zh.md: 2bacbde02838ef61b05f38796bbeeea262fc2d23

这里的值不是普通的 sha256,而是Git blob 对象 IDgitBlobHash 按 Git 对象格式计算——对 blob <字节长度>\0<内容> 求 SHA-1。这样 sidecar 中的哈希与 Git 历史中的 blob 直接对应,归档产物在仓库对象数据库里可追溯。

校验时 pairMeta 要求 sidecar 去掉注释后恰好包含两行 <文件名>: <40位十六进制> 记录,且两个值必须与当前两份语言文件的 blob 哈希逐一相等scripts/archived-agent-notes.ts)。这解释了为什么归档规则把"重新记录 sidecar"列为唯一允许的内容变更之一:插入 Archived: 行会改变两份语言文件的字节内容,从而改变 blob 哈希,sidecar 必须随之机械地重录。

manifest.json:追加式的内容封印

.agents/notes/archived/manifest.json 是覆盖全部归档产物的内容清单,目前为 512 行、记录了六个 kind 目录下所有三元组的哈希。它的格式是一个封闭 schema

{
  "version": 1,
  "files": { "architecture/2026-06-11-custom-schema-dsl.md": "sha256:f05d…" }
}

parseArchiveManifest 的解析规则体现了封闭性:顶层字段排序后必须恰好是 files,version 两个,多一个少一个都抛错;version 必须等于 1;每个哈希必须是 sha256: 前缀加 64 位小写十六进制。这里的哈希由 archiveContentHash 计算,直接对文件字节求 SHA-256——注释明确说明它"独立于仓库的 Git 对象格式",即封印绑定的是内容本身而非 Git 对象。

追加式(append-only)语义分两层实现:

  1. 基线比对validateArchiveManifestExtension 以某个 Git 提交中的旧 manifest 为基线,逐一检查其中每个已封印条目:在当前 manifest 中消失sealed manifest entry is missing哈希变化sealed manifest hash changed。已封印的历史条目既不能删也不能改。
  2. 全量重验 + 只增不删extendArchiveManifest 对现有 manifest 中每个路径重新读取磁盘内容并重算哈希(不一致即 sealed content hash changed),然后仅为磁盘上尚未封印的新产物追加哈希,并返回追加清单。

基线从哪来?verify 脚本 用三条 Git 命令读出基线 manifest:git cat-file -e <ref>^{commit} 确认引用存在、git ls-tree 检查该提交中 manifest 路径是否存在、git show <ref>:<path> 读取内容。基线引用取自环境变量 DSH_ARCHIVE_BASE_REF,缺省为 HEAD——脚本注释写明了分工:CI 提供可信的变更前提交,本地写入则与已提交的 HEAD 比对verify 脚本)。这意味着即使有人在工作区里同时篡改了磁盘文件与 manifest,只要 CI 以真实基线比对,篡改仍会被检出。

verify 命令:从校验到封印

归档体系的执行入口是 package.json 中的 npm script:

pnpm run verify-archived-agent-notes          # 常规校验模式
pnpm run verify-archived-agent-notes --write  # 追加封印模式

verify 脚本 的执行序列是:

  1. 检查归档根目录,只放行 AGENTS.mdmanifest.json 两个根文件,其余根文件直接报 unexpected root file
  2. 校验六个 kind 目录齐全、kind 目录内只有普通文件,并把全部产物读入内存(verify 脚本);
  3. 调用 validateArchiveArtifacts 做三元组完整性、六行头部、双语日期一致性、sidecar blob 哈希四项检查;
  4. 读取基线 manifest 并做追加式比对;
  5. 调用 extendArchiveManifest 重验全部封印并收集新产物。

两种模式的差异只有一处:非 --write 模式下,任何磁盘上存在但尚未封印的新产物都会报 archived artifact is not sealed in manifest.jsonverify 脚本);--write 模式则允许把这些新哈希追加写回 manifest,且只追加、不改动任何既有条目。这正是规则文件所说的"正常校验器拒绝被改动或缺失的封印产物、不完整三元组、未知 kind 目录和非法归档元数据"的完整含义。纯函数部分(manifest 解析、渲染、扩展、产物校验)全部收敛在 scripts/archived-agent-notes.ts 中,由 scripts/archived-agent-notes.spec.ts 提供聚焦测试。

标准归档操作全流程

操作层面的完整流程由 dsh-archive-agent-notes 技能 定义,其归档一份已实现三元组的五步是:

  1. 整体搬迁:把 foo.mdfoo.zh.mdfoo.i18n.yaml 三件套从 implemented/<kind>/ 移到 archived/<kind>/implemented 不出现在归档路径中。
  2. 仅插入元数据行:在两份语言文件的 Status: implemented 正下方各插入一行 Archived: YYYY-MM-DD,使用归档日期且两侧同值;正文一字不改。
  3. 机械重录 sidecar:仅为这次两处元数据编辑重新记录双语 blob 哈希(sidecar 注释给出的命令是 pnpm run verify-translation-pairing --write)。
  4. 处理入站链接:在活动文档中搜索指向该 note 的链接,要么改指当前权威、要么仅在刻意引用历史快照时改指归档路径、要么直接删除;绝不去修复归档 note 内部的出站链接。
  5. 封印并复核:运行 pnpm run verify-archived-agent-notes --write(追加模式先证明既有封印全部匹配,再只追加新三元组的哈希),随后再跑一次常规 pnpm run verify-archived-agent-notes

技能文档同时强调归档决策应基于语义判断而非字数、年龄或配额:已实现 note 中,其备选方案、所有权边界、负面保证、持久化/协议语义、安全规则或"重新引入条件"仍可能指引未来变更的,保留活动;一次性 UI 细节、窄适配器、已闭环的小 bug 才归档;proposed note 永不归档(废弃即 reject);rejected note 只有在其仍然防止一个"诱人且有意义"的错误时才保留。

设计要点小结

从源码结构看,这套机制把"历史不可篡改"落实成了四个相互咬合的工程约束:

  • 内容封印而非对象封印:manifest 绑定文件字节的 SHA-256,独立于 Git 对象格式,改名、改换 blob 都无法绕过;
  • 追加式 + 外部基线--write 只增不改,且 CI 以变更前提交为基线比对,堵死"文件与 manifest 一起改"的作弊路径(verify 脚本);
  • 格式即约束:六行头部、封闭 kind 集合、三元组完整性全部是逐行/逐字校验,任何"顺手修一下"都会让校验器失败;
  • 刻意的非功能:归档 note 的出站链接永不验证——历史快照的价值恰恰在于它保留的是当时的事实与链接状态,修复它反而破坏快照语义。

关键路径索引

登录后查看全文
热门项目推荐
相关项目推荐