---
name: convention-ticket
type: convention
description: >-
  How work is written and run as tickets in Aled's system, independent of the
  tracker — the standard ticket shape, the capture-then-refine-then-ready
  discipline, the evidence-first capture format, Pattern A folded validation, the
  hierarchy/milestone/dependency model, the triage→build→review→gate routing
  pattern, the execution lanes, and the decision-log entry format. Read whenever
  creating, refining, routing, or validating a ticket, or designing how work
  moves. These conventions hold even if the tracker changes; convention-linear
  maps them onto Linear's teams, states, and labels. Grown as the discipline
  solidifies.
---

# Ticket and work conventions

How work is captured, written, validated, and moved — stated independently of any one tracker, so it survives a change of tool. `convention-linear` is the tool layer: it maps every concept here onto Linear's teams, states, and labels. If those two ever disagree on a tool detail, Linear wins for the mechanics; this document wins for the discipline.

The tracker is the **system of record**: not the chat thread, not memory, not project files. If a decision, a piece of state, or a task is not in the tracker, it does not persist. Keep it clean — everything downstream trusts it.

## The lifecycle of a ticket

Work moves through three gates regardless of tracker:

* **Captured, not yet ready.** A new ticket the operator has not refined is *captured* — it needs refinement before it can be actioned. New tickets land here by default, never in a ready-to-action state. A captured ticket that raises questions carries a notes section listing them (see the standard shape).
* **Refined, parked for the operator.** A ticket that has been shaped and is awaiting the operator's promotion, or his answer to an open question. This is the **human refinement gate**. Agents treat refined-and-parked tickets as read-only, with one exception: a ticket where the operator has commented since the last agent comment is a signal to act — process those comments as trusted instructions, apply the answers, confirm, and promote when he says ready.
* **Ready.** Shaped, unblocked, and explicitly cleared to action. Only the operator moves a ticket to ready unless he has said otherwise.

Never advance a ticket to ready on the operator's behalf. Capture freely; promote deliberately.

## The standard ticket shape

A ticket body uses a **fixed section skeleton with exact heading text**, the way `convention-artefact-format` fixes the shape of a canon artefact. A fixed skeleton is what lets a human and a skill read any ticket the same way — find the objective, find the acceptance checklist — regardless of project or type. Loose headings drift (`Done when`, a merged `Body / acceptance criteria`, a bare `Acceptance`), and drift taxes every read and write and breaks the Definition-of-Done gate, which needs the acceptance block in one predictable place.

The canonical sections, in order, with their exact headings:

* **Title** — action-prefixed and specific (e.g. `PROPOSAL: …`, `SCOPE: …`, `FIX: …`, `DRIFT: …`, `CAPTURE: …`). The prefix signals the kind of work at a glance.
* **`## Objective`** — one or two sentences on what the ticket is for.
* **`## Why this matters`** — brief context, especially how it connects to other work.
* **`## Acceptance criteria`** — the specific, testable conditions that make *this* ticket correct, unique to it. For any ticket an agent will execute, a Pattern A checkbox list under exactly this heading, so the DoD gate and skill-qa / skill-merge can locate it. Omit only for a ticket that genuinely has no executable acceptance (e.g. a pure decision ticket).
* **`## Definition of done`** — the standing shipping bar for *every* ticket of this type, pasted from the type template (see *Per-type ticket formats* below) at refine. Also a checkbox list. Both this and the acceptance list must be fully ticked (or struck with a one-line waiver) before Done or merge.
* **`## Notes`** — open questions and decisions for the operator, included only when the brief is not fully defined. This is what makes a captured ticket refinable rather than vague.
* **`## On completion`** — usually "refine with the answers above, then move to ready."

Pin the acceptance heading as exactly `## Acceptance criteria`. Retire the drifted variants (`Done when`, the merged `Body / acceptance criteria`, the bare `Acceptance`).

**Minimal variant — trivial and auto-captured tickets.** A trivial or machine-captured ticket (e.g. a GTD capture) is not forced into the full skeleton. The permitted minimal shape is `## Summary` plus whatever the capture carries (`## Actions`, `## Notes`, `## Classification`). Defining this variant is what keeps drift from filling the gap: a ticket is either the full skeleton or the named minimal variant, not a free-form third thing.

**Capture format — the evidence-first capture shape (a third named shape).** An editorial or work *capture* — a decision, moment, breakthrough, or opinion recorded while the thread is fresh — uses its own named shape, neither the full skeleton nor the minimal variant. It exists to preserve **what happened**, sourced from the originating thread, before any interpretation is layered on. Naming it here is what keeps the next capture consistent rather than improvised. Title prefix **`CAPTURE:`**.

