domain-modeling:把项目里的叫法统一起来
一个项目里,「一个工作单元到底叫 Issue 还是 ticket」这种问题总得有人管——管它的就是
domain-modeling。它负责把术语定下来、写进 CONTEXT.md,
并把那些难反悔的架构决定记成 ADR。你跑 grill-with-docs、triage、wayfinder 时都会间接用到它。
学完这节课,你能说清:什么才算真的在跑这个 skill(打开术语表看一眼不算)、
它的文件怎么摆、什么决定够格写 ADR、行为不对时去改哪个文件的哪一段。
假设你正和 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)。这是本 skill 最容易被误用的边界。 invocation.md 写得很直白:
Merely readingCONTEXT.mdfor vocabulary is a one-line prose pointer, not thedomain-modelingskill. Only the active build/sharpen discipline (challenge terms, edge-case scenarios, write ADRs, updateCONTEXT.mdinline) isdomain-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。
docs/agents/domain.md
存不存在、有没有被读到;或者 CONTEXT 里根本还没定义这个词。grill-me,它明确不建 CONTEXT);或者整场会话只聊了实现,
一个术语也没敲定过。
它的 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)
布局由 setup 的 Section C 写进 docs/agents/domain.md 定下来;
本 skill 运行时看磁盘上有什么文件来判断:
CONTEXT-MAP.md → 多领域布局(multi-context):先读这张地图,再去找各个领域的术语表CONTEXT.md → 单领域布局(single-context),绝大多数仓库是这种CONTEXT.md(懒创建)/
├── 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/,两者别混。
/
├── 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 信号时才会 提供多领域这个选项;默认仍是单领域。
docs/adr/。
读的那一侧(domain.md)也一样:文件不存在就静默继续,不建议「先建个空术语表」。
以下几条全部来自 SKILL.md 的 During the session 一节。 把这个 skill 用出作者水平,就是把它们当成一个循环反复做,不是 checklist 打勾走过场。
用户用的词和现有 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。
碰到含糊或一词多义的词,就提议一个精确的标准用词(canonical:以后全项目统一用它):
“You're saying 'account' — do you mean the Customer or the User? Those are different things.”
本仓库的历史:backlog 曾经同时指「托管 issue 的工具」和「一堆没做的事」——
后来拆成了 Issue tracker 和日常口语(不算领域词),并把这段歧义记进了
CONTEXT 的 Flagged ambiguities 一节。
讨论概念之间的关系时,用具体场景去压边界和边角情况(edge case)。
例:Decision ticket 和普通 Issue——「wayfinder 地图关掉以后,这张子 issue 还算决策票吗?
implement 能不能直接把它当 tracer bullet(端到端打通的可执行小切片)来做?」
这样一问就逼出了结论:决策票的产物是「一个决定」,不是可执行的切片;
雾散了(看不清的东西都问清楚了)之后,要把决策交给 to-spec 汇总成正式需求,
而不是对着决策票直接 implement。
用户说「系统是这样工作的」→ 去对代码。说法和代码矛盾时,必须把矛盾摆上台面:
“Your code cancels entire Orders, but you just said partial cancellation is possible — which is right?”
这一步是这个 skill 和「只画图不对代码」的纸面领域设计(DDD)的分水岭: 语言和代码必须对齐,否则术语表会变成谎言。
术语一敲定就写,禁止攒着一批最后一起写。格式见下一节。 硬约束:
只有三个条件同时成立才提议写 ADR(见第 7 节)。缺任何一条,就跳过。
格式的权威原文在 CONTEXT-FORMAT.md。
# {Context Name}
{一两句:这个 context 是什么、为何存在}
## Language
**Order**:
{一两句:它是什么}
_Avoid_: Purchase, transaction
**Invoice**:
A request for payment sent to a customer after delivery.
_Avoid_: Bill, payment request
| 规则 | 含义 | 反例 |
|---|---|---|
| Be opinionated(敢于选定) | 几个词同义时,选定一个当标准词,其余写进 _Avoid_ |
Client / Buyer / Customer 三个都留着,不选定 |
| Keep definitions tight(定义要短) | 最多一两句;定义它是什么,不写怎么实现 | 「Customer 通过 REST 接口创建,存在 users 表……」 |
| Only project-specific terms(只收项目特有的词) | 通用编程概念不进术语表 | 把 Timeout、Error、Repository pattern 当领域词 |
| Group when natural(成簇才分组) | 有自然的一组就用小标题分组;单个领域可以平铺不分组 | 为了显得整齐,硬分出十个空小节 |
打开 CONTEXT.md 对照着看:
docs/agents/triage-labels.md 映射。
注意它没有写:用 gh 还是 .scratch、label 字符串表、plugin.json 路径——
那些是配置和实现,归 docs/agents/* 或代码管,不进术语表。
# 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
ADR(architecture decision record,架构决策记录)就是给「为什么这么干」留一份书面记录。
权威原文在
ADR-FORMAT.md。
路径是 docs/adr/0001-slug.md 这样递增编号;目录同样懒创建。
| # | 条件 | 缺了会怎样 |
|---|---|---|
| 1 | Hard to reverse——难逆转,改主意的成本高 | 容易改的就别记,反正很快就变了 |
| 2 | Surprising without context——不了解背景的人看到会问「为什么这样」 | 不让人惊讶就没人困惑,记了也是日记 |
| 3 | Real trade-off——有真实的备选方案,并且因为特定理由选了这一个 | 没有备选,只是做了件显然的事,没什么可记 |
这三个条件是防止 docs/adr/ 变成日记本的阀门。skill 和对外文档都强调:sparingly(有节制地写)。
# {Short title of the decision}
{1-3 sentences: context, decision, why.}
价值在于记下「做了这个决定」和「为什么」,不在于把章节填满。 可选的只有 Status frontmatter、Considered Options、Consequences——真有增益时才加。
用户拒绝某个加深候选时:只有当拒绝理由对后来的探索者有「承重」作用 (load-bearing——不知道这条理由,后人会再提一次同样的方案)才提议写 ADR; 「现在没时间」这种短期理由、和不言自明的理由,都跳过。 这和三条件是同一条原则,是编排层对「有节制地提议 ADR」在具体场景里的落实。
本仓库有
.agents/adr/0002
这类决策:Claude 插件现在发、Codex 原生插件推迟——难逆转、让人惊讶、有真实取舍,三条全占。
它展示了「好 ADR 长什么样」,但它的路径是 .agents/adr/,不是 skill 默认教给
使用这套 skill 的业务仓库的 docs/adr/。教别人用这套 skill 时,
目标仓库仍按 CONTEXT-FORMAT / ADR-FORMAT / domain.md 规定的布局来。
setup 写进目标仓库的 domain.md 模板, 规定的是其它所有 skill 该怎么读领域文档——它和 domain-modeling 的「怎么写」 正好配成一对:
_Avoid_ 列的同义词上。「副作用」就是:这个 skill 跑完,你的仓库里会多出或改动哪些文件。
| 产物 | 什么时候写 | 不写什么 |
|---|---|---|
根 CONTEXT.md,或某领域的 CONTEXT 文件 |
术语敲定时当场写 | 实现细节、API、库名、ticket 正文 |
CONTEXT-MAP.md |
需要多领域布局时(通常 setup 已经定好布局) | 不替代各领域术语表的正文 |
docs/adr/NNNN-*.md |
三个条件全满足,且用户/会话接受了提议 | 易逆的、不惊人的、没有取舍的随手笔记 |
| 业务代码 / 工单系统里的 issue | 本 skill 不直接写 | — |
本 skill 一般嵌在别的流程里跑,结束后回到调用方的下一步,不另起一条链:
grill-with-docs 进来 → 回主流程:说不清的点去 prototype 岔路 / 清楚了去 to-spec / 小改直接 implementtriage 的面试进来 → 继续出处理结果(brief / needs-info / wontfix……)wayfinder 进来 → 继续画地图或解决地图上的票;雾散了去 to-specimprove-codebase-architecture 进来 → 选定的想法再进主流程的 grill-with-docs,或走 design-it-twice(同一个接口设计两版再挑,codebase-design 的模式)
下游都会受益:to-spec 用项目里的词写需求;to-tickets / implement / tdd
的测试名和术语表对齐;任何探索类 skill 都少受「同一个东西好几个叫法」的干扰。
| domain-modeling | codebase-design | |
|---|---|---|
| 管哪类问题 | 「词」的问题:cancellation / account / Issue 到底指什么 | 「形」的问题:seam(接缝,方便插测试、换实现的边界)划在哪、模块深不深 |
| 留下什么 | CONTEXT,外加(很少的)ADR | 设计讨论的词汇,和 design-it-twice 模式 |
| 怎么配合 | 给接缝起名用的材料来自这里 | 用「深模块」(deep module:接口小、内部厚)那套词设计这条接缝 |
| 在 improve-arch 里 | 模块被加深后,把新名字补进术语表 | 探索阶段和接口方案用的架构词 |
对外文档里的分界:docs 原文—— 词的问题找 domain-modeling;模块形状的问题找 codebase-design; 计划本身要被盘问,找 grilling。
这个 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 内部的格式文件 |
先别往回翻;选项长度刻意对齐,不会从字数泄题。答完再回对应的节。
必读原文(本课的压缩替代不了):
skills/engineering/domain-modeling/SKILL.md
—— 主动/被动的定义、文件布局、会话动作清单、ADR 门槛
CONTEXT-FORMAT.md
—— 术语表的结构,以及多领域地图的写法
ADR-FORMAT.md
—— 极薄模板,以及什么决定算合格
setup-…/domain.md
—— 读侧契约的模板
CONTEXT.md(本仓库)——
Issue tracker / Decision ticket 等活样板
.agents/invocation.md
—— Passive vs active domain work(被动读 vs 主动建模)
ask-matt/SKILL.md
—— Vocabulary underneath 一段
docs/engineering/domain-modeling.md
—— 对外叙述(用来和 SKILL 对齐检查)
建议下一课(0005):
codebase-design——另一块词汇地板:module / interface / depth / seam / adapter / leverage / locality。
两课的分工:本课定领域词,下节课定模块形状词;
improve-codebase-architecture 和 tdd 两套词都要用。
SKILL.md 或格式文件原文,不临场编规则。
做完练习后回复:「练习分数 / 卡在哪 / 开 0005」。