为 AI 协作而设计的仓库
AGENTS.md 逐条精读、四态 Agent Note、把工程约定写成 AI 可执行的 skill、质量门禁矩阵。
如果你只打算读本站的一页,读这一页。
前面七个模块讲的都是「dsh 这个软件是怎么建的」,只有这一页讲的东西完全不依赖 dsh:一份为「主要由 agent 写代码」这个前提重新设计过的仓库规范,包括它怎么写规则、怎么留决策、怎么把约定变成可执行的东西、以及怎么用门禁把这一切钉住。
8.1 AGENTS.md 精读:几条最值得偷的规则
根目录的 AGENTS.md(CLAUDE.md 是它的软链)是这个仓库的宪法。它不长,但密度极高。挑几条讲。
还有几条,值得连读
- 「注册是 effect。」见 规则卡 01。
- 「在判别式 tag 上 switch。」封闭联合以
assertNever结尾;可被合并扩展的联合走一个有文档的 default 分支。这两种情况被显式区分开了。 - 「包边界上显式优于隐式。」默认值填充是拥有方实现里一个显式的
resolve(request): Spec步骤,而不是run()里一个藏起来的?? default。 - 「对并列的值优先保持对称。」没有解释的不对称,通常意味着漏了一次抽取。
- 「不要注释代码里显而易见的事实。」以及一整节关于用词的规范——
contract、boundary、shape这几个词被点名要求先想想有没有更精确的说法。 - 「文件以且仅以一个换行结尾」,由 pre-commit 的
git diff --cached --check把关。
8.2 Agent Notes:一个可以直接搬走的 ADR 变体
.agents/notes/ 下有 742 篇决策记录,其中 739 篇有中文对照。它跟常见的 ADR 有几处很聪明的差别。
路径就是状态:{生命周期}/{类别}/yyyy-mm-dd-题目.md
| 生命周期 | 篇数 | 含义 |
|---|---|---|
proposed/ | 26 | 实现前先 review 的提案,尚未(或只部分)建成 |
implemented/ | 561 | 决策已落地,并且持续与实际代码保持同步 |
rejected/ | 11 | 考虑过并否决。只在它的理由还能挡住一个诱人的错误时才保留,否则整套删掉 |
archived/ | 144 | 已实现且未来指导价值不大的,移进一棵永久冻结的树 |
表 8.2 英文正本计数,核对于 commit b150a55。{类别} 是一个封闭集合:feature / bug-fix / simplification / architecture / process / testing,由 verify-agent-note-classification 强制,加别的目录会被拒。
几个设计细节:
implemented/是要维护的,不是存档。「代码后来移动了一个文件、重命名了一个包、改了一个 key 或默认值,这篇 Agent Note 要在同一次改动里被更新以匹配(只改事实——路径、名字、结构——不改决策本身)。」- 一篇 note 永远不会被改成另一个决策。要变,就写一篇新的去取代它,两篇互相链接。
- 没有
INDEX.md。而且这个「没有」本身是一篇决策记录(2026-07-19-remove-generated-agent-note-index)。目录树就是索引。 - 交叉引用必须用相对 markdown 链接,不能用散文或编号——这样它们可被机器检查,并且能在移动文件夹时存活。
- 归档是一次性的封存。封存后的三件套(英文 / 中文 / 一致性记录)永久冻结:不编辑、不翻译、不重排、不移动、不删除,也不得当作现状的权威。
verify-archived-agent-notes用一份只增不减的清单和 hash 把它钉死。
常规 ADR 的最大问题是会烂:写的时候很认真,半年后没人知道哪几篇还算数。dsh 的解法是给「还算不算数」一个物理位置——文件在哪个文件夹里,就是它的状态;状态变了,文件就得移动,而移动是 PR 里看得见的动作。
再配上「非平凡改动必须在同一个 PR 里加或更新一篇」这条硬规则,决策记录就从「有空再写的文档」变成了「改动的一部分」。
8.3 把工程约定写成 AI 可执行的形式
.agents/skills/ 下有 11 个 skill,全部是「什么时候该用我」开头的触发式描述:
| Skill | 它替你执行什么判断 |
|---|---|
dsh-pre-push-checks | 「这次 push 该跑哪些检查」——挑最小的、能覆盖这次 diff 的那一组,而不是反射式地跑全套 |
dsh-code-review | 把 reviewer 定位到本仓库的标准:AGENTS.md 约定、防御性模式、决策记录、质量门禁,以及那些「光看代码看不出来」的检查 |
dsh-archive-agent-notes | 新 note 有没有让某篇活跃记录过时?某篇 implemented 还有没有未来价值?——按价值判断,不按字数、年龄或配额 |
dsh-find-simplifications | 找死代码、重复、投机性抽象、过度构建、加了又删的表面、以及「本可以用现成依赖却手搓」的地方 |
dsh-prose-standard | 散文标准:哪里必须有文档、注释、诊断信息、CLI 文案 |
dsh-doc-standards | 层级与详略、教程与参考的分离、doc slop 的裁剪、verify-doc-budgets 失败时怎么办 |
dsh-trim-cot-leakage | 本站最喜欢的一个,下面单讲 |
dsh-merging-stacked-prs | 依赖 PR 栈的落地顺序与规则 |
dsh-doc-site-sync / dsh-translate-docs | 文档站投影同步 / 双语工作流(后者只允许用户显式调用) |
record-browser-gif | 录 GUI 演示 GIF——而且规定:每个改动了产品可见 GUI 行为的 PR 都必须附一个,从这个 PR 真实的服务端与模型流程里录 |
dsh-trim-cot-leakage:一个只有 AI 时代才会存在的 skill
它处理的问题是:模型写出来的散文,经常读起来像一段泄漏出来的思考记录。要清理的模式包括——
- 已经死掉的设计会话引用:「(decision N)」、审计条目编号、指向某份未提交草稿的「§N」
- 变更叙述:「以前是……」「不再……」「这次砍掉了……」
- 栈或 review 视角:「本栈里靠后的某个 PR」「在 review 中被否决」
- 对着 reviewer 说话的辩解
- 控制流的复述
- 规划残留物:那些还带着犹豫语气的半成品句子
这几类文字有个共同点:它们对写的那一刻的人有意义,对半年后读代码的人完全没有意义,而且会误导——「不再 X」会让读者以为 X 曾经是个重要概念,实际上它可能只在某个 PR 的中间版本里存在过两小时。
AGENTS.md 里对应的原则写得更狠:「注释和文档陈述完整的契约与上下文,不是推理记录。」
8.4 质量门禁矩阵
门禁按聚合分组,scripts/run-gates.ts 是它们的调度器。四个本地常用的聚合:
| 聚合 | 盖住什么 |
|---|---|
pnpm run doc-sync | 28 个叶子检查:文档构建、文档图、markdown 链接与换行、类型等价、五份生成式 catalog 的新鲜度、mermaid、scoped events、双语配对、导出 JSDoc、包路径、配置来源归属、包 README 的 Model Experience 与已知限制、Agent Note 的分类 / 格式 / 归档、skill 调用元数据、翻译提示词、文档字数预算、文档站投影 |
pnpm run hygiene | knip(无用导出与依赖)+ publint + workspace 约束 + NodeNext consumer 检查 |
pnpm run test:coverage | 逐文件 100%(statements / branches / functions / lines)on packages/*/*/src |
pnpm run duplication | jscpd 跨文件克隆检测 |
加上快照层:keyless 的 ACP / headless 回放、Chromium 浏览器快照(Linux PR 必过门禁,CI 强制只读 DSH_SNAPSHOT=replay,绝不写期望输出),以及两个 SDK 各自的期望输出。
同一份 testing.md 里有一节叫「with-key policy: inference is cheap here」,原话是:「我们是 DeepSeek——不要节省真 API 测试。」无 key 的测试只证明管道通,只有带 key 的运行才证明 agent 对着真模型是好使的。
这个组合值得琢磨:在「结构与完整性」上极严(门禁全自动),在「花钱验证真实行为」上极松(明确要求别省)。严和松都指向同一个目的——把有限的人类注意力,留给机器判断不了的东西。