* **Produced from the thread, not authored from memory.** The capture is *extracted by the capturing task from the source thread or transcript* — sourced, not invented — and written in the subject's first person because it records their work. The operator reviews and corrects; he does not author it. Where the thread does not evidence a section, **name the gap** ("not in the thread") rather than filling it.

* **The evidence spine** — the record of the event, in order:
  * `## What happened` — the moment: the decision, the thing done. **Mandatory. A capture without it is not a capture, it is an untethered opinion.**
  * `## Why it happened` — the trigger and the stakes: what forced it, why it mattered.
  * `## How it happened` — the mechanism: the approach, the actual moves.
  * `## Decisions and trade-offs` — **the judgement layer, and the highest-value section.** The real forks: the options considered *and rejected*, what was traded off against what, and the reasoning behind the call. Naming the road not taken is where taste shows — a chosen path with no visible alternative reads as luck, not judgement. Same discipline as an ADR (`skill-architecture-spike`: options with trade-offs, recommendation, and why), folded into a capture. In an AI age execution is cheap, so the decisions and trade-offs are the differentiator and the readable evidence of taste — and the easiest section to fabricate, so it is the one to flag rather than fill when the thread is thin.
  * `## What it led to` — the outcome: what changed as a result, and any evidence of impact.

* **The interpretation layer** — sits *on top of* the spine, never in place of it:
  * `## The conviction` — the argument it suggests.
  * `## Design lens` — why it belongs in a Guide to Design (where the capture feeds Mr P).
  * `## Evidence status` — is there a `tc.*` yet; gaps named, not filled.
  * `## Format` — piece or note (+ any derivative).

* **Label lifecycle.** A capture carries the `capture` classification label while parked so the monthly aggregation re-reads it; the label is dropped when the capture is promoted (fleshed into a stated position), to keep the aggregation signal clean.

The spine maps directly to the Mr P piece shape in `convention-mrp-voice` — why = the stakes, how + decisions/trade-offs = the design move and the judgement, outcome = the proof, conviction = the flag — which is why extracting it at capture (while the thread exists) seeds the fuller `tc.*` later rather than forcing a reconstruction from memory. `task-content-drain` and `convention-mrp-voice` reference this format rather than restating it.

Set priority and classification per the tracker's taxonomy. Assign the operator **only** when the ticket carries a genuine question, decision, or verification that is his to make — never as a blanket parking gate, or "assigned to him" stops meaning anything. Any comment that assigns him must @mention him and lead with the specific action needed. Where such a comment hands him something to *do*, write it as novice-friendly numbered steps — see *Writing an instruction the operator has to action* below.

## Acceptance criteria vs definition of done

Two distinct things, split so responsibility for each is clear:

* **Acceptance criteria (AC) — per ticket.** The specific, testable conditions that make *this* ticket correct. Written fresh for each ticket.
* **Definition of done (DoD) — per type.** The standing shipping bar for *every* ticket of a type (all bugs, all features). Rule of thumb: if it would be true of the next ticket of this type, it is DoD; if it is unique to this one, it is AC.

Both live in the ticket as their own labelled checkbox section — `## Acceptance criteria` (written fresh) and `## Definition of done` (pasted from the type template) — and **both must be fully ticked, or struck with a one-line waiver, before Done or merge**. This is the two-section gate: splitting them stops a per-ticket condition and a standing shipping condition sharing one list where nobody owns which (worked case: A1-111, where "root cause stated" sat in the same checklist as "PR opened" and the whole list fell through to the operator as a manual waiver).

**Responsibility.**

* **AC** — written by the author (the operator or triage at refine, never the builder); ticked by the builder as built; verified by review (ticked *and* genuinely met); gated by the merge leg, then the operator.
* **DoD** — standing per type in this convention, pasted by the author at refine; ticked by the builder; verified by review; gated by the merge leg.
* The builder never authors AC. Review never ticks a box on the builder's behalf — a blank or unmet box is a changes-needed bounce, not a clean pass. A box the sandbox cannot verify is a blocker for the delivery manager to re-route, not an ask to the operator (see `skill-qa`). The operator waives an individual box with a one-line waiver.

## Per-type ticket formats

