Skills For Real Engineers 系列之三:需求聊清楚了,怎么一步步做到交付?
书接上回。
第二篇聊了 grill-with-docs:先查事实,把目标、取舍和边界讨论清楚,再确认双方理解一致。
问题问完了,我也点头了。这回总该写代码了吧?
可以。不过,如果这件事要分好几次做,甚至换个对话继续,"刚才都说好了"就有点靠不住了。聊天里既有最终决定,也有被我否掉的推荐。接手的人读到"升级日志系统",未必知道我为什么还要保留旧实现。
这篇接着聊:怎么把讨论整理成规格,拆成任务,再推进实现和验证,并做审查。
text
grill-with-docs → to-spec → to-tickets → implement
├─ 实现与验证
├─ code-review
└─ 提交
注意,code-review 已经是 implement 要求的收尾步骤,不需要每次结束后再重复调用一遍。小任务也不一定要走完整条路,后面展开。
案例继续用我的开源项目 RxStudy。日志代码和文档是真实依据,日志规格与任务是根据场景提炼的教学示例;本轮检查未找到 RxStudy 中后三个技能的完整调用链,不把示例写成当时的实施经过。
另外,我会穿插几段其他项目的真实调用,匿名说明当时的背景。这些经历不属于 RxStudy。调用展示只将工具中的技能入口统一为 /技能名,还原文件名转义,不扩写我的指令;助手回复的原文节选和历史过程概括分别标明。
1. 聊清楚了,为什么还要整理规格?
假设我们已经确认:旧日志保留,新体系独立接入;编译前选择模式,不做运行时切换;新日志能导出,失败要反馈,也不能顺手把旧目录打包进去。
这些话散在几十轮聊天里,当然也能继续做。但我想检查"这次究竟答应交付什么",就得来回翻,还要分辨哪些已经作废。
to-spec 做的就是这次整理。它的开头很直接:
text
Do NOT interview the user; just synthesize what you already know.
意思是:利用已有讨论和代码理解,整理规格,别重新开始采访。to-spec 源码
咳咳,刚被 grill 问完,再来一遍"请描述您的需求",我是真的会想关掉窗口。
1.1 不重新采访,但还要确认怎么测
技能会补看尚未了解的代码,使用项目术语,尊重相关 ADR。随后,它要提出从哪些接口验证行为,并让我确认。
源码把这个位置叫 seam。放在日志例子里,可以理解成:调用导出接口,检查返回的 ZIP 和错误,而不是伸进内部断言某个私有函数调用了几次。
它还要求优先复用已有位置,尽量从更高层观察,减少零散的测试入口。这里确认的是验证约定,不是把整轮需求讨论重来。
这里拿一次真实调用来说。2026 年 10 月 9 日,我在另一个项目里讨论应用入口重构:入口文件职责太多,Repository 又集中在启动时创建,我希望用到时再创建。
前面已经逐轮确认:拆分入口职责;统一配置创建规则,首次使用时才创建,按所属作用域复用;测试可以独立替换依赖。请求时机和页面状态保持不变,导航行为也不变,验证入口也已经确认。
到 to-spec 时,我实际只说了:
text
执行 /to-spec
助手回复的原文节选是:
测试接缝已在上一轮确认,本次直接沿用。
随后,它将已确认的讨论整理成规格,发布到当时配置的本地任务跟踪器,标记为 ready-for-agent,没有重新采访,也没有修改业务代码。这是历史过程概括。
命令虽然短,前面的讨论并不短。 如果在空白会话只输入这句话,助手并不知道哪些方案已经被否定、测试方式是否确认。短指令能接着做,是因为依据还在上下文里。
回到 RxStudy,它配置的是 GitHub Issues,因此按该配置"发布规格"意味着创建 Issue。存放方式与上面的本地实践不同,技能要求的整理过程仍然适用。如果任务系统尚未配置,则提示先运行初始化技能。
1.2 规格要留下哪些东西?
源码模板包括问题、方案、用户故事、实现决定、测试决定、范围之外和补充说明。用户故事要求充分覆盖功能,下面只展示与本文有关的节选:
markdown
## 问题
排查网络问题时,需要查看详细日志并导出复盘。
引入新体系时,仍需保留旧体系的行为与回退方式。
## 交付要求
1. 开发者可在编译前选择日志体系,重新启动后生效。
2. Modern 模式下,调用方可以获得包含新日志的 ZIP 地址。
3. 导出失败时,调用方得到明确错误;新体系不导出旧目录。
## 已确认的决定
两套体系独立,一次启动只启用一套。
Debug 提供详细网络输出;Release 只保留失败摘要。
导出回调在主队列;成功 ZIP 由调用方使用后清理。
## 测试决定
从日志输出和导出接口观察行为,检查 ZIP 实际内容与错误结果。
隔离回归与工程构建分别报告,设备系统日志另行验证。
## 不做什么
不增加运行时切换、上传服务或导出页面,不替换旧导出入口。
这是教学整理,不是已经发布的规格。完整规格还需覆盖已确认的其他行为,例如脱敏策略。
规格和任务通常不写具体文件路径或代码片段,避免实现调整后迅速过时。该版本允许保留来自原型、比文字更准确的状态机或类型等关键片段,但不要求把整个演示搬进去。

