Skill Learning

to-tickets

1. Bilingual SKILL.md

左右滑动可直接切换英文版与中文版;两种语言按段落自动对齐。英文中的蓝色虚线词语可点击查看解释。共标记 20 处。

入口 skill:to-tickets左右滑动切换语言 · 段落位置自动对齐
# To Tickets
Break a plan, spec, or conversation into a set of **tickets**: , each declaring the tickets that **block** it.
The issue tracker and triage label vocabulary should have been provided to you. If not, tell the user to run `/setup-matt-pocock-skills`.
## Process
### 1. Gather context
Work from whatever is already in the conversation context. If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its .
### 2. Explore the codebase (optional)
If you have not already explored the codebase, do so to understand the . Ticket titles and descriptions should use the project's , and respect ADRs in the area you're touching.
Look for opportunities to to make the implementation easier. "Make the change easy, then make the easy change."
### 3. Draft vertical slices
Break the work into **tracer bullet** tickets.
<vertical-slice-rules>
- Each slice cuts a through every layer (schema, API, UI, tests): vertical, NOT a horizontal slice of one layer - A completed slice is - Each slice is sized to fit in a - Any prefactoring should be done first
</vertical-slice-rules>
Give each ticket its ****: the other tickets that must complete before it can start. A ticket with no blockers can start immediately.
**Wide refactors are the exception to vertical slicing.** A **wide refactor** is one mechanical change (rename a column, retype a shared symbol) whose **** fans across the whole codebase, so a single edit breaks thousands of call sites at once and no vertical slice can land green. Don't force it into a tracer bullet; sequence it as ****. First expand: add the new form beside the old so nothing breaks. Then migrate the call sites over in batches sized by (per package, per directory), each batch its own ticket blocked by the expand, keeping CI green batch to batch because the old form still exists. Finally contract: delete the old form once no caller remains, in a ticket blocked by every migrate batch. When even the batches can't stay green alone, keep the sequence but let them share an integration branch that all block a final integrate-and-verify ticket; green is promised only there.
### 4. Quiz the user
Present the proposed breakdown as a numbered list. For each ticket, show:
- **Title**: short descriptive name - **Blocked by**: which other tickets (if any) must complete first - **What it delivers**: the end-to-end behaviour this ticket makes work
Ask the user:
- Does the granularity feel right? (too coarse / too fine) - Are the correct: does each ticket only depend on tickets that ? - Should any tickets be merged or split further?
Iterate until the user approves the breakdown.
### 5. Publish the tickets to the configured tracker
Publish the approved tickets. **How** depends on the tracker `/setup-matt-pocock-skills` configured; the tickets are the same either way, only the shape of the changes:
- **Local files** → write one file per ticket under `.scratch/<feature-slug>/issues/<NN>-<slug>.md`, numbered from `01` in (blockers first). Each file's "Blocked by" lists the numbers/titles it depends on. Use the per-ticket file template below: one ticket per file, never a single combined file. - **A real issue tracker (GitHub, Linear, …)** → publish one issue per ticket in (blockers first) so each ticket's can reference real identifiers. Use the platform's native blocking / sub-issue relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. Apply the `ready-for-agent` triage label unless instructed otherwise; the tickets are agent-grabbable by construction.
Work the **frontier**: any ticket whose blockers are all done. For a purely linear chain that means top to bottom.
Do NOT close or modify any parent issue.
<local-ticket-template>
# <NN>: <Ticket title>
**What to build:** the end-to-end behaviour this ticket makes work, from the user's perspective, not a layer-by-layer implementation list.
**Blocked by:** the numbers/titles of the tickets that gate this one, or "None (can start immediately)".
**Status:** ready-for-agent
- [ ] Acceptance criterion 1 - [ ] Acceptance criterion 2
</local-ticket-template>
<issue-template>
## Parent
A reference to the parent issue on the tracker (if the source was an existing issue, otherwise omit this section).
## What to build
The end-to-end behaviour this ticket makes work, from the user's perspective, not layer-by-layer implementation.
## Acceptance criteria
- [ ] Criterion 1 - [ ] Criterion 2
## Blocked by
- A reference to each blocking ticket, or "None (can start immediately)".
</issue-template>
In either form, avoid specific file paths or code snippets: they go stale fast. Exception: if a prototype produced a snippet that encodes a decision more precisely than prose can (state machine, reducer, schema, type shape), inline it and note briefly that it came from a prototype. Trim to the , not a working demo, just the important bits.
# 转化为工单(To Tickets)
把计划、规格或对话拆成一组**工单(tickets)**:每张工单都是 tracer-bullet 式的垂直切片,并声明哪些工单会**阻塞**它。
issue tracker 与 triage label 词汇应该已经提供给你。若没有,请告诉用户运行 `/setup-matt-pocock-skills`。
## 流程
### 1. 收集上下文
利用对话上下文中已经存在的内容开展工作。如果用户以参数形式传入了一个引用(规格路径、issue 编号或 URL),请获取它,并阅读其完整正文与评论。
### 2. 探索代码库(可选)
如果你尚未探索代码库,就先探索,以理解代码的当前状态。工单标题与描述应使用项目领域词汇表中的用语,并遵守你所触及区域内的 ADR。
寻找对代码进行预重构(prefactor)的机会,让后续实现更容易。“先让变更变得容易,再完成这个容易的变更。”
### 3. 起草垂直切片
把工作拆成 **tracer bullet** 工单。
<vertical-slice-rules>
- 每个切片都要以狭窄但完整(COMPLETE)的路径贯穿每一层(schema、API、UI、tests):它必须是垂直切片,而不是某一层的水平切片 - 每个已完成切片都能独立演示或验证 - 每个切片的规模都应适合放进一个全新的单次上下文窗口 - 任何预重构都应先完成
</vertical-slice-rules>
为每张工单给出它的**阻塞边(blocking edges)**:即在它开始前必须先完成的其他工单。没有阻塞项的工单可以立即开始。
**大范围重构是垂直切片的例外。** **大范围重构(wide refactor)**是一次机械性变更(如重命名一列、重新定义共享符号的类型),其**爆炸半径(blast radius)**扩散到整个代码库,导致一次编辑同时破坏数千个调用点,因而没有任何垂直切片能以绿色状态落地。不要强行把它变成 tracer bullet;应按**扩展—收缩(expand–contract)**排序。先扩展:在旧形式旁加入新形式,确保不破坏任何内容。然后按爆炸半径确定批次(每个包或每个目录),逐批迁移调用点;每个批次都是独立工单并被扩展步骤阻塞。由于旧形式仍在,各批次之间 CI 保持绿色。最后收缩:当没有调用方再使用旧形式时删除它;该工单被所有迁移批次阻塞。如果连单独批次也无法保持绿色,仍保留这个顺序,但让它们共享一个集成分支,并共同阻塞最终的“集成并验证”工单;只有在那里才承诺绿色。
### 4. 向用户提问确认
将建议的拆分方案以编号列表呈现。每张工单都要显示:
- **标题(Title)**:简短的描述性名称 - **被哪些工单阻塞(Blocked by)**:哪些其他工单(如有)必须先完成 - **交付内容(What it delivers)**:这张工单打通的端到端行为
询问用户:
- 粒度是否合适?(过粗 / 过细) - 阻塞边是否正确:每张工单是否只依赖真正构成前置门槛的工单? - 是否应进一步合并或拆分某些工单?
反复迭代,直到用户批准该拆分方案。
### 5. 把工单发布到已配置的 tracker
发布已批准的工单。具体**方式**取决于 `/setup-matt-pocock-skills` 配置的 tracker;工单内容本身相同,只有阻塞边的表示形态不同:
- **本地文件** → 在 `.scratch/<feature-slug>/issues/<NN>-<slug>.md` 下为每张工单写一个文件;从 `01` 开始按依赖顺序编号(阻塞项优先)。每个文件的 “Blocked by” 列出其依赖的编号/标题。使用下面的单工单文件模板:每个文件一张工单,绝不写成一个合并文件。 - **真实 issue tracker(GitHub、Linear 等)** → 按依赖顺序逐张发布 issue(阻塞项优先),这样每张工单的阻塞边就能引用真实标识符。平台有原生阻塞关系或子 issue 功能时就使用它;否则把每张工单的 “Blocked by” 设为对应的阻塞 issue。除非另有指示,否则应用 `ready-for-agent` triage label;这些工单按构造就是 agent 可直接领取的。
处理**前沿(frontier)**:所有阻塞项均已完成的任意工单。对于纯线性链,这意味着从上到下依次处理。
不得关闭或修改任何父 issue。
<local-ticket-template>
# <NN>: <工单标题>
**要构建的内容:** 从用户视角描述这张工单打通的端到端行为,而不是逐层罗列实现。
**被哪些工单阻塞:** 真正构成这张工单前置门槛的编号/标题,或 “None (can start immediately)”。
**状态:** ready-for-agent
- [ ] 验收标准 1 - [ ] 验收标准 2
</local-ticket-template>
<issue-template>
## 父项(Parent)
tracker 上父 issue 的引用(如果来源是已有 issue;否则省略本节)。
## 要构建的内容(What to build)
从用户视角描述这张工单打通的端到端行为,而不是逐层实现。
## 验收标准(Acceptance criteria)
- [ ] 标准 1 - [ ] 标准 2
## 被哪些工单阻塞(Blocked by)
- 对每张阻塞工单的引用,或 “None (can start immediately)”。
</issue-template>
无论采用哪种形式,都应避免具体文件路径或代码片段:它们很快就会过时。例外:如果原型产出的片段能比文字更精确地表达一项决策(状态机、reducer、schema、类型结构),则可将其内嵌,并简要注明它来自原型。只保留决策信息密集的部分,不要放入可运行的 demo,只留下关键内容。