Every type shares the common spine — `## Objective`, `## Why this matters`, `## Notes`, `## On completion` — and adds the sections below. The `type:` label selects the template (bound in `convention-linear`). At refine the author instantiates the ticket from its type template: paste the type DoD and confirm the AC are present before promoting to ready.

* **`type:epic`** — adds `## Child scope`. No AC. DoD = all children Done and the outcome met. Closed by the operator, never claimed by the builder.
* **`type:feature`** — adds `## Scope`. AC = the capability delivered. DoD = child tasks Done, end-to-end verified on preview, decision log updated.
* **`type:story`** — AC = the user-visible behaviour. DoD = PR opened, CI present/concluded/green, runtime-verified, no secrets, review verdict posted.
* **`type:task`** — AC = the technical conditions. DoD = PR against the right repo, CI present/concluded/green, typegen/build where relevant, no secrets, review verdict posted.
* **`type:bug`** — adds `## Problem` (repro, expected vs actual, evidence) and `## Already ruled out`. AC = root cause stated and fix applied. DoD = regression guard added, verified at runtime not by a green build, root cause recorded, cross-surface impact flagged, PR + CI.
* **Design variant** (a `work:` on a task or story, routed to the design agent) — adds `## References`. AC = the screens, states, and decisions. DoD = harvest-ready (node-id, reference asset/SVG, all states, interaction/motion spec); verified by design-parity.
* **Human / operator variant** — no AC if it is a pure decision. DoD = decision recorded (decision log where consequential) and downstream tickets updated. Carries `human` or `operator-<venture>`.
* **Capture variant** — uses the evidence-first *capture format* above rather than the AC/DoD skeleton; it is a record, not executable work, so it carries no acceptance checklist. Its "done" is the operator's review-and-correct and promotion (or parking).

## Writing an instruction the operator has to action

When a comment hands the operator something to *do* — a command to run, a setup step, a manual verification — how it is written matters as much as what it asks. These instructions are the one place an agent hands work directly to him, and the audience is a non-developer, so write them for a novice:

* **Numbered steps, one action per line** — never a run of actions jammed into a prose sentence.
* **Spell out every command literally, in a code span** (`` `npm run typegen` ``) — never describe a command in words he then has to translate into what to type.
* **Separate a decision to make from a thing to type.** A "confirm X?" and a "run this" are different asks; don't fold them into one line.
* **Assume no command-line fluency** — say where to run it and what a good result looks like.

Lead with the specific action or decision needed, then lay the steps out this way. This governs *how readably* the ask is written; the "lead with the action" rule governs *what* the ask is. (`convention-linear` and `skill-triage` bind this to Linear comments and point here rather than restating it.)

## Pattern A — folded validation

The core working discipline, non-negotiable:

**Review criteria live inside the build ticket as two literal checkbox sections — `## Acceptance criteria` (per ticket) and `## Definition of done` (per type). There are no separate review tickets.**

The checkboxes are not decorative — they are the **record of completion**, and ticking them is the gate:

* **Definition of Done = every box in *both* the `## Acceptance criteria` and `## Definition of done` sections ticked** (`- [x]`), or struck through with a one-line waiver where a criterion is deliberately not met. A ticket may not reach Done or be merged while any acceptance or definition-of-done box is unticked without a recorded waiver.

When an agent finishes a ticket:

1. It checks its work against each embedded criterion and **ticks every box it satisfies** when opening the change for review. Any box it cannot satisfy is left unticked and called out, not quietly skipped.
2. It moves the ticket to the review state (leaving the assignee per the routing pattern below).
3. It does **not** mark the ticket done. Sign-off is human, always.

Review verifies each box is both ticked and genuinely met, and names any blank or unmet box — "definition of done confirmed" means every box ticked, not a general thumbs-up. The merge gate refuses to ship a ticket with any box still unticked and unwaived, surfacing it as not-ready rather than merging. The operator reviews against the embedded criteria and closes it himself. This keeps the human queue showing only actionable work — review is a state transition, not a separate piece of work. When writing a ticket an agent will execute, embed the acceptance criteria so this works.

## The routing pattern — triage → build → review → gate

Agent-executable work runs as a loop across four roles. The roles are tool-agnostic; `convention-linear` binds them to label values and states.

