Lesson 0004 · 深课 · 人和 AI 都能启动 · 词汇层

domain-modeling:把项目里的叫法统一起来

一个项目里,「一个工作单元到底叫 Issue 还是 ticket」这种问题总得有人管——管它的就是 domain-modeling。它负责把术语定下来、写进 CONTEXT.md, 并把那些难反悔的架构决定记成 ADR。你跑 grill-with-docs、triage、wayfinder 时都会间接用到它。 学完这节课,你能说清:什么才算真的在跑这个 skill(打开术语表看一眼不算)、 它的文件怎么摆、什么决定够格写 ADR、行为不对时去改哪个文件的哪一段。

1. 这个 skill 在整套系统里站在哪

假设你正和 AI 讨论需求,它一会儿说「工单」一会儿说「issue」——词到底该谁管?就归 domain-modeling 管。它是纪律层里一个 Model-invoked 的 skill。纪律层就是那些可复用的基本功: 一般不单独交付什么,而是被完整流程调用。Model-invoked 的意思是人和 AI 都能启动它—— 你可以亲手输 /domain-modeling;AI 碰到合适的场景也会自己加载它; 别的 user-invoked skill 还可以在正文里写一句自然语言 「Run the /domain-modeling skill」来用上它(这叫 prose 调用: 用一句话声明调用,不是代码层面的 import)。

维度 本 skill 对照
谁能启动 人和 AI 都行(它文件的 frontmatter——头部元数据——里没有 disable-model-invocation 字段) grill-with-docs / wayfinder / triage 只能由人手动启动(User-invoked)
在地图上的角色 词汇地板(ask-matt 叫它 vocabulary underneath):不在主流程上,但主流程的每一步都可能踩着它走 主流程本身是 grill → spec → tickets → implement 这一条链
负责哪些词 项目领域的统一语言(ubiquitous language:同一个概念,全项目只用同一个词),以及架构决策留下的书面记录 codebase-design 管「模块形状」的词(module、seam 接缝、depth 深度……)
会在仓库里留下什么 CONTEXT.md(多领域布局下每个领域各有一份)、docs/adr/ 下的决策记录 grilling 本身什么都不改;把讨论结果写进文件,靠的就是本 skill
为什么单独拆一个 skill 让领域语言有唯一一份权威来源(single source of truth):全系统只认这一份,任何 skill 都能用一句话引用它 不然每个编排 skill 都得把同一套术语规则抄一遍

地图原文:ask-matt/SKILL.md 的 Vocabulary underneath 一段 · 两种启动方式的契约:.agents/invocation.md · 对外文档:docs/engineering/domain-modeling.md

一句话记住它为什么存在 grill-me = 只有 grilling(没有代码库,什么文件也不写)。
grill-with-docs = grilling + domain-modeling(有代码库,要留下术语表和 ADR)。
这样拆开,没库的时候就不会被逼着写文件,有库的时候也不会只聊天、什么都留不下来。

2. 什么才算真的在跑它(而不是读了一眼术语表)

这是本 skill 最容易被误用的边界。 invocation.md 写得很直白:

Merely reading CONTEXT.md for vocabulary is a one-line prose pointer, not the domain-modeling skill. Only the active build/sharpen discipline (challenge terms, edge-case scenarios, write ADRs, update CONTEXT.md inline) is domain-modeling.

用大白话说:打开 CONTEXT.md 查个词,不算用了这个 skill——那只是「读」。 只有动手去建它、磨它才算:挑战用错的词、拿具体场景压测定义、写 ADR、当场改 CONTEXT.md。两者的区别全在下面这张表里:

只读不改(不是本 skill) 主动建模(才是本 skill)
做什么 探索代码前读一遍 CONTEXT.md 和 ADR,自己输出时照着官方词说 挑战和术语表冲突的词、把模糊的词磨准(sharpen)、拿具体场景压测定义、和代码对质、当场改术语表、条件够了就写 ADR
谁在做 几乎所有工程 skill(tdd、triage 的探索段、diagnosing-bugs、to-spec……)都有这个一行习惯 明确跑了 /domain-modeling,或者任务本身就是「在改这个模型」
仓库会不会变 通常不动 CONTEXT 和 ADR 术语一敲定就立刻写进 CONTEXT;碰到真正的取舍,可能写 ADR
文件不存在时 静默继续:当没这回事,接着干活,不催你建 懒创建:第一个敲定的术语才建 CONTEXT,第一篇真 ADR 才建 docs/adr/——没有内容就不建空文件
典型误读 「我打开了 CONTEXT,所以我用了 domain-modeling」——错 「我们把 backlog 这个说法拆成了 Issue tracker 和 Issue,并写进了 CONTEXT」——这才算