Entry SHA-256: 5c9fba69845c2519b9b35b9af42ae5142c21f8ca15ac2123dc2722002c8058ae

2. Why This Skill Is Clever

一句话核心机制

把计划或规格编译成一张可执行的依赖图:节点是能独立演示的 tracer-bullet 垂直切片,边是真正阻塞开工的前置关系,并在发布前保留一次用户审批门。

触发与调用边界

直接来源: frontmatter 的 disable-model-invocation: true 表示技能必须由用户显式调用。它接受三类输入:当前对话、规格路径,以及 issue 编号或 URL;如果拿到引用,必须读取完整正文和评论。

直接来源: issue tracker 与 triage label vocabulary 是硬前置条件。缺失时不得临时猜测平台和标签,而要让用户先运行 /setup-matt-pocock-skills。

支持性上下文: companion docs 把 /to-tickets 放在主构建链 grill-with-docs → to-spec → to-tickets → implement → code-review 中,并明确:若整个改动能在一个 context window 内完成,应跳过拆票直接使用 implement。

信息架构 / 执行顺序

  1. 复用或补齐输入: 优先使用现有对话;外部引用必须读完正文与评论。
  2. 校准工程现场: 必要时探索代码库,继承领域词汇并遵守相关 ADR。
  3. 先清理路径: 寻找能让后续变更更容易的 prefactoring,并把它排在前面。
  4. 建立节点: 每张工单必须窄而完整、可独立演示或验证,并能放进一个全新 context window。
  5. 建立边: 只声明真正阻塞开工的 blocking edges;无阻塞节点组成当前 frontier。
  6. 处理结构例外: 无法保持单片绿色的 wide refactor 改用 expand–contract 与迁移批次。
  7. 人类校验: 用户确认粒度、阻塞边以及合并/拆分方案;批准前不得发布。
  8. 按依赖顺序发布: 本地模式一票一文件,真实 tracker 一票一 issue,并尽量使用平台原生关系。

