to-tickets breaks a Spec, a plan or a conversation into Tickets. Each Ticket is a tracer bullet: a thin slice that cuts all the way through every layer (schema, API, UI, tests), so it can be demoed or checked on its own. Each slice also fits in one fresh context window.
Every Ticket declares its blocking edges: the other Tickets that must be done before it can start. A Ticket with no blockers can start at once.
When to use it
Use it right after to-spec, in the same session, before you clear the context window. You can also hand it a reference, like a Spec's Issue number, and it reads the full Issue and its comments.
When not to use it
Issues you didn't create, such as bug reports or feature requests from other people, go through triage instead. The reverse also holds: Tickets that to-tickets made are already agent-ready, so don't triage them.
It doesn't build anything. Once the Tickets exist, build them with implement, one at a time, or implement-spec for the whole Spec in one run.
You, to approve the breakdown. It shows you a numbered list with each Ticket's title, blockers and what it delivers. It asks whether the size feels right and the blocking edges are correct, and it keeps adjusting until you approve.
What it creates
On a real Issue tracker like GitHub, one Issue per Ticket, published blockers first, with native blocking links and the ready-for-agent label. When it splits a Spec's Issue, each Ticket becomes a sub-issue of it. On a local tracker, one file per Ticket under .scratch/<feature-slug>/issues/.
The Tickets that are ready to grab, because all their blockers are done, are called the frontier. to-tickets never closes or changes the Spec itself.
One exception to slicing: a wide refactor, like renaming a column that hundreds of queries use. It's split as expand–contract: add the new form beside the old, move the callers over in batches, then delete the old form. If even the batches can't stay green alone, they share one branch, and a final Ticket checks the whole change.
add the new form beside the old
move the callers over in batchesIf even the batches can't stay green alone, they share one branch
delete the old form
It's split as expand–contract
Going deeper
6 min read · Going deeper not read
Why it exists
Left alone, an agent builds big layers and checks nothing until the end. In January 2026 Matt wrote that it "just keeps going, building entire layers in isolation, without ever testing whether the critical path actually works." Then you get a huge mess to clean up.
The second problem is size. When a Spec's build spans several sessions, one session can't hold it. In his July 2026 tutorial Matt put the rule plainly: "Each one of these tickets is supposed to just be the size of a single context window or a single smart zone." His Skill page, updated in August 2026, gives the reason: each Ticket is sized for one fresh context window, because a session "that has never seen your spec will pick the ticket up".
How it works underneath
The tracer bullet is an old idea. Matt credits it to The Pragmatic Programmer. In a 2003 interview, its co-author Andy Hunt described the first version of an app as "one thin line of execution goes end to end", from the UI all the way to a database. You then add one feature at a time, each another thin line, and adjust your aim from what you learn.
Why layers fail with agents. A horizontal Ticket ships one layer. Nothing works until every layer has landed, and Matt's page says its acceptance criteria "have to reach into work that another ticket owns". A vertical Ticket ships one thin path through every layer, so it can be checked alone. It "owns everything it grades".
Citations (75)
From snapshot/skills/to-tickets/SKILL.md, line 9: “Break a plan, spec, or conversation into a set of tickets: tracer-bullet vertical slices, each declaring the tickets that block it.”
From snapshot/skills/to-tickets/SKILL.md, line 17: “If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.”
From snapshot/skills/to-tickets/SKILL.md, lines 31–33: “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”
Matt's to-tickets page (updated Aug 24, 2026): every ticket is a tracer bullet through every layer. From aihero.dev, 30 October 2026.
A horizontal Ticket
A vertical Ticket
ships
one layer
one thin path through every layer
can be checked alone
no
yes
A horizontal Ticket ships one layer.
The blocking edges are the real output. In Matt's words, "The edges are the point of the artifact." On GitHub they become native blocking links, not text.
Tracer bullet or prototype? The sources disagree here. Dave Thomas, in that 2003 interview, says "Prototypes are designed to be thrown away." Matt's January 2026 article says tracer bullets are what prototype automates, "answer questions with throwaway code." The current SKILL.md (the 2026-10-29 pin commit, the newest source) settles it for this Skill. Each Ticket cuts "a narrow but COMPLETE path through every layer", and "A completed slice is demoable or verifiable on its own." A prototype only lends a Ticket a snippet that "encodes a decision more precisely than prose can".
A real example
In his tutorial (2026-07-16), Matt ran to-spec and then to-tickets "into the same session, not going to change sessions here". It proposed three Tickets. Matt pushed back: "I think that these three slices are a little bit much. I actually think it can be done in one slice." He asked for one, and got one.
He then showed a real Spec he had implemented "a couple of days ago", with "11 sub issues" under it. Each was "a single context window session". The one he opened was "just pretty short": "Most of the acceptance criteria is already in the main spec".
One detail in the video is out of date. In the video, it "put the ticket in tickets.md". Matt's page (August 2026) says that single file "was a bug". The current SKILL.md writes one file per Ticket under .scratch/<feature-slug>/issues/, "never a single combined file".
Where it goes wrong
Too many Tickets. Matt's page: "Over-decomposition is the most reported problem with this skill, and many users see it." The fix is at the review step: ask it to merge Tickets. And if the whole change fits one context window, skip this Skill and go straight to implement.
Tickets cut by layer. In issue #595 (July 2026), a team reported a "26-ticket stack" with "~20 agent runs per closed ticket", and "roughly three quarters of those are repair-loop rework". They first blamed the template. After re-reading the Skill, the reporter wrote: "It's our misuse, and the rule we broke is already in your skill." Their Tickets were "the layers of a single pipeline". Every one of their ownership defects was "a criterion grading an artifact built by a sibling ticket", and three Tickets formed a loop where "layer A needs B, B needs A". Matt's test catches this at the review step: "what can I demo when this is done? A ticket with no answer is a horizontal slice."
Issue #595 as opened on Jul 16: a 26-ticket stack's rework, blamed at first on the acceptance criteria. From github.com, 30 October 2026.
Blockers written as text. In issue #513 (July 2026), the agent wrote "Blocked by: #346 as text in the issue body" and claimed that "The gh issue create command has no --blocks or --blocked-by flag." That's wrong. Matt's page answers: "It does: gh issue create --blocked-by 12,15." It adds: "Because the skill publishes blockers first, their numbers are always available at creation time." The current SKILL.md says to "Use the platform's native blocking relationship where it has one", and the Ticket template now leaves out "Blocked by" when the edges are native.
Issue #513: the agent wrote "Blocked by" as text and claimed gh had no --blocked-by flag. From github.com, 30 October 2026.
Clearing between the two steps. A very large Spec can come back cut short when the agent fetches it again. Matt's advice: "Run them in the same context window and the agent never has to fetch the spec back."
Expecting it to run the Tickets. "The skill stops at the artifact, and there is no auto-dispatch mode." Matt adds that implement "does not reliably close or check off the ticket when it finishes", so you update each Ticket's state yourself.
Try it
In your practice project (set it up first with keeping-track's Try it step if you haven't): in the same session as to-spec's Try it, run /to-tickets with that Spec's Issue number; don't clear in between. When it shows the numbered list, ask of each Ticket: "what can I demo when this is done?" Then approve, open the new Issues on GitHub, and check that each one is a sub-issue of the Spec, carries the ready-for-agent label, and shows its blockers as native links, not just text in the body.
From snapshot/skills/to-tickets/SKILL.md, line 40: “Don't force it into a tracer bullet; sequence it as expand–contract. First expand: add the new form beside the old so nothing breaks.”
From snapshot/skills/to-tickets/SKILL.md, line 40: “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.”
From snapshot/skills/to-tickets/SKILL.md, lines 44–48: “- 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”
From snapshot/web/aihero.dev/skills-to-tickets.md, line 115: “A horizontal slice ships one layer of the change. Nothing works until every layer has landed, and each ticket's acceptance criteria have to reach into work that another ticket owns.”
From snapshot/skills/to-tickets/SKILL.md, line 105: “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.”
From snapshot/web/youtube.com/watch-v-m6myodf0djm.md, lines 183–186: “it's kind of given us three tickets here. [12:42] So, three slices. I think that these three slices are a little bit much. [12:47] I actually think it can be done in one slice. [12:49] Do it in one slice instead.”
From snapshot/web/youtube.com/watch-v-m6myodf0djm.md, lines 192–194: “And you can see that this is the spec and underneath it has 11 sub issues. [13:18] So, 11 tickets underneath it. [13:21] And each of these tickets, so this is a very detailed spec, each of these tickets is a single context window session.”
From snapshot/web/github.com/mattpocock-skills-issues-595.md, line 240: “Answering this properly meant re-reading to-tickets in full rather than the two lines I quoted, and most of my report doesn't survive it. It's our misuse, and the rule we broke is already in your skill.”
From snapshot/web/github.com/mattpocock-skills-issues-513.md, line 33: “I included Blocked by: #346 as text in the issue body, which creates a mention/link, but GitHub Issues has no native first-class blocking relationship in its standard API — so there was nothing to call. The gh issue create command has no --blocks or --blocked-by flag.”
From snapshot/skills/to-tickets/SKILL.md, line 17: “If the user passes a reference (a spec path, an issue number or URL) as an argument, fetch it and read its full body and comments.”
From snapshot/skills/to-tickets/SKILL.md, line 63: “Use the platform's native blocking relationship where it has one; otherwise set each ticket's "Blocked by" to the blocking issues. If the source was an existing issue, make each ticket its sub-issue (tracker doc's operation). Apply the ready-for-agent triage label unless instructed otherwise”