* **Triage** — reads the ticket, sets classification and links, decides whether it needs execution. If it does, routes it to the builder and moves it to ready with the acceptance criteria stated. Where a human decision is required, leaves it for the operator. Triage owns the **post-review merge gate**: it ships flagged work only on the operator's explicit approval signal, and unflagged clean-pass work on the loop's own routing-to-merge signal (see Gate).
* **Build** — claims a ready ticket (status is the lock — a ticket in progress is never re-grabbed), implements against the criteria, opens the change for review, and **never ships it itself**. Hands to review.
* **Review** — reads the change against the embedded criteria and writes a plain-language verdict the operator can act on without reading the work: if clean, what was done plus confirmation it meets the definition of done; if not, everything the builder needs. Does not ship or change state; hands the ticket to the operator's queue.
* **Gate** — the operator, on work that needs human judgment. Either raises follow-ups (a bounce back to ready re-triggers the builder on the same ticket) or approves. **The operator gates work needing human judgment; a clean-pass agent ticket auto-merges.** A review verdict that flags a human need — a design/visual side-by-side, product/UX sign-off, or an acceptance criterion review cannot verify — routes to the operator and ships only on his explicit approval (review finishing is not approval). A clean technical pass with no human-gate flag and genuinely green CI is merged by the loop without waiting for him — the routing-to-merge signal *is* the approval for unflagged work. The gate is by work-type, not universal. (Decision 2026-06-30, APP-402; supersedes the earlier "nothing ships without his approval" blanket. The critical safety condition — CI verified to have actually run, not a false green — lives in `skill-merge`.)

One builder per repo at a time (changes would otherwise conflict); tickets run serially in priority order. An epic is never claimed by the builder — only leaf tickets are.

## Structure — hierarchy, ordering, dependency

Three orthogonal axes carry how work is organised. Keep them distinct; conflating them is what makes a board hard to read.

* **Hierarchy = grouping.** An epic is an outcome held as a parent; it is never executed directly and is closed by the operator when its children are done. Features and stories are capability slices under an epic. A task is the executable leaf. The builder only ever claims leaves.
* **Ordering = phases.** Strategic sequence — platform order, phase order, "do this before that" — lives in milestones/phases and in the operator's promotion to ready. Every refined leaf gets a phase where the project has them. **Milestone or epic? A sequential, gated delivery stage is a milestone, not an epic.** Milestones carry time-ordered phases ("phase 1 → phase 2 → phase 3"); epics group a body of work regardless of when it runs. Reaching for `type:epic` to represent delivery phases forces a create-then-convert cleanup later (worked case: a1.firmup, four delivery phases first modelled as epics then converted to milestones, 2026-06-27). So: phases → milestones; bodies of work within a phase → epics.
* **Dependency = genuine blocker only.** A blocked-by relation means the work technically cannot start until the blocker is resolved (shared files that would conflict, an artefact that must exist first, a decision that gates scope). Never use a dependency to express a sequencing *preference* — that is what ordering is for. A spurious block silently delays delivery; the builder skips blocked tickets rather than stalling.

**Holding state for a blocked leaf — sequencing dependency vs harvest dependency.** A refined leaf carrying a dependency is placed by a single discriminator: **is its spec complete, or is its spec determined by the blocker's output?**

* **Sequencing dependency — spec complete, only ordering outstanding.** The leaf is **ready**: it goes to **Todo** carrying a blocked-by relation, never parked in the refinement gate. The builder's skip-blocked behaviour is the auto-flow — the loop skips it while the blocker stands and picks it up automatically when the blocker clears, with no manual re-promotion.
* **Harvest dependency — spec determined by the blocker's output** (the design→build case: the build leaf cannot be specified until the design leaf resolves the node-id, asset, states, and interaction). The leaf is **not ready**, because its brief does not yet exist. It is held in the refinement gate under the **triage marker** (not an agent executor), and triage re-checks it each pass, **harvesting** the blocker's resolved output into the leaf's body when the blocker completes — then promoting it. See *harvest* below.