最巧妙的设计点

1. 它拆的不是任务清单,而是可验证的价值路径

“Each slice cuts a narrow but COMPLETE path through every layer”

直接来源: 技能用 COMPLETE 与 NOT a horizontal slice 两个强约束对抗最自然却最危险的分层拆票。schema、API、UI、tests 不是四张票,而是一张票要贯穿的路径。

解释: 这把“票是否够小”升级为“票是否形成闭环”。每张票都拥有自己的行为和验证证据,降低了跨票验收与集成末期爆雷的概率。

2. 用 fresh context window 定义 agent 原生的容量单位

“Each slice is sized to fit in a single fresh context window”

直接来源: 规模上限不是人类工时或 story point,而是一个没有先前会话记忆的新上下文窗口。

解释: 这是明显的 agent-first 设计。它迫使工单自包含:标题、交付行为、验收标准与依赖必须足以让陌生执行者接手,而不能依赖“大家都知道”的隐性上下文。

3. 把依赖从排序提示提升为图的不变量

“does each ticket only depend on tickets that genuinely gate it?”

直接来源: 用户不仅检查拆分粒度,还要检查每条边是否真的构成门槛。发布又按 dependency order 进行,保证后创建的票能引用已存在的阻塞项。

解释: 许多计划把依赖当装饰性字段;这里则让它控制并行度、发布顺序和 frontier。删除虚假边能释放并行性,补上缺失边能防止过早开工。

