
🚩 2026 年「术哥无界」系列实战文档 X 篇原创计划 第 180 篇,AI 编程最佳实战「2026」系列第 62 篇
大家好,欢迎来到 术哥无界 | ShugeX | 运维有术。
我是术哥,一名专注于 AI 编程、AI 智能体、Agent Skills、MCP、云原生、AIOps、Milvus 向量数据库的技术实践者与开源布道者!
Talk is cheap, let's explore。无界探索,有术而行。

读完 Matt Pocock 的 skills 仓库源码,让人真正意外的其实是这五件事硬被按顺序串在了一起:/grill-with-docs → /to-spec → /to-tickets → /implement → /code-review。ask-matt 的 SKILL.md 把它叫做 main flow——所有正经工程任务都应该走这条路。
但这条链路上每一环真正在做什么、留下什么、什么时候算完成,并不是看 SKILL.md 标题就能猜到的。源码和 README 把 to-spec 的核心约束直接写在了第一行:Do NOT interview the user — just synthesize what you already know。这一句决定了整个主链路的上下文管理逻辑。
这篇文章用一个连续案例(一个叫 多格式导出 的 feature)从左到右走完这五环。每一步都给出:这一步本身在做什么、留下什么文件或 ticket、什么时候算完成。再补一段 链接断在中间 反例和 一次会话只做哪一段 判断。
一行前提:这套 skill 还有一个真正的前置 skill
/setup-matt-pocock-skills——它配置 issue tracker、triage labels、domain docs 路径,后面的to-spec/to-tickets都假设这些已写好。跳过它直接用to-spec/to-tickets不会失败,但写出的产物格式不对。这篇不展开 setup,假设你的仓库已经跑过它。
理解 main flow 之前要先承认一个前提:session 是 disposable 的,但大块工作不是。Matt 在他自己的 dictionary-of-ai-coding 仓库里写过原话:
A ticket should be completable before the session drifts out of the smart zone — and that constraint is testable.
也就是说,AI 编程的会话是会自己变笨的(ask-matt 写 ~120k tokens,dictionary-of-ai-coding 写 125k–150k,YouTube 视频里他提的是 140k——三处数字不一致)。所以 main flow 在做的远不止让 AI 更聪明——它的真正目标是把工程纪律物化成文件,让纪律穿越会话边界。
环节 | 入口触发 | 输入 | 产物 | 完成信号 | 失败症状 |
|---|---|---|---|---|---|
| 用户手动 | 想法 + codebase |
| 每个分支决策解清,术语收敛 | 术语漂移、未澄清的假设进入下一环 |
| 用户手动 | 已被 grill 过的对话状态 | spec,发到 issue tracker,自动打 | 所有 seams 与用户确认;spec 含 Problem / Solution / User Stories / Implementation / Testing / Out of Scope | spec 充满未验假设;用词不来自项目词表 |
| 用户手动 | spec | 多个 tracer-bullet ticket,按 blocking edges 编号发布 | 任意无 blocker 的 ticket 可立即被领走 | ticket 横向分层;单 ticket 装多个 seam |
| 用户手动 | 单个 ticket(fresh context) | 通过 tdd 红绿的代码 + 测试 | tdd 红绿 + code-review 双轴通过 | 测了错的 seam;上下文污染;spec drift |
| 用户手动(implement 末尾强制) | diff vs fixed point | Standards + Spec 两份独立报告 | 两轴分别判定后聚合 | 一轴掩盖另一轴;commit 时混进未经审查的代码 |

