to-spec 精读:明令『不许提问』的技能,却把唯一一次打断留给了接缝
to-spec 把刚结束的对话压成一份 spec。本文走一遍它的真实触发路径,解释它为什么禁止重新访谈、为什么动笔前只问一个问题(接缝),以及它刻意剥离文件路径、代码片段、架构任务适配和『保持同步』幻想之后,产物为什么反而更可靠。
(本文读的是 mattpocock/skills 快照 5beb35b,2026-10-19。引用原文都能按文中路径回仓库核对。这是「逐个技能精读」系列的第五篇,聚焦 to-spec 一个技能。)
先走一遍触发路径
skills/engineering/to-spec/SKILL.md 的 frontmatter 带着 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.
你输入 /to-spec,也可以带一个引用,比如 /to-spec #<map_issue>。接下来它只做四件事:读一遍仓库状态;画出这次改动的接缝(seams);把接缝拿给你确认;按固定模板写 spec、作为一条 issue 发到 tracker,顺手打上 ready-for-agent 标签。
产出是一份文档,七个固定小节:Problem Statement、Solution、User Stories、Implementation Decisions、Testing Decisions、Out of Scope、Further Notes。User Stories 被要求写成一个「LONG, numbered list」——很长、带编号、每条形如 As an <actor>, I want a <feature>, so that <benefit>。
它和「一般技能」的差别:它拒绝重新访谈
常见的「把对话变成文档」技能,动笔前多少会补一轮澄清:确认目标、追问缺失的细节、把模糊处问清楚。to-spec 把这条路明确堵死——Do NOT interview the user。
理由是它对自己产物的定位。docs/engineering/to-spec.md 里把它叫 decision record:“The spec exists because context windows end.” 该决定的事情,已经在上一轮的 grilling 里决定完了;spec 只是把那些决定落到一份新会话能接手的文档里。所以文档里还有一句更狠的:“The spec does not validate or decide anything.” 如果 spec 里出现了你从没说过的内容,那不是它在补全,那是缺陷。
正因为职责这么窄,它的触发条件也窄到近乎苛刻。文档给了一张表:
| Where you are | What to run |
|---|---|
| 还没决定任何事 | 先 grill-with-docs |
| 决定了,且一个上下文装得下 | 直接 /implement,跳过 spec |
| 决定了,但工作跨好几个会话 | 才轮到 /to-spec + /to-tickets |
| wayfinder 地图已经清空 | /to-spec #<map_issue> |
真正把「直接写一份 spec」区分开的,是接缝
写散文之前先定接缝——Seams before prose。这是整个技能里唯一一个会打断你的动作,也是它与「让模型直接写一份 spec」最本质的差别。SKILL.md 的 step 2 原文:
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.
两个约束值得单独拎出来:优先复用已有接缝,而不是新建;接缝越少越好,理想值是一。这不是审美偏好,是因为下游要复用这份契约。文档说得很直白:tdd 只在事先约定好的接缝上工作;code-review 拿 diff 对照 spec,所以一条没人事先同意的接缝会直接变成 review 里的一条 finding。这两条通路都穿过这份文档。
拿它和「让模型直接写一份 spec」对照:后者一上来就生成七个段落。你拿到一份读起来很顺的文档,读到 Implementation Decisions 才发现,它悄悄假设了一批你从没同意的测试边界。to-spec 把整个技能里唯一一次交互,压在了这个问题上。
它有意识摒弃了什么
一、摒弃了访谈。 见上。这条是整个技能的定位声明,不是省事。
二、摒弃了文件路径与代码片段。 模板里明文写着:「Do NOT include specific file paths or code snippets. They may end up being outdated very quickly.」例外只开一个口子:如果 prototype 产出的片段比散文更精确地编码了某个决定(状态机、reducer、schema、类型形状),可以内联进相关决定里,注明来自 prototype,并且只留决策密集的部分,不留一个能跑的 demo。
三、摒弃了硬凑架构任务。 文档承认这是已知局限:模板重度依赖 user stories,而 user stories 不适合重构或模块边界这类工作。它的处理不是加一个新模板,而是告诉你绕过:改用 Implementation Decisions 与 Testing Decisions 两节,把持久的架构决定写进 ADR,而不是塞进 spec。
四、摒弃了重复搬来的理由。 曾经的前置条件后面挂着一句解释:「它是 user-invoked,所以你自己调不了它」。提交 6a34259 把它整句删掉,提交信息给的理由是「Per writing-for-agents’ no-op test」——tell the user to run /X 本身已经是一条没有分支的指令,agent 不可能真的去调用它,那句解释在运行时什么也没改变,而且还和 .agents/invocation.md 里的单一来源重复。五处调用点一起删。
五、摒弃了「让 spec 保持同步」的幻想。 文档直说:没有任何机制让它保持最新,它实现的第一次就会过时,所以要「treat it as disposable after the work ships」。真正要长期留下来的东西是 GLOSSARY.md 和 ADR,不是那份被反复编辑的 spec。
六、摒弃了讨好读者的「摘要模式」。 文档承认有人抱怨读完太累,而且「there is no summary mode」。它的回应不是加一个摘要,而是把责任推回上游:「if the spec surprises you, the grilling was too shallow; the spec is not too long.」它只请你认真读两节——接缝和 Out of Scope,因为漏掉的决定在这两处最贵。
七、摒弃了旧词汇。 提交 386d4ff 把它从 /to-prd 改名为 /to-spec,同时把 /to-plan 与 /to-issues 合并成一个 /to-tickets。改名的理由是让 “spec” 成为贯穿全程的单一术语;文档里为此留了一条 FAQ,说明旧 slug 不再可用、需要按新名字重装。这次改名不只动了技能本身——仓库里的交叉引用、链式图、CONTEXT.md、ADR 和 setup 模板都被一次扫过。
一个它没有藏起来的代价
spec 发布时会自动被打上 ready-for-agent。这个标签的意思是「不需要再做 triage」,标记的是输入——这份文档已经完整到 agent 可以据此开工。但它并不等于「这是一张可以执行的工单」。
问题恰恰出在这里:轮询 ready-for-agent 的 AFK agent 看不出这层区别,于是会试图在一个 run 里把整份 spec 建完,而不是去接那一片片切好的票。文档把这个列为该技能上报最多的问题,并且没有假装解决它,只给了两个缓解手法——在你的 AFK agent 的 prompt 里显式排除父 spec,或者在 /to-tickets 跑完之后把标签摘掉。一个技能肯把「我这个标签会被误读」写进文档,本身就是它值得读的地方。
原文证据
docs/engineering/to-spec.md 里「seams before prose」的价值链条:
Other skills use those agreed seams later. tdd works only at seams you agreed in advance. code-review reviews the diff against the spec, so a seam nobody agreed to shows up as a review finding. Both connections go through this document.
同一文档的「It’s working if」里有一条,是检验这个技能有没有真在工作的最好判据:
You remember making every decision in it. It invented nothing to fill a section.
可迁移结论
- 把「做决定」和「记录决定」拆成两个技能,并让记录者明文禁止提问。 一个技能只干一件事时,它的产物才可验证:这里「spec 里出现我没说过的内容」就是一条硬缺陷。
- 生成之前先确认一个结构性问题,而且只确认一个。 接缝清单比十轮澄清更值钱,因为它会污染下游所有技能。
- 在产物里写清「这不是做决定的地方」。 一句
Do NOT validate or decide anything比一堆格式约束更能防止模型自己加戏。 - 删理由时用 no-op test。 一句话删掉之后运行时行为不变,它就不是指令,是注释——注释该放回单一来源,不该在每个调用点重复。
- 承认产物会过期,并指定真正的持久文件。 spec 是一次性快照,
GLOSSARY.md和 ADR 才是要活下来的东西。
to-spec 读起来像一份很朴素的文档生成器。但它把技能里最贵的两个决定都摆到了明面上:什么时候不该问人,以及问的时候该问什么。
讨论
用 GitHub 账号留言;评论保存在公开仓库chengshu-blog-discussions的 Discussions 里。也可通过 RSS 订阅后续文章。