4. 规则有一个按拓扑失败模式设计的窄例外

“Wide refactors are the exception to vertical slicing.”

直接来源: 共享符号或列重命名可能让任何单独垂直切片都无法保持绿色。技能没有硬套主规则,而是明确 expand、分批 migrate、contract 的阻塞关系;必要时再加入集成分支与最终验证票。

解释: 好例外不是“自行判断”,而是另一套完整算法。这里先描述识别信号(blast radius 与无法单片绿色),再规定迁移阶段、边和绿色承诺发生在哪里,因此例外仍然可审计。

5. 在不可逆外部写入前设置结构化审批门

“Iterate until the user approves the breakdown.”

直接来源: 发布前必须用编号列表呈现标题、Blocked by 与 What it delivers,并询问粗细、边和合并/拆分;只有批准后才能写入 tracker。

解释: 这把人类注意力集中在三项高杠杆决策,而不是逐字审稿。它也遵循外部副作用边界:草稿可以自主生成,污染 tracker 前必须得到明确批准。

6. 同一语义模型适配两种存储后端

“the tickets are the same either way, only the shape of the blocking edges changes”

直接来源: 本地 tracker 以一票一文件和文本 Blocked by 表达边;真实 tracker 则优先原生 blocking / sub-issue 关系。两者都按阻塞项优先发布。

解释: 技能把领域模型(票与边)和持久化适配器(文件或平台 API)分开。这样迁移 tracker 不会改变拆票算法,只改变边的编码方式。

防失败机制与显式不变量

直接来源:

取舍、可移植风险与宿主假设

解释: 垂直切片降低集成风险,却可能重复触碰相同层并增加部署开销;single fresh context window 也不是稳定的复杂度单位,会随模型、工具和上下文容量变化。阻塞图依赖执行者正确识别“相关”与“真正 gate”的差异,错误边会压低并行度或制造返工。技能还假设宿主能读取完整 issue、探索代码库、找到 glossary/ADR,并通过 tracker API 创建原生 blocking 或 sub-issue 关系。

支持性上下文: companion docs 记录了两个现实缺口:某些运行会把 GitHub 原生关系退化成正文文本,或没有把工单挂成父 issue 的 sub-issue;本地单文件方案也曾因并行写入发生竞争,后来改成一票一文件。这些是实现适配器的风险,不改变所选源文件定义的票与边模型。

可迁移原则

  1. 用可独立观察的结果拆工作,不用组织架构或技术层拆工作。
  2. 把依赖建模成可验证的边。 每条边都要回答“没有它,真的不能开始吗?”
  3. 选择与执行者匹配的容量单位。 对 agent 来说,fresh context window 比人类工时更贴近真实限制。
  4. 给主规则设计有识别条件、有步骤、有承诺点的窄例外。
  5. 先审批结构,再执行外部副作用。 人类只需盯住粒度、边和交付行为。
  6. 领域模型与存储适配器分离。 节点与边不随 GitHub、Linear 或本地文件而改变。

今日实践题

假设“把用户 ID 从整数迁移为 UUID”会横跨 schema、API 和数百个调用点:你会如何判断它应拆成普通 tracer bullets 还是 expand–contract,并画出最小的阻塞图?

Evidence note

本文关于触发方式、输入、前置配置、上下文读取、代码库探索、垂直切片、blocking edges、wide-refactor 例外、用户审批、tracker 发布、模板和禁止修改父 issue 的结论均来自所选 SKILL.md。关于主构建链、跳过条件、已知 GitHub 原生关系缺口及本地并发写入历史来自 docs/engineering/to-tickets.md,只作为支持性上下文。关于 agent-first 容量单位、图并行性、外部副作用边界和适配器分层的判断属于设计解释。

Source & provenance
Repository path
skills/engineering/to-tickets/SKILL.md
Generated
2026-08-24 08:24 Asia/Shanghai
Upstream commit
5b15a47f2d7150f545fbcacbfe381787fc0230dc
Source
View on GitHub