DSH / Atlas
2026-06-20implementedprocess

Classify Agent Notes by kind via path-encoded subdirectories

通过路径编码的子目录对 Agent Note 进行分类

A lifecycle-only Agent Note tree — `proposed/` / `implemented/` / `rejected/` — does not record what *kind* of decision each file contains. A reader browsing one lifecycle cannot distinguish a new capability from a removal or a tooling-policy change without opening each file. The repo's standing bias is [mechanical quality gates over prose guidelines](2026-06-11-quality-gates.md): a convention that isn't machine-chec

English

Problem

A lifecycle-only Agent Note tree — proposed/ / implemented/ / rejected/ — does not record what kind of decision each file contains. A reader browsing one lifecycle cannot distinguish a new capability from a removal or a tooling-policy change without opening each file.

The repo's standing bias is mechanical quality gates over prose guidelines: a convention that isn't machine-checked rots. So a classification scheme here had to be enforceable, not an honor-system header.

Decision

Add a second axis — the Agent Note's class — and encode it in the path: {lifecycle}/{class}/yyyy-mm-dd-topic.md. The folder is the label. A file's location declares its class, the closed set is "these folders and no others," and the existing verify-md-links gate already protects the path rewrites the move required.

The closed set of six classes

ClassCovers
featureA new user- or model-facing capability.
bug-fixCorrects a defect or closes a gap a postmortem surfaced.
simplificationRemoves code, behavior, or surface area without adding a capability.
architectureA structural decision about the shipped source — how packages relate, what the runtime vocabulary is.
processTooling, policy, or workflow around the code, not runtime behavior.
testingTest infrastructure and strategy.

The architecture / process line: architecture is about the source we ship; process is the surrounding tooling and workflow. This Agent Note is itself a process decision — it changes how the repo is organized and gated, not what the harness does at runtime — so it lives under implemented/process/.

Two gates