main flow 五环节:从澄清访谈到代码审查,每环都把思考物化成文件
我们用一个连续案例走完全程。场景:
这五环每一步都用到这个 feature 作为具体落点。
grill-with-docs 的内部细节(relentless interview、/domain-modeling 怎么驱动、CONTEXT.md 怎么迭代)由同系列第 2 篇讲,这篇不复述。它只回答一件事:在主链路里它扮演什么角色?
源码里 grill-with-docs 的 SKILL.md 全长只有 7 行:
Run a
/grillingsession, using the/domain-modelingskill.
它是一个包装 skill,本身不写代码,只产出三类工件:
CONTEXT.md:项目共享词表——比如多格式导出里 export job 是不是同一个概念、cancel token 是不是和 HTTP 取消混用。docs/adr/*.md:关键决策记录——比如为什么不用同步导出。主链路对它的硬性要求是:spec 之前必须已经被 grill 过一次。原因到 to-spec 那一节才看得到。
源码里 to-spec 的核心约束是:
Do NOT interview the user — just synthesize what you already know.
这一句反直觉。大部分 spec 工具的核心动作就是问用户,但 to-spec 的工作方式是反向的:它假设上游已经做完了访谈,它只把已有共识压缩成 spec 模板。
这个设计有两个后果。
第一,spec 的质量完全取决于上游对话质量。如果 grill 阶段没把导出取消语义问清,spec 里写出来的就是一句支持取消,下游 implement 阶段会按字面意思瞎补。
第二,为什么这么设计——Matt 自己在 AIHero 的 9 Things People Get Wrong With /grill-me and /grill-with-docs 文章里直接解释过:
By the time you finish grilling, you've made hundreds of tokens worth of choices about how your system should work. This is pure gold. Do not clear the context and start fresh just to write a PRD. That's throwing away all your design work.
这正是 main flow 强调 grill + spec + tickets 三步保持同一 context window 的原始理由——上游思考是金的,不能为了让 spec 看起来干净就把上下文清空重做。
to-spec 用一份固定模板(出处:skills/engineering/to-spec/SKILL.md),字段如下:
extremely extensiveImplementation Decisions 这一条很反直觉——它故意不写具体文件路径。原因 to-spec SKILL.md 也明说了:路径会过时,但模块边界不会。
应用到 多格式导出 时,spec 应当这样写(节选):
Problem Statement:用户在使用数据看板时只能下载 CSV。下游 ETL 需要 JSON 和 Parquet 格式做流式消费,运营需要 Excel 做人工核对。
Solution:在现有导出模块上扩展多格式输出。
Implementation Decisions:复用现有 export job 状态机;新增
format字段而不是新模块 复用现有 cancel token 协议 新增格式注册表(FormatRegistry),每种格式注册一个 writer 不要触碰现有 CSV 路径Testing Decisions:测试公共格式输出契约,不测具体 writer 内部 参考
export-csv.test.ts的 seam 模式
源码 to-spec 的第二条硬规则:
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.
seam 是测试的公共边界。在多格式导出里,seam 选得越高越好(理想是一个)——export job 产物契约 就是这么一个 seam:加一个 format 字段就够,writer 实现是内部细节。这条 seam 必须在 spec 阶段就定下来,因为 implement 阶段没有权力决定在哪里测。
如果 spec 写完后,实施过程中发现原 spec 漏掉了一类边界——比如导出超过 10GB 时应该分片——必须回去更新 spec。如果只更新 ticket 描述或直接 implement,spec 轴的 review 会判定实现偏离 spec,整段重做。
判断标准一句话:任何影响 contract(对外接口、状态机、状态枚举)的实现必须回 spec;只影响 writer 内部的可以写进 ticket。
to-tickets 的核心创新只有两个概念:tracer bullet(贯穿弹)和 blocking edges(阻塞边)。
源码原文:
Each slice cuts a narrow but COMPLETE path through every layer (schema, API, UI, tests) — vertical, NOT a horizontal slice of one layer. A completed slice is demoable or verifiable on its own. Each slice is sized to fit in a single fresh context window.
to-tickets 禁止横向分层——比如先做完所有 schema、再做完所有 API、再做完所有 UI。每张 ticket 必须是窄但完整地穿过所有层。
tracer bullet 解决的实际工程问题:
Matt 在 AIHero 的 Tracer Bullets: Keeping AI Slop Under Control 文章里直接引用了《The Pragmatic Programmer》原话:
The pragmatic programmer calls this outrunning your headlights.
AI 写代码典型的失败模式是一口气写完整个 feature 再说——这就是 outrunning your headlights:跑得比车灯照得远。tracer bullet 强制 AI 每次只写一小段全栈走通,写一段就停,等反馈。
应用到 多格式导出,tracer bullet 切法:
format 枚举 + API 接 format 参数 + UI 暴露下拉框 + 一个端到端测试跑通(用 fake writer)。每张 ticket 自己跑完就能 demo,没有写完 Ticket 3 但 Ticket 1 的测试挂了这种事。
to-tickets 给每张 ticket 显式声明它被哪些 ticket 阻塞:
A ticket with no blockers can start immediately.
blocking edges 解决的实际工程问题:让多张 ticket 可以并行。但这里要分清两个边界——ask-matt 源码侧只描述了串行模型(每个 ticket 一个新 session、worked blockers-first by hand);字典(dictionary-of-ai-coding)里 Matt 承认这是官方认可的扩展:票图的叶子节点可以并发执行。读源码照搬会得出全串行的结论,读字典照搬会得出全并发的结论。文章这里严格按字典表述——叶子节点可以并发,但每个 implement 仍然各自一个 session。
在 多格式导出 例子里,Ticket 2/3/4 之间互相无依赖,可以并发。Ticket 5(cancel token 全链路)被所有 writer ticket 阻塞,因为它要测的是任何一个 writer 都能取消。
发布到 issue tracker 时:
.scratch/<feature>/issues/NN-slug.md,每个 ticket 一个文件,按依赖顺序编号。ready-for-agent 标签——源码原话是 agent-grabbable by construction。# 02 — 实现 JSON writer
## 7. Depends on
- 01(schema + API + UI 的 tracer bullet)
## 8. Goal
让 export job 接受 `format=json` 后输出 JSON 文件。
## 9. Implementation Decisions
- 在 `FormatRegistry` 注册 `json` writer。
- 复用 01 已有的 schema 字段映射。
- 不动 cancel token(05 才统一处理)。
## 10. Testing Decisions
- seam 仍然是「export job 产物契约」。
- 测:给定 input data + format=json,产物的 schema 等于 JSON Schema fixture。
- 不测 writer 内部(那是 private impl detail)。
- 参考已有 test:`packages/export/__tests__/csv.test.ts` 的 seam 写法。
## 11. Out of Scope
- Parquet、Excel(03 / 04)。
- cancel token 全链路(05)。这张 ticket 看着啰嗦,但每一段都有用:
/implement 的 SKILL.md 全长只有 15 行:
Implement the work described by the user in the spec or tickets. Use /tdd where possible, at pre-agreed seams. Run typechecking regularly, single test files regularly, and the full test suite once at the end. Once done, use /code-review to review the work. Commit your work to the current branch.
字面意思不多,但拉通看,三条隐含规则贯穿:
测试只能在 spec 阶段就约定好的 seam 上做,不是 implement 阶段才决定。/tdd 的 SKILL.md 把 seam 定义得很清楚——测试的公共边界,不是内部实现。配套的反面是三个 anti-pattern:
反推到主链路:seam 如果没在 spec 里写明,implement 阶段只能瞎选。这正是为什么 spec 阶段那一句 the fewer seams, the better 是硬规则。
ask-matt 的 context hygiene 原话:
Keep steps 1–3 in one unbroken context window — don't compact or clear until after /to-tickets — so the grilling, spec, and tickets all build on the same thinking. Each /implement then starts fresh, working from the ticket.
也就是说:
多格式导出 里,Ticket 1(tracer bullet)做完后,开新会话做 Ticket 2。Ticket 1 跑过的中间讨论、错过的尝试、踩过的坑——全部不进入 Ticket 2 的上下文。Ticket 2 只读 ticket 文件本身。
为什么?Matt 在 dictionary 里写过原话:
The smart zone is a budget, and unrelated work spends it.
在一个 session 里做第二个任务,等于从 dumb zone 起点开始。
implement 末尾必须 review,不能写完直接 commit。这一条把 implement 和 code-review 锁成一对:implement 是完成 ticket,code-review 是评判 diff。
哪一类 ticket 写完就算,不一定要 review?
源码没有给出豁免——implement/SKILL.md 最后一段明确写 Once done, use /code-review to review the work,无例外。
但实践上可以做点折扣:没有 contract 改动的纯内部重构(比如把一个函数提取成 helper)可以走轻量 review。但不能跳过——review 不只是查 bug,更是查 spec drift 的最后一道闸。
/code-review 是 main flow 的关卡门。它必须在 commit 前跑完。
code-review 的核心机制是把 diff 喂给两个并行子代理:
┌────────────────────────┐
│ diff between FIXED │
│ POINT and HEAD │
└──────────┬─────────────┘
│
┌─────────┴─────────┐
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Standards │ │ Spec │
│ agent │ │ agent │
│ (parallel) │ │ (parallel) │
└──────┬──────┘ └──────┬──────┘
│ │
└─────────┬─────────┘
▼
┌─────────────┐
│ Aggregate │
│ (separate) │
└─────────────┘源码原话:
Two-axis review of the diff between HEAD and a fixed point the user supplies. Reporting them separately stops one axis from masking the other.
如果串行做(先 Standards 再 Spec),有一个真实风险:Standards 那轴判了一堆实现有 bug 的结论,会稀释 Spec 轴的发现——读者会下意识用代码不行去解释spec 不一致,把两个独立问题混在一起。两轴分别报告、各自完整,就是不让一个轴掩盖另一个。
这是 main flow 容易被质疑的设计之一。我的判断基于三个事实:
第一,视角不同。implement 的视角是完成 ticket——目标是让 ticket 的 acceptance criteria 跑通。review 的视角是评判 diff——目标是diff 是不是符合仓库标准 + spec。两个目标在同一个思维里很难同时 hold。
第二,确认偏误。implement 的人会偏好自己的实现——我刚写完,逻辑当然是对的。把 review 拆给独立子代理,至少切断了作者评自己这个偏误。
第三,防止上下文污染。implement 的上下文里装满了为什么这么写的合理性(毕竟是你刚做的),这些理由在 review 时应该不存在。新会话/独立子代理强制这个切断。
/code-review 的双轴不是表演,是结构性地保护 review 的判断不被 implement 的判断稀释。
下面这段是模拟 Standards 轴命中 Shotgun Surgery 的 diff 片段。这是样式化样例,不是真实 PR——只为说明 review 报告长什么样。
// 改动:把格式枚举从三处分散的 if/else 合并到注册表
- if (format === 'csv') return renderCSV(data)
- if (format === 'json') return renderJSON(data)
- if (format === 'parquet') return renderParquet(data)
- if (format === 'excel') return renderExcel(data)
+ return FormatRegistry.lookup(format).render(data)Standards 轴报告(节选):
Hit — Repeated Switches: 4 个 if/else 在 3 个不同文件(ExportPanel.tsx, ExportAPI.ts, ExportJob.ts)出现完全相同的形态。已被替换为 FormatRegistry.lookup。 Verdict: ✅ 通过。 Note: 同时建议给 FormatRegistry 加一个注册时类型断言(
register(format: FormatKind, writer: Writer)),防止 runtime 写错。
Spec 轴报告(节选):
Item 1 — 实现 JSON writer:spec 要求 format 字段接受
json,产物 schema 等于 fixture。 Implementation: 实际产物在空数组时省略data字段(fixture 包含空数组但data: [])。Spec 没显式说空数组行为。 Verdict: ⚠️ 需要 spec 仲裁。建议在 spec 的 Further Notes 段补一句空输入时产物为{ data: []} 而非省略 data。
两个轴各自的判定互不污染,aggregator 把两份报告直接合并发给 implement 的人。

code-review 双轴并行:Standards 与 Spec 各自独立判断,aggregator 不让一轴掩盖另一轴
main flow 的代价是步骤多。问题常常藏在中间——跳过其中某一环。下面三个反例都来自 main flow 自身规则的逻辑反推。
/to-spec,直接 /to-tickets症状:
to-tickets 没有 spec 可读,只能从对话里抽取任务——粒度完全取决于对话质量。具体到 多格式导出:跳过 spec 之后,Ticket 2 很可能写成加 JSON 输出——没说 seam,没说 schema 边界,没说 cancel token 是否要兼容。implement 阶段会按字面意思实现,再让 code-review 来兜底,但 code-review 兜的是 spec drift,而 spec 本身不存在。
源码对应规则:to-spec/SKILL.md 第一行 + to-tickets/SKILL.md 中 respect ADRs in the area you're touching——没有 spec 就没有 ADRs 可尊重。
/to-tickets,直接 /implement症状:
具体到 多格式导出:直接 implement 加多格式导出 这个大 ticket,会写出 schema、API、UI、3 个 writer、cancel token 的全栈改动——一票写完。某次 tdd 失败回滚时,会发现改动横跨 8 个文件,只能 git reset --hard,连做到哪里了的痕迹都没留下。
源码对应规则:to-tickets/SKILL.md 中 Each slice is sized to fit in a single fresh context window。
/code-review,implement 后直接 commit症状:
具体到 多格式导出:直接 commit 后,代码里如果出现了 writer 内部写死 schema、cancel token 协议悄悄变种 等情况,没有 review 抓住这些,下一个 feature 在同一个 seam 上叠加,最终整段重写。
源码对应规则:implement/SKILL.md 最后一段 Once done, use /code-review to review the work。
每个反例的本质都是把上一环的纪律扔掉,让下一环独自承担。main flow 看起来啰嗦,但每一环都在做上游做不了的事——grill 做澄清、spec 做综合、tickets 做切片、implement 做实现、review 做评判。没有一环是冗余的。

三个反例的共同模式:把上一环的纪律扔掉,让下一环独自承担——main flow 没有一环是冗余的
main flow 真正反直觉的设计在 ask-matt 的 context hygiene。直接给原文:
Keep steps 1–3 in one unbroken context window — don't compact or clear until after /to-tickets — so the grilling, spec, and tickets all build on the same thinking. Each /implement then starts fresh, working from the ticket.
一次会话只做哪一段的具体判断:
/clear、不要 /compact。/implement:开新会话,只读 ticket 文件。dictionary-of-ai-coding):约 120k–150k tokens(三个来源数字不一致:ask-matt 写 120k,dictionary 写 125-150k,Matt YouTube 视频说 140k)。/handoff 把当前会话压缩为 markdown 文件,新会话引用它(forks,开新会话);或 /compact 留在当前会话但压缩较早历史(continues,同会话)。ask-matt 明确区分这两条:/handoff forks;/compact continues。多格式导出 例子的会话切分:
/code-review。要么开新会话,要么用同会话末尾但新开 review 子代理——避免 implement 的上下文污染 review 的判断。ask-matt 解决的是一次会话内的 scope 控制——长会话做规划,短会话做执行。
但任务跨多个会话也不够(greenfield、大型 feature build、路径不清晰)时,main flow 就不够了。Matt 的解决方案在第 7 篇里:wayfinder 输出决策 ticket(决策不是交付),雾散去后 handoff 而不是 build,最后把决策合并到 main flow 的 /to-spec。
本文不复述 wayfinder/handoff 的内部细节——/handoff skill、AIHero 的 The /handoff Skill 原文、与 wayfinder 的衔接模式都留给第 7 篇。这里只说一句衔接点:main flow 内部其实默认假设你会用 /handoff 来切会话,但源码 README 没把它列进 main flow——这一点也是第 7 篇会展开的内容。

main flow 会话切分:grill+spec+tickets 共享长会话,每个 implement 独立新会话,smart zone 是 token 预算
把 main flow 当成刻度尺而不是开关。强制走完全部 5 环不一定是正确选择。
判断的边界:
1. grill = 写 spec:错。grill 是访谈,spec 是综合。两者在主链路里分工不同——这也是为什么 grill-with-docs 的产物(CONTEXT.md + ADR)和 spec 是不同的文件。
2. to-spec 可以事后访谈补:错。源码原话 Do NOT interview the user——如果 spec 阶段发现需要访谈,说明上游 grill 没做完,应该回 grill 而不是去 spec 里临时补问。
3. implement 内部可以顺手 review:错。两轴 review 的价值就在于独立判断。implement 的人评自己会被确认偏误污染。
4. tracer bullet = 小步快跑:不完全对。tracer bullet 强调窄但完整穿过所有层,不是小。一行代码改完不穿过所有层就不算 tracer bullet。
5. blocking edges 是项目管理工具:错。blocking edges 的目的是让无阻塞的 ticket 可以并发被领走,这和项目管理不是一回事——票图叶子节点可并行执行是 main flow 官方认可的扩展。
main flow 的源码、Matt 的 blog、Reddit / Hacker News 上的真实反馈,都没有承诺 ROI。Reddit 上有用户自报质量优先版本大约 1 小时/小 issue,但这是把 main flow 进一步工业化后的代价样本——Matt 自己没有要求这样串。
所以本文不写减少返工 / 减少 token / 减少事故。这些是 vibe coding 时代被反复证伪的承诺。main flow 卖的不是省时间,是让 AI 编程有工程纪律——而工程纪律的价值,取决于你愿不愿意用文件来承载它。
把这五环放到一起看,会发现一个共同特征:每一环都把当前思考物化成文件。
CONTEXT.md + ADR文件是 main flow 的真正产物。会话是一次性的(smart zone 会过期),但文件可以穿越会话。这就是为什么 main flow 强调每个 implement 一个新会话——下一个会话只需要读 ticket 文件就够了,根本不需要记住上一个会话的所有思考。
如果只记一句话:main flow 的设计不是让 AI 更聪明,是让工程纪律活在文件里,会话死了纪律还活着。这也是它和 vibe coding 工具之间一条清晰的分界线。
至于这个设计能不能撑住——再看半年:实际工程团队跑下来的 ticket 数量、code-review 命中率和 spec drift 频率会给出答案。这是一个开放的问题,不是已经验证的结论。
说明:本文内容基于 Matt Pocock Skills 源码和官方文档分析整理而成。文中关于 main flow 五环节的入口、出口、产物、门禁描述,三处反例的对照,会话语义边界判断,均来自源码 SKILL.md 原文引用,但本文未在生产环境中逐项跑通整套 main flow。 文中的配置模板和参数建议仅供参考,实际效果请以你的业务数据和环境测试结果为准。 如果你有实际使用经验,欢迎在评论区分享交流。
好啦,谢谢你观看我的文章,如果喜欢可以点赞转发给需要的朋友,我们下一期再见!敬请期待!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。