图 1:规格保留整体承诺,任务承接其中一批交付;相关依据仍要能找到。
顺带补一处版本差异:本文使用的 RxStudy 基线中,配置源码选择了 .modern,而日志文档和测试脚本仍要求默认 .legacy。两者不一致,所以不能继续把"默认旧模式"当成该提交的源码事实。讨论形成的要求与当前实现,也需要这样分开核对。
2. 规格与任务,分别解决什么问题?
规格回答"整体答应交付什么",任务回答"这一批做完,具体多了什么能力"。
拿导出来说,规格包含模式选择和输出策略,也包括导出与兼容边界;一张任务可以只负责其中的正常导出路径,但要把这条路径做完整。
下面是一张完整的教学任务。A、B 只是文章编号,不是真实 Issue 编号。
markdown
# A:Modern 模式能够记录并导出日志
## 来源与背景
关联"日志改造与导出"规格及其测试决定。
为开发者提供新日志的离线复盘能力,保留旧体系。
## 本次交付
选择 Modern 并启动后,写入一条指定日志,调用导出接口,
得到可解压且包含该条日志的 ZIP。
## 验收条件
- [ ] 最近写入的指定日志出现在解压后的文件中。
- [ ] ZIP 返回时仍存在,调用方可以读取。
- [ ] 完成回调位于主队列。
- [ ] 临时快照清理,原日志不被导出操作删除。
- [ ] 通过约定的隔离回归验证上述行为。
## 阻塞关系
无,可立即开始。
## 范围与验证约定
不新增上传或页面,不替换旧导出入口。
错误分支的扩展见任务 C,本票仍需处理基本失败。
继承规格中的模式隔离限制;设备系统日志不属于本票验证结果。
to-tickets 的远端任务模板本身提供父任务与交付内容,也列出验收条件和阻塞关系。这里额外展开背景、范围与验证约定,是我对接续任务的建议,不是说每个字段都由模板强制规定。
也不必把整份规格复制到每张票里。关键限制可以简短重申,其他决定通过明确关联找到。只有"实现导出功能"六个字,接手的人还是得猜:导出哪个目录?成功文件什么时候删?旧入口能不能改?
3. to-tickets:每张票都要能验
to-tickets 可以从计划、规格或当前讨论拆任务。传入规格路径或 Issue 引用时,它会读取完整内容和评论;代码没了解过,也要先探索。to-tickets 源码
它强调 tracer bullet,也就是纵向切片:范围窄,但从入口到结果是完整的,做完可以展示或验证,大小适合一个新上下文完成。
3.1 别把零件齐了,当成能力有了
"写文件管理器""写压缩层""接回调",看起来分工明确,但前三张票可能都完成了,调用方仍然导不出一个文件。
我更愿意按行为拆。
| 教学任务 | 做完能验证什么 | 被谁阻塞 |
|---|---|---|
| A:记录并导出 | 写入指定日志,获得有效 ZIP,内容可检查 | 无 |
| B:区分构建输出 | Debug 有详细网络内容,Release 只保留失败摘要 | A |
| C:导出错误反馈 | 未初始化、无日志、压缩失败等场景返回错误,临时产物正确清理 | A |
| D:保留旧模式与回退 | 切回旧模式后原行为保留;新接口不隐式启用 Modern;两套目录隔离 | A |
这里假设 A 交付可运行的新日志入口和正常导出路径,所以后三张依赖它;B、C、D 之间没有凭空连线。每张都带自己的验证,不能最后再补一张"统一写测试",替前面所有任务证明完成。
D 也不代表前面可以随意破坏旧体系。保留旧实现是全程约束;D 专门交付模式切换与回退场景的完整保证。