Both are doc-sync members, in the verify-md-wrap style (tsx ESM, verify-don't-generate, exit non-zero on the first violation):

  • scripts/verify-agent-note-classification.ts — the closed lifecycle and class sets. It asserts every file under a lifecycle folder lives in a class folder from the canonical set (a loose .md at a lifecycle root, or an unknown class folder, fails) and rejects a centralized INDEX.md. The canonical sets live in scripts/agent-note-tree.ts, and the README documents each class in prose.
  • scripts/verify-doc-refs.ts — source comments that cite docs. Agent Note paths are referenced not only from Markdown but from TypeScript doc comments (root-relative prose like .agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md). verify-md-links does not see those, so a reorganization could silently orphan them. This gate scans repo-authored .ts under packages/** and examples/** (excluding built lib/ and vendor/) for docs/….md and .agents/notes/….md tokens, resolves each root-relative path, and asserts it exists. It requires the .md extension so extensionless prose is left alone.

Alternatives considered

  • A Classification: prose line in each file (next to Status:), parsed by the gate. Workable, but it duplicates into the file a fact the path can already carry, and a line can disagree with its folder. Path-encoding makes the label and its storage the same thing — there is nothing to keep in sync.
  • A refactor class. It overlaps simplification almost entirely; the only discriminator anyone reached for was "does observable behavior change?", which simplification already encodes (it does not). One class, not two.
  • A generated or hand-maintained corpus index. Rejected because the lifecycle/class tree is authoritative, while a centralized inventory creates a merge hotspot without providing discovery that tree navigation or repository search cannot provide.

Consequences

  • Every Agent Note sits under a class folder. A reader can browse one folder to see all simplifications or all testing decisions within a lifecycle.
  • Two more fast tsx scripts in the doc-sync chain; no new dependency (the mdast/GFM stack was already present for verify-md-wrap/verify-md-links).
  • Adding a class is a deliberate act: amend the const in scripts/agent-note-tree.ts and the Classification section, not just mkdir a folder. The gate rejects an unknown folder, so an ad-hoc class can't slip in.
  • Source-comment doc references are gated too — a moved or renamed doc that a .ts comment cites fails verify-doc-refs in doc-sync and CI, closing a drift class verify-md-links structurally could not see.

中文

问题

仅按生命周期组织的 Agent Note 目录树(proposed/ / implemented/ / rejected/)无法记录每个文件包含哪一决策。读者浏览某个生命周期时,如果不逐一打开文件,就无法区分新功能、移除项或工具策略变更。

本仓库一贯的倾向是机械质量门禁优于行文规范:不被机器检查的约定终将腐烂。因此这里的分类方案必须可强制执行,而非靠自觉的文件头。

决策

增加第二个维度,即 Agent Note 的类别,并将其编码在路径中:{lifecycle}/{class}/yyyy-mm-dd-topic.md。文件夹就是标签。文件位置声明其类别;封闭集合限定为「这些文件夹且仅限这些」;既有的 verify-md-links 门禁已经保护移动文件所需的路径改写。

六个类别的封闭集合

类别涵盖范围
feature面向用户或模型的新功能。
bug-fix修正缺陷或填补事故复盘(postmortem)暴露的空白。
simplification移除代码、行为或对外接口范围,不引入新功能。
architecture关于交付源码的结构性决策——包之间的关系、运行时词汇。
process围绕代码的工具、策略或工作流,而非运行时行为。
testing测试基础设施与策略。

architectureprocess 的分界是:architecture 关乎我们交付的源码;process 关乎源码周边的工具与工作流。本 Agent Note 本身属于 process 决策:它改变仓库的组织方式与门禁,而不是 harness 的运行时行为,因此位于 implemented/process/ 下。

两道门禁

两者都是 doc-sync(文档同步门禁)的成员,风格与 verify-md-wrap 一致(tsx ESM,只校验不生成,首个违规即以非零退出码退出):

  • scripts/verify-agent-note-classification.ts:定义封闭的生命周期与类别集合。它断言生命周期文件夹下的每个文件都位于规范集合中的类别文件夹内(生命周期根目录下散落的 .md 或未知类别文件夹都会失败),并拒绝集中式 INDEX.md。规范集合位于 scripts/agent-note-tree.ts 中,README 则以行文记录每个类别。
  • scripts/verify-doc-refs.ts:检查引用文档的源码注释。Agent Note 路径不仅出现在 Markdown 中,也出现在 TypeScript 文档注释中(例如以仓库根为起点的 .agents/notes/implemented/testing/2026-06-19-acp-snapshot-tests.md)。verify-md-links 看不到这些引用,因此目录重组可能静默留下失效引用。该门禁扫描 packages/**examples/** 下仓库自有的 .ts 文件(排除已构建的 lib/vendor/),查找 docs/….md.agents/notes/….md token,解析每个以仓库根为起点的路径并断言其存在。它要求使用 .md 扩展名,因此会忽略行文中不带扩展名的引用。

曾考虑的替代方案

  • 在每个文件中添加 Classification: 文本行(紧邻 Status:),由门禁解析。可行,但它将路径已能承载的事实重复到文件中,且行内容可能与所在文件夹不一致。路径编码使标签与其存储合二为一,没有需要保持同步的东西。
  • 设立 refactor 类别。simplification 几乎完全重叠;唯一有人试图用来区分的标准是「可观察行为是否改变?」,而 simplification 已经编码了这一点(它不改变)。一个类别即可,无需两个。
  • 生成或手工维护的文档集索引。 不予采纳:生命周期/类别目录树才是权威结构;集中式清单会制造合并热点,却没有提供目录树导航或仓库搜索无法实现的发现能力。

后果

  • 每份 Agent Note 都位于一个类别文件夹下。读者浏览单个文件夹,即可查看某个生命周期内的全部简化或测试决策。
  • doc-sync 链中多了两个快速 tsx 脚本;无新依赖(mdast/GFM 栈已因 verify-md-wrap/verify-md-links 而存在)。
  • 新增类别必须是显式决策:修改 scripts/agent-note-tree.ts 中的 constClassification 章节,而不是只用 mkdir 创建文件夹。门禁会拒绝未知文件夹,因此临时类别无法悄然混入。
  • 源码注释中的文档引用同样受门禁约束:被 .ts 注释引用的文档一旦移动或重命名,doc-sync 与 CI 中的 verify-doc-refs 就会失败,从而堵住 verify-md-links 在结构上无法发现的一类漂移。