Volume 3 · Chapter 35
AI 协同:仓库如何与 agent 一起写代码
前 34 章读的都是「dsh 是什么」。这一章换个方向:这个仓库本身是怎么被写出来的。它的 6600+ 文件里有一套完整的 agent 协同基建——分层指令、1182 篇决策记录、14 个可复用工作流、55 个把文档规则变成编译错误的校验门。这套东西不是产品能力,但它解释了为什么一个 agent 深度参与的项目还能保持结构。
示例本次示例:一次典型的 agent 改动
# ① agent 会话开始 # agent-instructions 插件向上找 .git 定项目根 → 收集 AGENTS.md 链 # → 作为 user/message 注入 inbox(08 页 preStep 的第一步) # 根 AGENTS.md 175 行 = 「每次会话都要在上下文里的常备命令」 # # ② agent 读代码 → 改 packages/core/agent-loop/src/agent.ts # packages/AGENTS.md(28 行)补充该子树的额外规则 # # ③ 判定「这次改动是不是 non-trivial」 # → 是 → 必须同时写一篇 Agent Note(.agents/notes/README.md 的硬规则) # # ④ 提交前:dsh-pre-push-checks skill 选最小的检查集 # → 不跑全量测试(CI 负责穷尽性) # # ⑤ 推送 → CI 跑 55 个 verify-* 门 # → verify-agent-note-format 检查笔记的段落骨架 # → verify-doc-budgets 检查文档有没有超字数 # → verify-md-links 检查相对链接 # # ⑥ 合入。决策留在 .agents/notes/implemented/ 里,供后来者查「为什么」
# AGENTS.md 层级文件:22 个(根 + 各子树) # Agent Notes:1182 篇英文正本 # implemented/ 487 proposed/ 39 rejected/ 14 archived/ 640 # 按月份:2026-06 = 68,2026-07 = 385,2026-08 = 372,2026-09 = 159 # 可复用 skill:14 个(.agents/skills/*/SKILL.md) # 校验脚本:257 个(scripts/*.ts),其中 verify-* 门 55 个 # 文档门聚合(doc-sync):约 35 个 leaf gate # # 换算:平均每天新增 ~10 篇决策记录。
§1分层指令:AGENTS.md 与它的符号链接
仓库的 agent 指令放在 AGENTS.md,按目录分层,共 22 个。切分依据写在根文件自己的规则里:
The launcher parses only what it owns… Root AGENTS.md = Standing orders: rules an agent needs in context in every session, one to three lines each, linking its home.
| 层级 | 文件 | 行数 | 职责 |
|---|---|---|---|
| 根 | AGENTS.md | 175 | 常备命令:仓库布局、命令、约定、防御模式。每条规则 1-3 行 + 一个指向「家」的链接 |
| 包树 | packages/AGENTS.md | 28 | 该子树专属:插件导出形态、可选服务、REAL-composition 测试要求、Service Definition 设计规则 |
| 文档树 | docs/AGENTS.md | 76 | 文档标准:分层分类法、写作规则、字数预算、slop 清单 |
| 笔记树 | .agents/notes/AGENTS.md | 7 | 新笔记触发 supersession 检查 |
| 其余 | .github/ vendor/ scripts/ snapshots/ benchmarks/ website/ | 3-19 | 各自子树的补充规则 |
核心设计:一个事实只有一个家。docs/AGENTS.md 的分层表把这条规则写成了表格——每个 tier 明确「该放什么」和 「不该放什么」。根 AGENTS.md 不放故事、不放示例、不放情景化流程;这些都属于「→ 链接到它的家」。一个字面意义上的 single source of truth。
CLAUDE.md 是符号链接
如果你用 Claude Code 打开这个仓库,它会读 CLAUDE.md——但那个文件只有 9 个字节,内容是 AGENTS.md。git 视角下它是一个符号链接(mode 120000):
$ git ls-files -s CLAUDE.md packages/CLAUDE.md
120000 47dc3e3d86... 0 CLAUDE.md
120000 47dc3e3d86... 0 packages/CLAUDE.md
$ cat CLAUDE.md
AGENTS.md
根 AGENTS.md 的第 171 行把这条规则写明了:「CLAUDE.md symlinks AGENTS.md at root and packages/; edit the real file.」——不要编辑符号链接,编辑真身。这样同一份指令同时服务 Claude Code(读 CLAUDE.md)和其他遵循 AGENTS.md 约定的 agent,不存在两份需要同步的副本。这是「一个事实一个家」在工具兼容层面的应用。
§2Agent Notes:决策记录体系
这是整个协同基建里最有分量的一块。1182 篇英文正本(另有等量的 .zh.md 中文对照与 sidecar),放在 .agents/notes/。
两条轴:生命周期 × 类别
规则文件 .agents/notes/README.md 把它们编码进路径:{lifecycle}/{class}/yyyy-mm-dd-topic-title.md。
| 生命周期 | 含义 | 篇数 |
|---|---|---|
proposed/ | 已评审但尚未实现(或只实现了一部分)的提案 | 39 |
implemented/ | 决策已落地。必须与代码保持同步——代码移动文件、改名、改变默认值时,笔记在同一次变更里更新事实 | 487 |
rejected/ | 考虑过并否决。只在其理由能阻止一个诱人的错误时保留 | 14 |
archived/ | 冻结的历史快照。永不编辑,也永远不作为当前行为的权威 | 640 |
类别是闭集,定义在 scripts/agent-note-tree.ts:
11/** The closed set of active Agent Note lifecycles (top-level folders under .agents/notes/). */
12const AGENT_NOTE_LIFECYCLES = ['proposed', 'implemented', 'rejected'] as const
14/**
15 * The closed set of Agent Note classes (nested folder under each lifecycle). Adding a
16 * class is a deliberate act: extend this list AND the README's Classification
17 * section. The gate rejects any folder not listed here.
18 */
19export const AGENT_NOTE_CLASSES = ['feature', 'bug-fix', 'simplification', 'architecture', 'process', 'testing'] as const六类,加一类需同时改 README
「加一类是一个刻意的动作」:注释明确要求同时改这个常量和 README 的分类章节。architecture 与 process 的分界被特别说明——前者关于「我们发布的源码」,后者关于「围绕代码的工具与流程」。refactor 被刻意排除:它与 simplification 重叠,而后者的判据「可观测行为变了吗」已经覆盖了它。
段落骨架是强制的
每篇笔记的骨架由 scripts/verify-agent-note-format.ts(94 行)强制执行。这份门本身就是「把文档规则变成编译错误」的典范:
12/** The date these format rules took effect; the grandfather comment is valid only before it. */
13const FORMAT_ADOPTED = '2026-07-05'
15/** The exact comment a pre-format Agent Note carries in place of `## Alternatives considered`. */
16const GRANDFATHER = '<!-- agent-note-format: alternatives-not-recorded (pre-format Agent Note) -->'
18/** The retired debt marker that flagged pre-format bodies; banned so it cannot creep back. */
19const LEGACY_MARKERS = ['XXX: legacy ADR/RFC body format', ...]
21/** Status-line grammar per lifecycle folder. */
22const STATUS: Record<string, RegExp> = {
23 proposed: /^Status: proposed$/,
24 implemented: /^Status: implemented$/,
25 rejected: /^Status: rejected — .+$/, // 唯一带内容的状态
26}
28/** Required `##` headings per lifecycle, beyond the universal `## Problem` opener. */
29const REQUIRED: Record<string, string[]> = {
30 proposed: ['## Proposal', '## Acceptance criteria', '## Risks'],
31 implemented: ['## Decision', '## Consequences'],
32 rejected: ['## Proposal'],
33}
35/** Headings banned in `implemented/` — proposal-era spec-speak per the slop checklist. */
36const BANNED_IMPLEMENTED = /^## (?:Proposal\b|Plan\b|Migration plan\b|Acceptance criteria\b)/i实现后不许再用「计划」语气
FORMAT_ADOPTED = '2026-07-05':格式规则生效日。此前的笔记可以携带「豁免注释」,此后的不行(第 82 行断言)。规则变更自带时间边界,老笔记不用回填。
状态行语法按生命周期分别定义。rejected 是唯一要求带内容的(rejected — <一句话原因>)——因为「为什么否决」正是读者来看的东西。
骨架随生命周期不同:提案有 ## Proposal/## Acceptance criteria/## Risks(未来时态合理);已实现是 ## Decision/## Consequences(现在时描述已发布的事实)。
禁止清单:implemented/ 里不许出现 ## Proposal、## Plan、## Migration plan、## Acceptance criteria——这些是「规格语言」,决策落地后它们就过时了。门会强制你把内容折进 Decision/Consequences。
Alternatives considered 是必填的
README 里的原话:「A decision recorded without what it beat invites re-litigation — the failure Agent Notes exist to prevent.」
每一篇笔记都必须有 ## Alternatives considered 段。格式门第 78-82 行检查它是否存在——唯一豁免是 2026-07-05 之前的老笔记,且必须携带一个精确的注释字符串(不是随便写一句「理由见别处」)。替代方案只记录,不许发明。
归档是单向的
archived/ 树(640 篇)被永久冻结:不许编辑、翻译、重排版、更新、移动或删除。scripts/verify-archived-agent-notes.ts 强制一套追加式的内容清单(append-only frozen-content manifest)——归档时的哈希被记录下来,之后任何改动都会被发现。
归档本身有明确判据(README 的 Archiving 章节):当已发布的决策已经完成、且其理由不太可能指导未来工作时。反面清单同样明确——如果「替代方案、所有权边界、否定性保证、持久或线上语义、安全规则、重新引入的条件」中任何一项仍然有用,就保持活跃。工具是 dsh-archive-agent-notes skill,README 特意强调:不要用字数、年龄或配额来判据。
§3Skill:可复用的工作流
.agents/skills/ 下有 14 个 skill。它们不是产品文档——按 docs/AGENTS.md 的分层表,skill 这个 tier 的职责是「可复用的工作流与专门的决策标准」,明确不放产品与运行时契约(那些属于 docs 或源码)。
一个 skill 长什么样
每个 skill 是一个目录,核心是 SKILL.md,带 YAML frontmatter。以 dsh-pre-push-checks 为例(136 行):
1---
2name: dsh-pre-push-checks
3description: Use before pushing, force-pushing, marking ready for review, or claiming checks pass on a deepseek-harness branch, and immediately after gh stack sync publishes rewritten branches, to select the smallest tests and checks that cover the outgoing or just-published diff without reflexively running the full repository suite.「何时用」写在 description 里
4---
6# DSH Pre-Push Checks
10## Inspect the outgoing change
27## Select relevant evidence
52### Focus unit coverage on the affected source
75## Full local rehearsal
79## Protect history-rewriting pushes
96## Handle failures
107## Push procedure
frontmatter 只有两个字段(全部 14 个 skill 都是如此,只有一个例外见下)。加载器要求两者都存在,缺失就忽略该文件。
description 就是触发器。注意写法:不是「这个 skill 做什么」,而是「Use before pushing, force-pushing, marking ready for review, or claiming checks pass…」——从 agent 的决策时机出发描述何时该调用它。这是 skill 能被正确自动触发的关键。
正文是从「检查什么」到「怎么推」的完整流程。结构在所有 skill 里一致:先给判据,再给动作。文档门 verify-skill-invocation-metadata 检查这个元数据格式。
14 个 skill 的全貌
| skill | 行数 | 用途 |
|---|---|---|
dsh-doc | 131 | 文档的创建/重构/审查/审计/迁移。带 5 个 references + 7 个 templates |
dsh-find-simplifications | 157 | 找非显然的简化候选、移除冗余、合并被取代的 Agent Note |
dsh-pre-push-checks | 136 | 推送前选最小的检查集,不反射性地跑全量测试 |
dsh-ci-test-reliability | 131 | 设计/诊断在 CI 上会不确定失败的测试。带 references/ |
dsh-merging-stacked-prs | 127 | 把依赖栈(A ← B ← C)落到 master |
dsh-speed-up-perf | 92 | 性能调查:先建逼真基准,再证明回归,最后删工作 |
dsh-prose-standard | 81 | 编辑判断标准。明确自称「guidance, not a script」。带 references/examples.md(169 行) |
dsh-translate-docs | 74 | 双语文档工作流。只能手动触发(见下) |
dsh-archive-agent-notes | 68 | Agent Note 的归档与 supersession 检查(§2 用到) |
dsh-code-review | 52 | 审查 PR,让审查者熟悉这个代码库特有的标准 |
dsh-trim-cot-leakage | 45 | 猎捕「泄漏的推理记录」。带 2 个 references(275 + 52 行) |
agent-experience | 13 | 0.1.7 新增:设计 agent 工具、skill、上下文加载与多步工作流时,让信息可被发现、让上下文用得省 |
dsh-client-ui-ux | 62 | 0.1.7 新增:客户端 UI 改动的设计与审查——视觉 token 纪律、先复用再加新、反馈面(toast vs 就地提示)的选择 |
record-browser-gif | 172 | 把 Web UI 交互录成 GIF。带可执行脚本 scripts/encode_gif.py(337 行)+ 测试 |
形态差异值得注意:多数 skill 是纯 Markdown 流程,但 dsh-doc 配了模板库、record-browser-gif 配了 Python 编码脚本。skill 可以携带任意辅助资源——加载器只认 SKILL.md,目录里的其他文件由 skill 自己引用。
别和 packages/skill/ 混淆:那个目录是 skill 的运行时实现(skill / skill-filesystem / tool-skill / skill-office 等,见 23 页),是产品代码;.agents/skills/ 是这个仓库自己用的工作流,是工程实践。两者同名但不同层——前者的 skill-filesystem 加载的正是后者。
唯一带额外 frontmatter 的 skill
dsh-translate-docs 的 frontmatter 是 6 行而不是 4 行:
1---
2name: dsh-translate-docs
3description: Manually run the extended DeepSeek Harness bilingual-document workflow, ...
4disable-model-invocation: true模型不能自己调用它
5user-invocable: true只有用户能调用
6---
这两个字段控制「谁能调用」。加载器把任意 frontmatter 解析成 SkillInvocationPolicy,而 catalog 渲染时用 filter(isModelInvocable) 过滤——所以 disable-model-invocation: true 的 skill 根本不会出现在模型的 <available_skills> 清单里。它只能由用户显式调用。
为什么这个 skill 需要这道闸?因为它做的是大批量双语文件改写——根 AGENTS.md 明确写着「only explicit user invocation may run dsh-translate-docs」。自动触发会让模型在无关的改动里顺手重写几十个翻译文件。
.claude/skills 也是符号链接
§1 提到 CLAUDE.md → AGENTS.md 的符号链接。同一招在这里又用了一次:
$ git ls-files -s .claude/skills
120000 2b7a412b8f... 0 .claude/skills
$ cat .claude/skills
../.agents/skills
Claude Code 从 .claude/skills/ 发现 skill,而这个仓库把整个目录链接到 .agents/skills/。同一份 skill 定义同时服务两个工具,不存在副本。这是「一个事实一个家」第三次出现(前两次是 CLAUDE.md 和文档分层)。
.agents/skills/.gitignore 只有一行:*/agents/openai.yaml——按工具约定忽略其他 agent 的本地元数据,但这恰恰说明目录本身是共享的。
本章讲的是仓库自己的 skill;skill 作为产品能力的加载机制在 23 页。两者的连接点很有意思——加载这些 SKILL.md 的正是 dsh 自己的 skill-filesystem 插件。仓库既是这套机制的使用者,也是它的实现者。§5 会看完整的加载链路。
§4把文档规则变成校验门
这是整套基建里最工程化的一环。scripts/ 下有 257 个 TypeScript 脚本,其中 55 个是 verify-* 门。文档相关的约 35 个聚合在 pnpm run doc-sync 里。
文档标准的关键条目
docs/AGENTS.md(76 行)定义标准,其中几条被门直接执行:
| 规则 | 执行它的门 |
|---|---|
| 每段必须是一个物理行(用编辑器软换行,不要硬折行) | verify-md-wrap |
| 相对 Markdown 链接必须指向存在的文件 | verify-md-links |
| 正文里不许出现真实 commit id 或不被允许的组织 URL | verify-repository-references |
| 文档字数不得超过预算清单 | verify-doc-budgets |
围栏 ts 代码块必须能编译;粘贴的类型声明要登记防漂移 | doc-typecheck / verify-type-equiv |
| 中英对照必须逐段对齐 | verify-translation-pairing |
| 包 README 必须有 Summary / Model Experience / Known Limitations 段 | verify-package-readme-summaries 等三个门 |
| Agent Note 的路径、状态行、段落骨架 | verify-agent-note-classification / -format / archived-agent-notes |
| skill 的调用元数据格式 | verify-skill-invocation-metadata |
字数预算是硬约束
scripts/doc-budgets.manifest.json 是一份很短的清单——它只约束「常备文档」:
1{
2 "AGENTS.md": 1950,
3 "docs/AGENTS.md": 1320,
4 "docs/architecture.md": 2400,
5 "docs/cordis-primer.md": 600,
6 "docs/defensive-patterns.md": 550,
7 "docs/testing.md": 1350,
8 "packages/AGENTS.md": 750,
9 "packages/README.md": 994
10}
门红了怎么办?docs/AGENTS.md 给的是一套有序的处置流程(第 52-58 行),顺序本身表达优先级:
- Relocate——内容属于别的 tier,搬过去,必要时留一行链接
- Condense——内容确实该在这,但可以更短
- Raise——只有文字确实需要空间时才提高上限,且要在 PR 里论证
为什么是硬约束而不是「建议」?因为这些文档是常驻上下文——根 AGENTS.md 每次会话都会被注入(§5)。一个无限膨胀的指令文件会挤掉真正的工作内容。上限 1950 词不是审美偏好,是上下文预算。文件里还写着:「A too-low ceiling is a budget bug」——上限过低同样是 bug,说明这条预算被当成压缩目标而不是护栏了。
slop 清单:可执行的编辑标准
docs/AGENTS.md 的第 60-72 行是一份「slop 清单」——不是模糊的「写得好一点」,而是九条可检查的病症:
- 重复的规则:搜一个有辨识度的短语;只留一个家,其余改成链接
- 历史出现在不该出现的 tier:陈述当前事实 + 链接历史所有者
- 实现状态标注(「implemented!」「future: …」):状态会腐烂,仓库布局和 manifest 才是权威
- 手工重述的目录:源码或生成器才是权威
- 推理记录:逐步实现叙述、显然分支的证明、测试走查、被否决的本地替代方案
- 段落墙:一段塞了好几条规则和插入语
- 强调膨胀:处处加粗 = 什么都不突出
implemented/笔记里的规格语言:「should」、迁移计划、验收清单
dsh-doc skill 把这份清单当作审计流程来跑。
§5闭环:指令如何进入模型
前面四节讲的是「仓库里有什么」。这一节讲这些东西怎么真正到达模型——答案就在 dsh 自己的代码里。有两条独立的注入链路:一条送 AGENTS.md,一条送 skill 目录。
链路 A:AGENTS.md → 上下文
负责的插件是 packages/context/agent-instructions(360 行)。模块注释一句话说清职责:
Workspace instruction loader for AGENTS.md-compatible files.
…tool touches project nested, changed, and removed instructions into the inbox.
11const DEFAULT_PROJECT_ROOT_MARKERS = ['.git'] as const
12const DEFAULT_INSTRUCTION_FILE_CANDIDATES = ['AGENTS.md', 'CLAUDE.md'] as const同一目录里两个都读
13const DEFAULT_LOCAL_INSTRUCTION_FILE_CANDIDATES = ['AGENTS.local.md', 'CLAUDE.local.md'] as const
14const DEFAULT_MAX_SOURCE_BYTES = 1_048_576
向上找 .git 定项目根——从会话的 cwd 往上走,遇到 .git 就停下。这是「哪个 AGENTS.md 链适用于我」的判据。
AGENTS.md 与 CLAUDE.md 并列在候选列表里,同一目录两个都存在就都读(按目录内 trim 后的内容去重,重复的折叠到靠前的候选)。.local.md 变体作为覆盖层在基础文件之后加载。这就解释了为什么仓库用符号链接而不是复制——加载器本来就认这两个名字。
它的 README 把完整行为讲清楚了,其中三条对理解协同很关键:
- 首次请求时加载适用的链;
- 不持续监听外部编辑——而是成功的文件系统操作(read/write/edit)发现新出现的嵌套文件、或让后续的修改/删除变得可见;session resume 时重新对齐基线;
- 字节预算限制注入内容:更宽泛的文件先被省略,最具体的文件才会被截断;空链不添加任何东西。
注意第二条的词表耦合:插件监视的工具名是硬编码的——FILE_TOUCH_TOOL_NAMES = new Set(['read', 'write', 'edit'])(index.ts:74)。仓库里改工具名会让这条链路失效,而这类耦合不写在文档里,只能从代码读出来。
链路 B:skill 目录 → <available_skills>
skill 的发现与注入走另一条链,实现也在仓库自己的代码里(这正是 23 页的主题)。解析 .agents/skills/ 的那一行在:
250 { path: join(projectRoot, '.dsh/skills'), source: 'project-dsh', rank: PROJECT_DSH_RANK, projectRoot },
251 { path: join(projectRoot, '.agents/skills'), source: 'project-agents', rank: PROJECT_AGENTS_RANK, projectRoot },本仓库的 12 个 skill 从这里被发现
findProjectRoot 用同一个 .git 判据找项目根,然后拼出两个候选目录。优先级常量定义在同文件 36-40 行:PROJECT_DSH=100 < PROJECT_AGENTS=200 < CUSTOM=300 < USER_DSH=400 < USER_AGENTS=500——数字小的赢。所以项目内的 skill 会遮蔽用户级同名的。
发现之后,每个 SKILL.md 被解析(parseSkillFile,index.ts:797):frontmatter 缺 name 或 description 就忽略整个文件,正文作为 content。然后 tool-skill 把它们送进模型:
<available_skills>226-227226 const skills = snapshot.skills.filter(isModelInvocable)不可模型调用的被滤掉
227 const entries = catalogSourceEntries(skills, catalogDescriptionMaxLength)
这段跑在 agent/pre-step 监听器里(index.ts:213),产出的是一个 <available_skills> 块作为 user message。模型看到的是 skill 的名称与 description,不是正文——它要按 description 匹配,然后显式调用 skill 工具才拿到内容。这正是 description 为什么要写成「何时用」的原因。
闭环
把两条链连起来:
仓库根 AGENTS.md(175 行常备命令) → agent-instructions 插件首次请求时读入 → 作为 user/message 注入 session inbox(08 页 preStep 的 claim) → 进入模型上下文 .agents/skills/*/SKILL.md(12 个) → skill-filesystem 扫描 .agents/skills/(source: 'project-agents') → tool-skill 在 agent/pre-step 渲染 <available_skills> 块 → 模型按 description 匹配 → 调 skill 工具读正文 → agent 按指令改代码 → 推送前跑 dsh-pre-push-checks skill 选检查集 → CI 跑 55 个 verify-* 门 → non-trivial 改动必须附一篇 Agent Note → 决策沉淀回 .agents/notes/,供下一个 agent 查「为什么」
仓库用 dsh 的机制管理 dsh 自己的开发。这不是比喻——agent-instructions 与 skill-filesystem 都是随 dsh-base 默认启用的插件。你在读的这套指令注入链路,就是你在用的那套。
还有个细节值得注意:packages/AGENTS.md 里有一条规则专门管这件事——「Write model-facing contracts from the model's perspective」。它要求 prompt、tool schema、结果、诊断里只出现任务相关的概念,不带 UI、传输层或实现词汇。而 agent-instructions 注入的正是「模型可见文本」,所以这套指令文件本身也要遵守那条规则。规则递归地适用于写规则的文件。
§6这套做法的取舍
把 1028 篇决策记录、12 个 skill、55 个校验门当作一个设计来读,它选择的是什么?
| 选择 | 得到 | 付出 |
|---|---|---|
| 决策写进仓库(Agent Notes),不留在 PR 讨论里 | 理由可检索、可交叉引用、随代码一起演化;不依赖 GitHub 的可访问性 | 每篇都要维护中英对照与 sidecar;实现变更时必须同步更新 |
| 「替代方案」必填 | 阻止重新litigation——后人不必再问「当初为什么不用 X」 | 写一篇笔记的成本显著上升 |
| 归档永久冻结 + 哈希清单 | 历史不可篡改,审计可信 | 归档判据必须谨慎;错误归档无法挽回 |
| 文档规则由门执行(而非 review 时人工判断) | 标准一致、不依赖审查者的记忆;agent 能自己发现违规 | 257 个脚本本身需要维护;规则改动的成本更高 |
| 字数预算是硬上限 | 常驻上下文不膨胀——这直接决定 agent 每次会话能用多少 token 干活 | 内容必须搬家或精简,不能「先加上回头再整理」 |
| 指令用符号链接而非复制 | 一个事实一个家;Claude Code 与其他 agent 共享同一份 | Windows 上符号链接需要额外权限(仓库仍提交为 mode 120000) |
贯穿始终的一条原则:把「纪律」变成「可执行的断言」。这与前 34 章反复出现的 fail-loud、模型可见 ⟺ 已记录、invariant 断言是同一种思路——只不过这里被断言的不是运行时行为,而是协作过程本身。
根 AGENTS.md 第 165 行把这条写成了要求:「Wire mechanically checkable invariants into an executed top-level gate and prove each changed acceptance path rejects an invalid case.」——凡是能机械检查的不变量,都要接进一个真正会跑的门,并且证明每条改动过的验收路径能拒绝一个非法输入。
给读者的一句话:如果你在自己的项目里想引入 AI 协同,这套体系里最值得先拿走的不是 1028 篇笔记的规模,而是三个小东西——① AGENTS.md 分层 + 符号链接共享(成本最低,收益立刻);② ## Alternatives considered 必填(一篇笔记就能看出差别);③ 把一条你最常重复的 review 意见写成 verify-* 脚本(从此不再需要重复它)。