图 2:箭头表示阻塞关系;没有相互阻塞,不代表修改同一文件时天然不会冲突。
这个例子没有 UI,就不需要为了凑"纵向"新增页面。完整指的是穿过完成该行为必需的部分,不是每张票都必须集齐 UI、网络、数据库。
3.2 拆完先确认,再发布
技能会展示标题和阻塞关系,说明交付行为,再询问粒度是否合适、依赖是否真实,以及是否合并或继续拆分,确认后再发布。
前面那次应用入口重构,在规格生成后,我实际也只说了:
text
执行 /to-tickets
它没有直接落盘,而是先给出五张票的草案。下面是脱敏后的历史拆分概括,不是原话引用:
| 编号 | 本批交付 | 阻塞 |
|---|---|---|
| 01 | 拆分入口职责,保持现有行为,相关测试继续通过 | 无 |
| 02 | 引入显式运行模式与按需装配,先贯通一条启动流程,暂留旧入口兼容 | 01 |
| 03 | 迁移其余应用作用域依赖及相关测试 | 02 |
| 04 | 迁移会话作用域依赖,验证会话切换与旧请求隔离 | 02 |
| 05 | 删除旧构造入口,完成完整测试与静态分析 | 03、04 |
实际依赖结构是:
text
01 → 02 → 03/04 → 05
助手随后问粒度与阻塞关系是否接受,我回复的原话是"确认"。这之后,它才发布五张本地任务,并记录验收条件与依赖。
这不是一套通用拆票模板,也不是前面 RxStudy 日志任务的执行记录。它展示的是迁移时的安排:先让新旧方式兼容,两批迁移共享前置任务,最后才能删旧入口。不能因为我只发了一句命令,就省掉中间的拆分确认。
远端一票一个 Issue,按依赖顺序创建,优先使用平台原生阻塞关系;来源是已有 Issue 时关联为子任务。本地则一票一个 Markdown 文件,用正文记录依赖。技能还明确不关闭或修改父 Issue。
执行时挑阻塞已完成的票,不是看编号小就先拿。若有必要的前置重构,技能要求先做;但不能借此夹带一轮与目标无关的大清理。
3.3 全仓迁移为什么有例外?
如果只是把一个共享接口改名,却会同时打断大量调用方,硬拆"每个页面各做一票"可能票票都无法通过检查。
源码对此给出 expand--contract:先并存新旧形式,再按包或目录分批迁移,最后等所有批次完成才删除旧形式。若批次仍不能独立保持通过,就共用集成分支,并由最终集成验证任务收口,不能声称每批都已经可独立合入。
RxStudy 的 CocoaPods/Tuist 双构建也涉及不少文件,但这是工程与依赖协作问题,不能因为"改得多"就直接套用这个例外。
4. implement:拿到任务以后怎么推进?
implement 的正文也很短。传入任务引用,它要先获取任务并报出标题,引用有歧义就询问。至少能让我及时发现:你是不是拿错票了?
接着是几条明确规定:尽可能在预先约定的位置调用 tdd;经常做类型检查和单个测试文件的检查;结束时运行完整测试集;调用 code-review,然后提交到当前分支。implement 源码
所以它当然要写代码,但工作并没有停在生成代码。
4.1 约好的测试,别写着写着换了
依赖的 tdd 要求先确认测试位置,按"一条失败测试、一份最小实现"的方式推进,测试外部行为。tdd 源码
导出例子里,我想看到的是"ZIP 里确实有刚写的日志",而不只是"压缩函数被调用了一次"。如果换掉内部实现,行为没变,测试不应该跟着全碎。
实际接手一张已经写清楚的任务,指令可以更短。还是 2026 年 10 月 9 日的记录,我执行过另一张收口任务:
text
/implement 执行 08-controller-removal-review.md
这张编号任务此前已经形成。我这一轮直接指定文件,助手先定位、读取它,再确认实施范围与验证标准。它开头回复的原文节选是:
先定位并读取
08-controller-removal-review.md,确认实施范围和验证标准,再修改、审查并提交到当前分支。
历史收尾报告记录了测试和静态分析结果,也记录了规范与规格审查以及本地提交,同时明确设备验证尚未执行。这是对历史会话报告的概括,本轮没有重新验证这些结果,也不能拿它们证明 RxStudy 已通过检查。
这里还要分清顺序:这张 08 任务在前,前面介绍的入口重构讨论与拆票在后,属于新一轮工作。它们不是同一项工作从规格到实现的完整闭环。
这段实践让我觉得,真正省事的是任务里已有目标和范围,也有验证约定,文件也能找到。缺少这些内容,"执行某张票"依然只是让助手从头猜。
4.2 验证结果得说人话
RxStudy 的日志回归入口使用真实日志、网络和压缩依赖,在临时副本中做隔离测试,并不启动模拟器。
项目文档记录了这些检查,也记录了整工程和设备验证的限制。本篇只读检查,没有重新运行它们。前面提到的默认模式不一致,还会触发该脚本的默认值断言,不能看见入口就宣称当前运行通过。
源码类型检查和隔离回归各自证明不同事情,CocoaPods/Tuist 工程构建与真机系统日志观察也各有验证范围。两套构建方式也要分别检查,不能一边通过就替另一边签字。
缺工具或依赖时,我希望结果写成"哪些通过,哪些受阻,哪些还没验证"。技能要求完整测试,不代表遇到环境问题可以直接宣布完成。
5. code-review:写完了,还要检查有没有做对
implement 明确要求完成后调用 code-review;后者也能独立审查分支或 PR。它拆成两个方向:
| 方向 | 检查什么 |
|---|---|
| Standards | 是否遵守项目文档中的编码规范,并检查该版本列出的代码异味 |
| Spec | 规格要求有没有遗漏、实现是否正确、有没有做范围之外的事情 |
该版本通过两个并行子代理分别审查,再并列汇总,不把两个方向混成一个评分。代码异味属于判断性提示,项目明确规范优先。code-review 源码
日志代码可能命名漂亮、类型检查通过,却把旧目录一起导出了;也可能功能都实现了,但违反项目规范。这两种问题,不能互相抵消。
教学审查时,我会沿着验收条件看:错误反馈漏了吗?ZIP 是否过早删除?有没有擅自加上传?审查必须能对照任务与规格,不能只留一句"整体看起来不错"。
5.1 还有一个不能略过的范围问题
这个版本要求固定比较基点,读取到 HEAD 的差异:
bash
git diff <fixed-point>...HEAD
它会确认引用有效、差异非空,再找规格和规范来源。它因此不能被解释成"自动审查当前目录所有未提交修改"。而 implement 又写着审查后提交,两份说明并没有详细交代这中间如何安排提交状态。
我的建议是,实际使用时明确审查基点和规格来源,再确认哪些改动确实进入比较范围。发现漏审,就先解决范围问题,不能把空差异算作通过,也不能擅自替源码补出一套隐藏机制。
审查发现问题后,核对、修正并重跑受影响的验证,也是我的使用建议。调用过一次审查,不等于问题已经修好。最后的提交只代表记录了改动,推送、建 PR 或合并需要另外的流程。
6. 什么时候继续聊,什么时候开新对话?
ask-matt 明确建议:讨论、规格和拆票保持连续上下文,不要在拆票前随意清空或压缩;拆出独立任务后,每张 implement 可以从新上下文开始。流程与上下文说明
前半段还在整理决定及其原因,过早清空,容易留下"要做什么",丢掉"为什么不能那么做"。后半段任务已经承接交付要求,旧任务的探索细节才更容易放下。
新会话可以这样开:
text
/implement <任务引用>
接着做这张票,先读关联规格和评论,确认阻塞任务都完成了。
目标、不能改的地方和测试约定都在里面;有矛盾先指出来。
别凭现在的代码猜我们当时想做什么。
这些信息得真的存在,而且能访问。只有一个父任务标题或失效链接,算不上独立任务。
空间不够时也不用硬撑。阶段边界说明允许在合适阶段压缩、交接。我会先核对保留目标和已确认决定,再核对否定方案与未决问题,确认证据位置,再继续。第二篇说讨论不好可以重开,这里补一句:已经确认的依据要么带走,要么明确重新讨论,别悄悄丢掉。
7. 小任务,不用每次办一套手续
看到这里,可能有人已经开始头大:改一行字,也要规格、拆票、开会?
不用。ask-matt 的分支很明确:多会话工作走规格和任务;单会话能完成,就在当前上下文直接进入 implement。
比如现有日志提示有个错别字,目标和影响都清楚,没有必要再造一份长规格。"帮我优化日志"虽然也只有六个字,含义却没确定,还是要先聊。
小不小,看实际行为与风险,不能靠提示词长度判断。已经出现异常时,也要先建立可验证的问题与诊断依据,不能靠走完技能名称代替查原因。
我喜欢这套流程的地方,是能随时检查:答应交付什么,这张票完成什么,结果怎么证明,审查有没有对上要求。
流程走到这一步,才算把"咱们聊清楚了",往"这件事确实做好了"推进了一段。至于文档写了多少页、调用了几个技能,我觉得都排在后面。
附言
就在编写与整理的时候,Skills For Real Engineers的Skill也进行了更新,主流程也在后续更新了/retro这个技能,用于复盘本次会话中反复找文件和漏掉检查的问题,作为一个主流程的闭环,我目前还没有用上,所以不敢过多的进行说明。
参考资料
本文于 2026 年 10 月 19 日核对官方仓库,main 指向下列固定提交;后续更新可能改变行为。技能源码采用仓库的 MIT 许可,文中只做短摘录与解释。