**Invariant: an agent executor never rests in the refinement gate.** The refinement gate holds only the triage marker (a harvest leaf awaiting its source) or the human-owned marker (a leaf awaiting the operator's input). A leaf carrying a build executor belongs in Todo — ready, or ready-carrying-a-blocked-by — never parked in refinement. An agent-executor leaf sitting in the refinement gate is a **blind spot**: the triage poll scans the triage marker and the builder scans Todo, so neither sees it, and it stalls silently (worked case: four refined build leaves parked in refinement for two weeks, invisible to both, 2026-06-30, APP-400). Treat an agent executor in the refinement gate as an anomaly to correct, the mirror of the triage marker lingering in Todo.

**Harvest — lifting a completed blocker's output into the dependent leaf.** A blocker reaching a done state does not by itself make a harvest-dependent leaf ready: the blocker's *output* must be lifted into the leaf's body so the leaf becomes a self-contained brief the builder can run without reading the closed ticket. For a design→build dependency this means the node-id, the reference asset/SVG, the states, and the interaction/motion spec are written into the build leaf, *then* it promotes. Harvest is an explicit triage step owned by the re-check of the refinement gate, not something that happens automatically on the blocker's state change. Correspondingly, a producing leaf (e.g. a design leaf) is **not done until its output actually exists to harvest** — a definition-of-done that names an artefact by label alone, with no node-id, asset, states, interaction spec, or resolved purpose, is not met, and marking it done leaves nothing to harvest and a misleading closed ticket.

## The execution lanes

Work runs in parallel lanes that must not collide:

1. **Code in a repo** — one agent at a time per repo; tickets run serially in priority order.
2. **The operator in content/design surfaces** — runs in parallel (different surface, no conflict).
3. **A separate spec/design session** — runs alongside both (MCP-dependent work).

When sequencing, mark genuine cross-lane dependencies explicitly and mark what can run in parallel. Never queue two code tickets in the same repo as simultaneously actionable.

## The decision log

Every consequential architectural or scope decision is recorded in the project's decision log, newest-first. Its structure, entry format, and lifecycle (one entry per decision; update rather than leave a stale one) are canon in `convention-decision-log`; `convention-linear` maps it onto Linear documents.

## Canon and not fabricating

* **Never edit canon silently when the change is consequential.** Detecting drift or proposing a change → raise it for the operator. Canon changes are human-owned. Mechanical, agreed updates are fine; judgement calls are not.
* **Don't fabricate or infer.** If a fact is missing, flag the gap rather than fill it. For anything describing real work, fetch the authoritative source rather than relying on memory of a past session. This is the same discipline the *capture format* enforces at the point of capture: record what the thread evidences, name what it does not.
* **Don't hard-code a linked ticket's current state in a body.** When a ticket body references *another* ticket's state to gate a future action, write it as a status-conditional to re-check when the ticket is actioned ("if A1-109 is still open, re-harvest its spec; if it has since gone Done, raise a re-point follow-up"), never as a fixed assertion ("A1-109 is Blocked, re-harvest after this lands"). A hard-coded state goes stale silently the moment the linked ticket moves, so any instruction predicated on it can be wrong by the time it is read (worked case: A1-127 instructed a re-harvest of A1-109 that had already shipped Done, 2026-07-02). This is the authoring-side mirror of the triage rule that judges a blocker by its own status, never the relation or the prose: triage re-fetches any linked ticket a body's instruction depends on before acting on that instruction.

## Tone

Everything written into a ticket or doc follows `cos.tov`: calm, precise, structurally confident, sentence case, no exclamation marks, no marketing-speak, outcome before adjective, British spelling.

## Quick checklist before creating or updating a ticket

- [ ] Captured (not ready) unless the operator has refined it?
- [ ] Fixed section skeleton with exact headings (`## Objective`, `## Why this matters`, `## Acceptance criteria`, `## Notes`, `## On completion`) — or the named minimal variant for trivial/auto-captured tickets, or the evidence-first **capture format** (`CAPTURE:` prefix, `## What happened` mandatory) for a capture?
- [ ] Instantiated from its `type:` template — the per-type section skeleton plus the standing `## Definition of done` pasted in — with the per-ticket `## Acceptance criteria` written fresh (see *Per-type ticket formats*)?
- [ ] For agent tickets: both `## Acceptance criteria` and `## Definition of done` checkbox lists present (Pattern A), each box to be ticked as the record of completion?
- [ ] For a capture: evidence spine produced from the source thread (not memory), gaps named not filled, interpretation layer sitting on top of the spine?
- [ ] Definition of Done understood as every box in *both* sections ticked (or struck with a one-line waiver) before Done/merge?
- [ ] Notes section listing open questions, if the brief isn't fully defined?
- [ ] Assignee — operator only where a genuine human action is needed, not as a blanket gate?
- [ ] Hierarchy, ordering, and dependency set correctly and kept distinct? (Phases → milestones; bodies of work → epics; sequencing-blocked → Todo + blocked-by; harvest-blocked → refinement gate under the triage marker; no agent executor resting in the refinement gate.)
- [ ] Consequential decision made? → decision-log entry added.
- [ ] Tracker mechanics applied per convention-linear?
- [ ] Tone clean per `cos.tov`?
