
快记快用
把已经谈妥的对话合成一份书面 Spec(产品需求文档),并发布到 issue tracker。不再提问、不再做新决策,只记录你已做的选择(含测试 seams)。适合跨多会话的工作;单会话可直接 /implement。在 grill 之后输入 /to-spec 即可。
使用 to-spec 能解决什么问题?
你通过 grilling 阶段谈定的东西(方案的形状、权衡过的选项、刻意拒绝的做法)都装在一段对话里,而只要会话窗口结束,这些决策就会消失。spec 存在的理由就是让这些决策能够从上下文窗口中提取并永久保留下来。
没有它时会出现的后果:
- 下一个 session 需要你重新解释一遍方案形状和取舍。
- 刻意拒绝的做法没有记录,实现时可能被当成没回答的问题补回来。
- 跨 session 的构建缺少决策记录,模型会在没有约束的情况下自行发挥。
使用方法和运行机制
输入
输入由三部分组成,另有两个变体入口。
当前对话上下文 + 代码库理解 + 代码库本身tracker 与 triage 标签词汇由 /setup-matt-pocock-skills 事先提供- wayfinder 地图清空后,调用
/to-spec #<map_issue>,也就是主 map issue,不是单条决策票。 - 更上游的 research 与
to-questionnaire产物先进入 grilling,不直接进入 spec。
处理
探索代码库,读取领域词汇与 ADR;画出测试接缝,已有接缝优先、取最高、理想一个,并与用户确认;按七段模板写 spec;发布到 tracker。
输出
tracker 上的一个 issue,也就是决策记录,带 ready-for-agent 标签。这个技能不产生文件之外的动作,没有“审核通过”或“开始实施”这类后续,发布本身就是动作的终点。spec 放在哪里,决定了下一个 session 怎么找到它。
spec 的生命周期要从两个部分看。你与 AI 的对话会结束,spec 就是对话的产物;spec 自己也会过期,产品要进行更好的迭代时,它作为快照就失效了。
什么是测试缝(seam)?
在 Matt Pocock 的技能流里,seam(测试缝) 指的是:你观察行为的「公开边界」——在这个边界上测外部可见行为,而不深入内部实现。
用售货机来理解
想象你在测一台自动售货机:
- 从外面测:投币 → 按按钮 → 看有没有掉出饮料。你只关心「投了钱、按了键,有没有货出来」,机器里面怎么选货、怎么找零,你不管。
- 拆开机器测:测内部某个齿轮转了几次、某个继电器有没有通电。一换零件、一改结构,这些“内部检查”就全废了。
Seam 就是第一种做法:从外面、从公开入口测行为的那个观察点。
对应到代码
| 场景 | 是不是好的 seam |
|---|---|
调用 创建订单(),断言「订单状态变成已创建」 | ✅ 测的是对外行为 |
断言内部 支付服务.process() 被调用了 1 次 | ❌ 测的是实现细节 |
调 API POST /checkout,看返回 200 和订单号 | ✅ 从系统边界测 |
测某个私有函数 _计算折扣() 的中间结果 | ❌ 深入内部了 |
Seam = 你约定好的「从哪扇门往里看」——只通过这扇门看结果,不拆墙进去查零件。
为什么 to-spec 要先定它?
Agent 很爱「到处写小测试」:每个函数、每个内部模块都测一遍。那样测试会:
- 和实现绑死 → 一重构就挂
- 测了很多局部,却保证不了「整条链路通」
所以流程是:
- to-spec:先和你商量——这次功能主要从哪几个公开入口测?(最好就 1 个)
- tdd / implement:只允许在这些入口上写测试
- code-review:检查有没有偷偷在别处乱加测试
原则大致是:优先用已有 seam,少开新的;尽量选「最高」的那一层(越靠外越好);理想情况一次改动只有一个 seam。
一个极简例子
需求:用户可以「用购物车结账」。
约定一个 seam:
从
checkout(购物车)这个公开函数测:
传入有效购物车 → 返回成功,且能查出对应订单。
后面写代码时,只围绕这个入口写失败测试 → 写最少代码让它通过。内部怎么拆模块、怎么调支付,随便改,只要这个入口的行为还在就行。
概念总结
to-spec 把当前对话里已经定好的内容合成为一份 spec(产品需求文档),作为单个 issue 发布到项目的 issue tracker。它不重新采访你,只整理已知内容,素材来自对话、代码库、CONTEXT.md 与 ADR。它在主构建链 grill-with-docs → to-spec → to-tickets → implement → code-review 上只走多 session 分支。
源码解读
文件头声明技能名、一句话职责与调用方式
---name: to-specdescription: "Turn the current conversation into a spec and publish it to the project issue tracker: no interview, just synthesis of what you've already discussed."disable-model-invocation: true---译文
技能名是 to-spec。介绍:把当前对话变成一份 spec,发布到项目的 issue tracker:不采访,只合成你们已经讨论过的东西。disable-model-invocation: true 表示只能由人调用。
开篇总纲禁止采访用户,只做合成
This skill takes the current conversation context and codebase understanding and produces a spec. Do NOT interview the user; just synthesize what you already know.
译文
本技能把当前对话上下文和对代码库的理解变成一份 spec。不要采访用户,只合成你已经知道的东西。
前置条件依赖 tracker 与 triage 标签词汇
The issue tracker and triage label vocabulary should have been provided to you. If not, tell the user to run
/setup-matt-pocock-skills.
译文
issue tracker 和 triage 标签词汇应该已经提供给你了。如果没有,告诉用户去运行 /setup-matt-pocock-skills。
说明
技能不自己发现 tracker,而是假设配置已注入,缺了就让用户跑配置技能,要调用别的技能就直呼其名。这句话在 skills/engineering/to-tickets/SKILL.md 里一字不差地出现,同一套前置条件被两个技能共用。
docs/engineering/to-spec.md 补充了 tracker 的两种形态:真实的 tracker 如 GitHub,或者 .scratch/ 下的本地 markdown 文件,后者开箱即用。vocabulary 一词说明 triage 标签在技能眼里是一套词汇,与 CONTEXT.md 里 Triage role 的定义呼应,每个角色映射到 tracker 的真实标签串,例如 ready-for-agent。
第一步先探索代码库,沿用领域词汇并尊重 ADR
- Explore the repo to understand the current state of the codebase, if you haven’t already. Use the project’s domain glossary vocabulary throughout the spec, and respect any ADRs in the area you’re touching.
译文
- 探索仓库,理解代码库的当前状态(如果还没做过)。整个 spec 都用项目的领域词汇表词汇,并且尊重你碰到的区域里的任何 ADR。
说明
这条要求分成用词与 ADR 两半。
-
用词方面,spec 全文使用项目的领域词汇,上游词汇表在仓库根
CONTEXT.md,例如 Issue tracker、Decision ticket;docs 的 It’s working if 里写着它“用你的名词回来,而不是通用产品管理样板”。这里的结论是词汇即接口:spec 用项目的词写,等于把术语表当作跨 session 的接口契约。 -
ADR 方面,ADR 是难以反悔的决定的存档,spec 触碰那块区域就得站在它的基础上。
docs/engineering/to-spec.md划了边界:它读取并尊重所涉区域的 ADR,但不在 spec 里链接它们,也不在起草前搜索 tracker 查重,繁忙区域需要你先自己查。代价是 spec 可能重复别人已经提过的 issue,缓解办法是在动手前自己搜一遍 tracker。
第二步先画测试接缝并与用户确认
- Sketch out the seams at which you’re going to test the feature. Existing seams should be preferred to new ones. Use the highest seam possible. If new seams are needed, propose them at the highest point you can. The fewer seams across the codebase, the better - the ideal number is one.
Check with the user that these seams match their expectations.
译文
- 画出你将要测试这个功能的接缝。已有接缝优先于新接缝。尽可能用最高的接缝。如果确实需要新接缝,在你能放到的最高点提出。整个代码库里的接缝越少越好,理想数目是一个。与用户确认这些接缝符合他们的预期。
说明
这一段是整份技能最重要的一段,四条规则构成一条递进的约束链。
- 已有优先:接缝是公共边界,新接缝意味着新的测试面和新的架构承诺,能用旧的就不造新的。
- 取最高:越接近用户行为、离实现细节越远越好,在高接缝上测的是行为而不是内部,这与 codebase-design 的深模块语言一致。
- 越少越好:接缝数量是对架构复杂度的预算,理想数目是一个,也是
improve-codebase-architecture判断某处该不该加深模块的参照。 - 先确认:这是动笔写正文前唯一的一道人类关卡,顺序是先接缝后正文,不是先写再改。
No test is written at an unconfirmed seam.
译文
没有测试写在未确认的接缝上。
说明
这句在 skills/engineering/tdd/SKILL.md,是 tdd 执行层的约束。绑定是间接的,靠这份 spec 传递,所以接缝对话值得在这里认真谈,而不是拖到实现时再补。
第三步按模板写 spec 并发布,打上 ready-for-agent 标签
- Write the spec using the template below, then publish it to the project issue tracker. Apply the
ready-for-agenttriage label - no need for additional triage.
译文
- 用下面的模板写 spec,然后发布到项目 issue tracker。打上
ready-for-agenttriage 标签,不需要额外分流。
模板的 Problem Statement 与 Solution 都从用户视角写
Problem Statement
The problem that the user is facing, from the user’s perspective.
Solution
The solution to the problem, from the user’s perspective.
译文
问题陈述:用户正在面对的问题,从用户的视角写。 方案:这个问题的解法,从用户的视角写。
from the user’s perspective, not a layer-by-layer implementation list
译文
从用户的视角,而不是逐层的实现清单。
说明
这句措辞在 to-tickets 的模板里原样复现,是这对技能之间共用的一条措辞,也是判断一份文档是 spec 还是实现清单的第一眼标准。
User Stories 要求长且编号
A LONG, numbered list of user stories. Each user story should be in the format of:
As an
<actor>, I want a<feature>, so that<benefit>As a mobile bank customer, I want to see balance on my accounts, so that I can make better informed decisions about my spending
This list of user stories should be extremely extensive and cover all aspects of the feature.
译文
一长串编号的用户故事列表。每个用户故事按这个格式写:作为某个角色,我想要某个功能,以便得到某个好处。示例:作为手机银行客户,我想看到我账户的余额,以便对我的支出做出更明智的决定。这份用户故事列表应该极其广泛,覆盖功能的各个方面。
Implementation Decisions 列出决策,不给路径和代码
A list of implementation decisions that were made. This can include:
- The modules that will be built/modified
- The interfaces of those modules that will be modified
- Technical clarifications from the developer
- Architectural decisions
- Schema changes
- API contracts
- Specific interactions
Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.
Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it within the relevant decision and note briefly that it came from a prototype. Trim to the decision-rich parts, not a working demo, just the important bits.
译文
一份已经做出的实现决策清单。可以包括:将构建或修改的模块、这些模块中被修改的接口、来自开发者的技术澄清、架构决策、schema 变更、API 契约、具体交互。不要包含具体文件路径或代码片段,它们很快会过时。例外:如果原型产出的一段代码比散文更能精确地编码一个决策(状态机、reducer、schema、类型形状),把它内联进相关决策,并简要注明它来自原型。只保留决策密集的部分,不是可运行的演示,只要重要的部分。
Testing Decisions 定义好测试并锚定先例
A list of testing decisions that were made. Include:
- A description of what makes a good test (only test external behavior, not implementation details)
- Which modules will be tested
- Prior art for the tests (i.e. similar types of tests in the codebase)
译文
一份已经做出的测试决策清单。包括:什么算好测试的描述(只测外部行为,不测实现细节)、将测试哪些模块、测试的先例(即代码库里类似类型的测试)。
Out of Scope 与 Further Notes 记录边界
Out of Scope
A description of the things that are out of scope for this spec.
Further Notes
Any further notes about the feature.
译文
范围之外:本 spec 不涵盖的事情的描述。 进一步说明:关于该功能的任何进一步说明。
the things you refused are usually the most useful lines on the page
译文
你拒绝过的东西通常是页面上最有用的几行。
分享文章
生成精美分享图或复制链接,与更多人分享本文。
继续阅读
换条路线
从其他文章中稳定抽取
最后更新于 ,距今已过 43 天
部分内容可能已过时