SKILL 正文开头说的是同一个意思:本 skill 用于 changing the model, not just consuming it (改模型,不只是消费模型)。如果你只是写 issue 标题时避开了 _Avoid_ 列的同义词—— 那是读者该遵守的规则,不算跑了一次本 skill。

调试时怎么想 「agent 输出里没用项目里的词」→ 先查读的那一侧:目标仓库的 docs/agents/domain.md 存不存在、有没有被读到;或者 CONTEXT 里根本还没定义这个词。
「grill 跑完了 CONTEXT 还是空的」→ 查入口 skill 是不是真的调用了 domain-modeling (比如误用了 grill-me,它明确不建 CONTEXT);或者整场会话只聊了实现, 一个术语也没敲定过。

3. 谁会启动它

它的 description(frontmatter 里写给 AI 看的触发语)说的是:要敲定术语、 要建立 ubiquitous language、要记录架构决策,或者另一个 skill 需要维护领域模型时。 编排层(你手动启动的那些完整流程)里,在正文用 prose 调用它的「主客户」有这些 (以各 SKILL.md 为准):

调用方 什么时候启动它 用它做什么
grill-with-docs 它整段正文就一行:跑 grilling,并用 domain-modeling 最大客户。有代码库的面试讨论,必须把术语和决定留下记录
triage 第 4 步「需要时面试」:grilling 和 domain-modeling 一起跑 外来 issue 内容太薄、需要追问时,顺便把词磨准;处理意见(brief)用项目里的词写
wayfinder 画地图时:给目的地(destination)定名;解决票时:有拿不准的就启动它 定下地图上 destination / decision 这些说法;默认的面试类票就是它俩搭档
improve-codebase-architecture 你选定一个「模块加深」(deepening:把模块改得接口更小、内部更厚)候选之后,在面试循环里用 给新模块起的名字补进 CONTEXT;候选被拒绝且理由值得留档时,可以写 ADR
你直接输 /domain-modeling 词本身就是问题,又不想绑定 grill / wayfinder 的完整流程时 对外文档强调:它可以当一份参考(reference),单独打开来用

不启动它、只读 CONTEXT 的例子: tdd 写测试名时对齐术语表、diagnosing-bugs 的探索阶段、 to-spec 用项目里的词写需求文档、improve-codebase-architecture 的探索和报告阶段(先读;等你选定方案后,才在面试循环里主动改模型)。

grill-with-docs     → /grilling + /domain-modeling
grill-me            → /grilling only(明确不建 CONTEXT)
triage (if needed)  → /grilling + /domain-modeling
wayfinder chart     → /grilling + /domain-modeling(定下 destination)
wayfinder work      → 默认 /grilling + /domain-modeling;Notes 可点名其它 skill
improve-arch pick   → /grilling;过程中用 /domain-modeling 维持模型
                      (接口形状另用 /codebase-design)

4. 文件怎么摆:一个术语表,还是按领域分多个

布局由 setup 的 Section C 写进 docs/agents/domain.md 定下来; 本 skill 运行时看磁盘上有什么文件来判断:

4.1 单领域布局(默认)

/
├── CONTEXT.md
├── docs/
│   └── adr/
│       ├── 0001-event-sourced-orders.md
│       └── 0002-postgres-for-write-model.md
└── src/

本 skills 仓库本身就是单领域布局:根目录的 CONTEXT.md 定义了 Issue tracker / Issue / Decision ticket / Triage role。 ADR 若存在,约定路径是 docs/adr/——注意本仓库自己的作者决策放在 .agents/adr/,那是这个仓库自身的工程习惯; skill 教给目标业务仓库的默认路径是 docs/adr/,两者别混。

4.2 多领域布局(根目录有 CONTEXT-MAP.md)

/
├── CONTEXT-MAP.md
├── docs/
│   └── adr/                          ← system-wide decisions
├── src/
│   ├── ordering/
│   │   ├── CONTEXT.md
│   │   └── docs/adr/                 ← context-specific decisions
│   └── billing/
│       ├── CONTEXT.md
│       └── docs/adr/

