| name | implementation-ticket-writer |
| description | The SECOND planner — turn the operator-approved design package (program-design) into implementation tickets one at a time, each carrying the MINIMAL atomic context that fits a weaker tier, with the exact io sub-skills attached by a deterministic router (io: type → skills). Use at stage 11, after Gate #1, to prepare per-module/per-slice tickets an implementer subagent executes independently (tests → module → green → mark done). Do NOT design module trees/contracts (program-design) or write code (program-implementation). Tier-agnostic: io-router table, a minimal-context checklist, STOP. |
implementation-ticket-writer — cut small-tier-sized implementation tickets
In: the operator-approved (Gate #1) design package from program-design — slices, module
trees, contracts with io:, unit-test formulas, Gherkin-mapping. Out: an implementation backlog
where each ticket is self-contained and fits a weaker tier — one ticket = one slice/module = one
implementer subagent. This is the second planner (docs/04_PLANNING_PIPELINE.md §7): the first
planner designs; this pass packages the design into minimal-context tickets. It writes tickets —
it does not design or implement.
Companion (read on demand): reference.md — the annotated ticket-body template,
the component-test @wip special rule, foundations. Read when you fill a ticket body.
Minimal-context principle (the whole point)
A ticket must carry only what its module needs, not the full design package. A small-tier-sized ticket
holds: the module contract (Input/Deps/io:/antecedent/consequent), the exact unit-test list (by
formula), the component scenario(s) it must green, the io sub-skill(s) for its io:, and its
dependencies — nothing else. If a ticket needs the whole package to be understood, it is too big —
split it (one module / one slice). Do not inline entire specs; link them and quote only the
module's own rows. Test of a good ticket: an implementer subagent with no other context can complete it.
io-router — attach skills by the module's io: (deterministic)
The io: field (set by program-design Step 5) is the sole routing key — no judgement:
module io: | Skills attached to the ticket |
|---|
none | program-implementation (+ program-design contract excerpt) |
http | + http-io |
llm | + http-io + llm-client |
queue | + queue-io |
db | + db-io + db-schema |
The implementer subagent receives exactly these — it selects nothing. A module with io: none
gets no io sub-skill.
The ticket's machine-readable skills: field carries exactly these io add-ons (the core
program-implementation/code-style/communication/memory is always loaded, so it is NOT listed).
By ticket type: scaffold → [service-scaffold]; component → [component-tests]; module →
the io-router row above (none → []). harness/validate-tickets.mjs enforces skills: exactly
equals the router output — neither missing nor extra. Over- or under-provisioning is a deterministic blocker before Gate #1.
io: http/queue are OUTBOUND only. They tag an autonomous Client/Publisher/Consumer the
service calls out to. The service's own inbound HTTP handler / ingress adapter is io: none —
never route http-io to it. If the module has no outbound call, its io: is none.
Content skill — by ARTIFACT discipline (orthogonal to the io-router)
The io-router covers outbound integration, but some tickets produce an artifact whose discipline is
not code. Attach the content skill by what the ticket writes — this is deterministic (by artifact),
not the implementer's discretion:
| Ticket produces | Content skill in skills: |
|---|
README.md / docs | documentation (+ md-formatting) |
code (.go, …) | (implicit program-implementation core — NOT listed) |
The repo README.md is NOT a ticket — it is a design artifact authored by @dijkstra in the
planning phase (spec → documentation → code); scaffold.sh preserves it and @fagan verifies it. Do
not cut a README ticket. This Content-skill rule is the defensive floor for any other doc-ticket
one might still cut: if a ticket's outputs include README.md/docs it MUST carry documentation
(+md-formatting). Content skills (documentation/md-formatting) and the ingress cli-io are
orthogonal to the io-router — validate-tickets strips them before the io-equality check and
hard-blocks a README-producing ticket whose skills: lacks documentation (poka-yoke, run 13-07).
Mandatory machine-readable header (MUST — the router's contract)
Every ticket file tickets/NN-*.md MUST start with a strict YAML front-matter header so the
orchestrator (izi) can route mechanically without reading the body. Use flow arrays [a, b]
(the harness YAML parser does not read block - lists). Missing/broken header = blocker at review
(harness/validate-tickets.mjs enforces it deterministically before Gate #1):
---
id: 05
type: module
slice: slice-02-catalog
blocked_by: [01, 02, 04]
inputs: [docs/design/slice-02-catalog/contracts.md, api-specification/openapi.yaml]
outputs: [internal/catalog/logic.go, internal/catalog/logic_test.go]
io: db
skills: [db-io, db-schema]
---
Rules: exactly one scaffold ticket, and it is id: 01 with blocked_by: [] (blocks all others);
every other ticket lists its real prerequisites in blocked_by, its real input paths in inputs, and
its real produced-artifact paths in outputs (izi passes exactly these — it does not compute them).
outputs: is required on every ticket and must be non-empty — the files the ticket produces; unlike
inputs these do not exist at Gate #1 (they are what the implementer writes), so the header only
declares them and the guardrail poka-yoke verifies their existence when the done.log marker is
written, not before. io: is required only for module. skills: is required on every ticket and
must exactly equal the io-router output for its type/io. The ticket body below the header
follows the template in reference.md.
Ordering & granularity
Canonical order (contract-first — MUST hold): component tests come from the specification,
BEFORE the modules and their unit tests. Order the backlog exactly:
- spec — OpenAPI/AsyncAPI frozen (stages 3–4; usually already done in the design package);
- scaffold — clone the stack template → runnable placeholder (
service-scaffold);
- component tests (RED) — realized from the designed scenarios, tagged
@wip (component-tests) — precedes every module ticket;
- module tickets — one per module-tree node (cutting formula below);
- wiring —
register.go (Deps + route) + mount in cmd/app/main.go (501 → live API — exposes the endpoint).
(No README ticket — the repo README.md is authored earlier by @dijkstra as a design artifact.)
The slice is then closed by the @fagan acceptance step (remove @wip + run build/unit/component green +
verify every TASK §DoD) — a pipeline step, NOT a ticket. No file-producing «final».
MUST NOT place the component-tests ticket after the module tickets — component tests are the
executable spec modules are built against (RED → GREEN), not a check written after unit tests.
RED-first is a blocked_by EDGE, not just list order (HARD). «Precedes» above is not enough: every
module ticket MUST carry the slice's component-test ticket in its blocked_by — directly, or
transitively through another module that already does. The component test is not a code prerequisite of
the module (the reverse is true: the test greens because of the module), so it feels wrong to list — but
RED-first requires this edge in the dependency graph so the RED test exists before any module is built.
validate-plan.mjs checks the edge (module → component), not the list position — omit it and the plan
is a blocker (this is exactly what bounces to @linger; wire the edge here and skip the round).
Anti-example (WRONG): module ticket blocked_by: [01] (scaffold only). RIGHT: blocked_by: [01, 02]
(scaffold + component), or [03] where 03 already carries 02.
Cutting formula (antecedent → consequent)
T = 1 scaffold + 1 component(RED,@wip) + N + wiring — N = module-tree node count. The repo README.md
is not a ticket (design artifact by @dijkstra). There is no file-producing final — @fagan's
acceptance closes DoD (green + verify), not a ticket. Dependency-order the module tickets (a module before
its consumers) via blocked_by; wiring blocked_by all modules.
One ticket = one contract (MUST): one antecedent P (Input+Deps) → one consequent Q
(Result<T,E>). Two P→Q in a ticket = ≥2 modules → split; "always changes together" is one module (one
responsibility), never a shared ticket. Acceptance verifies only the ticket's Q — unit tests by
formula (io-testable node) or the component scenario(s) it greens (pipe/io node, no units). Green /
@wip-removal is not a deliverable — it is the fixer's act (§6). wiring/README implement no module
logic; DoD-closure is @linger's acceptance step, not a ticket — there is no file-producing final.
Self-check (before handing to @mills — MUST; any No ⇒ fix first)
- Count ==
1+1+N+2 (scaffold+component+N+wiring+README); module tickets ↔ tree nodes bijective (no node unowned, none owning two).
- No module ticket carries >1 contract (>1 P→Q); no ticket bundles wiring+README+deploy (single-concern —
validate-plan blocks it).
- No file-producing
final — DoD-closure is @linger's acceptance step; wiring/README carry no module logic.
- No ticket delivers "all scenarios green" / "assemble the service"; green is nowhere a deliverable (fixer's, §6).
- Each ticket completes from only
{ticket + inputs} — self-contained, small-tier-sized.
STOP
- The design package is not operator-approved (Gate #1 not passed) → STOP.
- A module has no
io: field → STOP, back to program-design Step 5 (can't route).
- A ticket won't fit a weaker tier even after splitting to one module → STOP, report (module too big — a design smell, back to
program-design).
- Asked to design or to implement here → STOP (wrong role).
Definition of Done
One ticket per module/slice, dependency-ordered, each self-contained and small-tier-sized; every ticket has
io: + the router-attached skills (implementer selects nothing); the component-test ticket carries the
@wip acceptance, module tickets carry unit-by-formula; the backlog assembles the whole service →
hand off to program-implementation.