地图文件(CONTEXT-MAP.md)列出每个领域的路径和领域之间的关系;这场会话聊的是哪个领域, 就改哪个领域的术语表。拿不准就问用户。setup 只有在探测到 monorepo 信号时才会 提供多领域这个选项;默认仍是单领域。

懒创建是铁律 没有东西可写,就不要建空文件。 第一个敲定的术语 → 才建 CONTEXT;第一个真正的取舍 → 才建 docs/adr/。 读的那一侧(domain.md)也一样:文件不存在就静默继续,不建议「先建个空术语表」。

5. 会话里的动作清单

以下几条全部来自 SKILL.md 的 During the session 一节。 把这个 skill 用出作者水平,就是把它们当成一个循环反复做,不是 checklist 打勾走过场。

5.1 挑战和术语表冲突的词

用户用的词和现有 CONTEXT.md 冲突时,立刻点破,不要默默跟着新词走:

“Your glossary defines 'cancellation' as X, but you seem to mean Y — which is it?”

本仓库的对应例子:如果有人说「把 backlog 写进 triage」——CONTEXT 已经定过, 「backlog」不再是领域词,工具叫 Issue tracker,单元叫 Issue。 这时应该挑战,而不是在处理意见里继续写 backlog。

5.2 把模糊的词磨准

碰到含糊或一词多义的词,就提议一个精确的标准用词(canonical:以后全项目统一用它):

“You're saying 'account' — do you mean the Customer or the User? Those are different things.”

本仓库的历史:backlog 曾经同时指「托管 issue 的工具」和「一堆没做的事」—— 后来拆成了 Issue tracker 和日常口语(不算领域词),并把这段歧义记进了 CONTEXT 的 Flagged ambiguities 一节。

5.3 拿具体场景压测定义

讨论概念之间的关系时,用具体场景去压边界和边角情况(edge case)。 例:Decision ticket 和普通 Issue——「wayfinder 地图关掉以后,这张子 issue 还算决策票吗? implement 能不能直接把它当 tracer bullet(端到端打通的可执行小切片)来做?」 这样一问就逼出了结论:决策票的产物是「一个决定」,不是可执行的切片; 雾散了(看不清的东西都问清楚了)之后,要把决策交给 to-spec 汇总成正式需求, 而不是对着决策票直接 implement。

5.4 和代码对质

用户说「系统是这样工作的」→ 去对代码。说法和代码矛盾时,必须把矛盾摆上台面:

“Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?”

这一步是这个 skill 和「只画图不对代码」的纸面领域设计(DDD)的分水岭: 语言和代码必须对齐,否则术语表会变成谎言。

5.5 当场更新 CONTEXT.md

术语一敲定就写,禁止攒着一批最后一起写。格式见下一节。 硬约束:

CONTEXT 只放术语表 禁止实现细节、禁止当需求文档、禁止当草稿纸、禁止塞实现决策。 实现决策如果值得记,去写 ADR(而且要过三个条件),不是塞进 CONTEXT 的词条里。

5.6 有节制地提议写 ADR

只有三个条件同时成立才提议写 ADR(见第 7 节)。缺任何一条,就跳过。

6. CONTEXT.md 的格式与本仓库的实例

格式的权威原文在 CONTEXT-FORMAT.md

6.1 骨架

# {Context Name}

{一两句:这个 context 是什么、为何存在}

## Language

**Order**:
{一两句:它是什么}
_Avoid_: Purchase, transaction

**Invoice**:
A request for payment sent to a customer after delivery.
_Avoid_: Bill, payment request

6.2 四条格式规则

规则 含义 反例
Be opinionated(敢于选定) 几个词同义时,选定一个当标准词,其余写进 _Avoid_ Client / Buyer / Customer 三个都留着,不选定
Keep definitions tight(定义要短) 最多一两句;定义它是什么,不写怎么实现 「Customer 通过 REST 接口创建,存在 users 表……」
Only project-specific terms(只收项目特有的词) 通用编程概念不进术语表 把 Timeout、Error、Repository pattern 当领域词
Group when natural(成簇才分组) 有自然的一组就用小标题分组;单个领域可以平铺不分组 为了显得整齐,硬分出十个空小节

6.3 本仓库的 CONTEXT.md 是怎么示范这些规则的

打开 CONTEXT.md 对照着看:

注意它没有写:用 gh 还是 .scratch、label 字符串表、plugin.json 路径—— 那些是配置和实现,归 docs/agents/* 或代码管,不进术语表。

6.4 多领域地图长什么样(格式文件的原文结构)

# Context Map

## Contexts

- [Ordering](./src/ordering/CONTEXT.md) — receives and tracks customer orders
- [Billing](./src/billing/CONTEXT.md) — generates invoices and processes payments
- [Fulfillment](./src/fulfillment/CONTEXT.md) — manages warehouse picking and shipping

## Relationships

- **Ordering → Fulfillment**: Ordering emits OrderPlaced; Fulfillment starts picking
- **Fulfillment → Billing**: ShipmentDispatched → invoices
- **Ordering ↔ Billing**: Shared types for CustomerId and Money

7. ADR:什么决定够格记、记成什么样

ADR(architecture decision record,架构决策记录)就是给「为什么这么干」留一份书面记录。 权威原文在 ADR-FORMAT.md。 路径是 docs/adr/0001-slug.md 这样递增编号;目录同样懒创建。

7.1 三个条件(要同时满足,不是满足其一)

# 条件 缺了会怎样
1 Hard to reverse——难逆转,改主意的成本高 容易改的就别记,反正很快就变了
2 Surprising without context——不了解背景的人看到会问「为什么这样」 不让人惊讶就没人困惑,记了也是日记
3 Real trade-off——有真实的备选方案,并且因为特定理由选了这一个 没有备选,只是做了件显然的事,没什么可记

这三个条件是防止 docs/adr/ 变成日记本的阀门。skill 和对外文档都强调:sparingly(有节制地写)。

7.2 什么决定算合格(格式文件的清单,压缩版)

7.3 模板:刻意做得极薄

# {Short title of the decision}

{1-3 sentences: context, decision, why.}

价值在于记下「做了这个决定」和「为什么」,不在于把章节填满。 可选的只有 Status frontmatter、Considered Options、Consequences——真有增益时才加。

7.4 improve-codebase-architecture 里的特殊规则

用户拒绝某个加深候选时:只有当拒绝理由对后来的探索者有「承重」作用 (load-bearing——不知道这条理由,后人会再提一次同样的方案)才提议写 ADR; 「现在没时间」这种短期理由、和不言自明的理由,都跳过。 这和三条件是同一条原则,是编排层对「有节制地提议 ADR」在具体场景里的落实。

7.5 说实话:本仓库自己的 ADR 例子

本仓库有 .agents/adr/0002 这类决策:Claude 插件现在发、Codex 原生插件推迟——难逆转、让人惊讶、有真实取舍,三条全占。 它展示了「好 ADR 长什么样」,但它的路径是 .agents/adr/,不是 skill 默认教给 使用这套 skill 的业务仓库docs/adr/。教别人用这套 skill 时, 目标仓库仍按 CONTEXT-FORMAT / ADR-FORMAT / domain.md 规定的布局来。

8. 读的那一侧:docs/agents/domain.md

setup 写进目标仓库的 domain.md 模板, 规定的是其它所有 skill 该怎么读领域文档——它和 domain-modeling 的「怎么写」 正好配成一对:

  1. 探索前先读 CONTEXT(多领域布局先读地图)和相关的 ADR;多领域时还要查当前领域自己的 adr 目录。
  2. 文件不存在 → 静默继续,不催你建;创建权只留给 domain-modeling 的懒创建。
  3. 输出里给领域概念命名时,用术语表里的词,不滑到 _Avoid_ 列的同义词上。
  4. 要用的概念不在术语表里:要么你在发明一个项目根本不用的词(重新想想),要么真是缺口(记下来,交给 domain-modeling)。
  5. 你的输出和某篇 ADR 冲突 → 明确说出来,禁止悄悄覆盖。
写和读,别搞混 写模型 → domain-modeling(主动,会改文件)。
读模型 → 任何 skill 的一行习惯 + domain.md 的规定(被动,通常不改文件)。
把这两者搞混,是最常见的误用,也是最要命的。

9. 会留下什么、结束后接什么、与 codebase-design 的分工

9.1 会留下什么(副作用)

「副作用」就是:这个 skill 跑完,你的仓库里会多出或改动哪些文件。

产物 什么时候写 不写什么
CONTEXT.md,或某领域的 CONTEXT 文件 术语敲定时当场写 实现细节、API、库名、ticket 正文
CONTEXT-MAP.md 需要多领域布局时(通常 setup 已经定好布局) 不替代各领域术语表的正文
docs/adr/NNNN-*.md 三个条件全满足,且用户/会话接受了提议 易逆的、不惊人的、没有取舍的随手笔记
业务代码 / 工单系统里的 issue 本 skill 不直接写

9.2 结束后通常接什么

本 skill 一般嵌在别的流程里跑,结束后回到调用方的下一步,不另起一条链:

下游都会受益:to-spec 用项目里的词写需求;to-tickets / implement / tdd 的测试名和术语表对齐;任何探索类 skill 都少受「同一个东西好几个叫法」的干扰。

9.3 和 codebase-design 怎么分工

domain-modeling codebase-design
管哪类问题 「词」的问题:cancellation / account / Issue 到底指什么 「形」的问题:seam(接缝,方便插测试、换实现的边界)划在哪、模块深不深
留下什么 CONTEXT,外加(很少的)ADR 设计讨论的词汇,和 design-it-twice 模式
怎么配合 给接缝起名用的材料来自这里 用「深模块」(deep module:接口小、内部厚)那套词设计这条接缝
在 improve-arch 里 模块被加深后,把新名字补进术语表 探索阶段和接口方案用的架构词

对外文档里的分界:docs 原文—— 词的问题找 domain-modeling;模块形状的问题找 codebase-design; 计划本身要被盘问,找 grilling。

10. 行为不对时,去改哪个文件

这个 skill 行为不对时,先判断问题出在哪一层,再去改对应文件的那一段 (也就是 Mission 里说的「定位该改哪段文本」):

症状 优先去改 别急着动
该挑战冲突词时沉默了 / 该写 CONTEXT 却一直攒着不写 skills/engineering/domain-modeling/SKILL.md 的会话纪律段 grill-with-docs 那个只有一行的入口文件(薄 wrapper:本身没逻辑,只负责调用别的 skill)
词条形态乱、塞了实现细节、缺 _Avoid_ 列 CONTEXT-FORMAT.md ask-matt 地图上的描述文字
ADR 写成了日记 / 模板太重 ADR-FORMAT.md(三条件 + 极薄模板) 让每个调用方 skill 各抄一份 ADR 规则
探索时不读术语表、同义词乱用 目标仓库的 docs/agents/domain.md(读侧规则) domain-modeling 的正文(那是写侧,管不着读)
单/多领域布局和仓库对不上 setup 的 Section C 结果;或直接手改 domain.md 里的布局说明 把 SKILL 里的示意目录树当成唯一真理硬改
AI 从不自己启动这个 skill frontmatter 里 description 的触发语(那是写给模型看的) 误加 disable-model-invocation——加了就变成只能人启动
对外文档的说法和 skill 实际行为对不上 docs/engineering/domain-modeling.md(重新同步) 只改文档不改 SKILL——行为以 SKILL 为准
路由器(ask-matt)还把人指到错误的 skill ask-matt/SKILL.md 的 Vocabulary underneath 段 本 skill 内部的格式文件
三类文件各管一摊

11. 检索练习

先别往回翻;选项长度刻意对齐,不会从字数泄题。答完再回对应的节。

自测(立即反馈)

1. 下列哪项在跑 domain-modeling,而不是被动读 glossary?
2. CONTEXT.md 被允许放什么?
3. ADR 三条件缺了「Surprising」时正确做法是?
4. multi-context 仓库的磁盘信号是?
5. grill-with-docs 与 grill-me 在领域文档上的关键分叉是?
6. 术语刚在对话里决议,正确时机是?
7. 消费侧发现 CONTEXT 不存在时,domain.md 要求?
8. 「agent 输出里同义词漂移」优先拧哪?
额外提取(无选项) 合上本页,默写:(1) 主动建模和只读各举两例;(2) ADR 三条件的关键词(原文级别); (3) 四个会用 prose 调用启动 domain-modeling 的 user-invoked skill 名。 写完对照第 2、7、3 节。

12. 一手材料与下一课

必读原文(本课的压缩替代不了):

建议下一课(0005): codebase-design——另一块词汇地板:module / interface / depth / seam / adapter / leverage / locality。 两课的分工:本课定领域词,下节课定模块形状词; improve-codebase-architecture 和 tdd 两套词都要用。

老师就在会话里。 对本课任何一句断言有疑问——比如某个 skill 是不是真的会调用 domain-modeling、 某个决定过没过 ADR 三条件——直接问。回答会回到对应的 SKILL.md 或格式文件原文,不临场编规则。 做完练习后回复:「练习分数 / 卡在哪 / 开 0005」。