Skip to content

Tickets as contracts

A ticket is the whole specification the loop works from. Its body is frozen at claim, held against at merge, and read by a reviewer who can witness only what is in the candidate tree. Writing one well is most of the operator's job.

The shape

ticketTemplate.md is the canonical structure and ticket_template.py validates it. Sections, in order: an H1 title; Summary; What / Why / How (the bold keys **What:**, **Why:**, **How:**, plain What: also accepted); In scope (at most three entries); Out of scope; Acceptance criteria (at most five, each Given … when … then …); Verify command(s) (a fenced block of relative-path, non-interactive commands; exit 0 is pass); optional Contract checks (relative/path: exact literal); Implementation notes; Estimate & dependencies (Estimate: N min · Depends on: KO-n or none, 30 minutes at most); Open questions (exactly - None to be claimable).

Validate before filing, against the target repository:

python3 ticket_template.py TICKET.md --repo /path/to/target

Blockers make the ticket INVALID and the loop skips it as needs_spec. Advisories print and let it through.

What the validator refuses, and why

Refusal Why it exists
Unfilled placeholder: any <…> or {{…}} outside a markdown link KO-165 was claimed with template placeholders in its title and criteria and merged anyway. HTML tags count; write "the main element", not <main>.
More than three in-scope items, five criteria, or 30 minutes KO-110 was a 180-minute blob. Small tickets converge; big ones burn rounds.
A non-relative path in a verify command KO-111 cd'd to an absolute path and verified the wrong tree.
A path a criterion names that the target repository gitignores KO-166 named a rendered file under a gitignored artifacts/; the reviewer's export cannot contain it and the implementer force-tracked it.
A Depends on that is not a ticket id or none dependencies are machine-checked through Linear blocks relations
Open questions not exactly - None an open question is not a frozen contract

Advisories: a What: that chains two deliverables; a bare python3 in a verify command on a project with a venv; a criterion that reads as an operator or post-merge witness.

What the reviewer can witness

The reviewer sees a clean export of the candidate commit, read-only, and nothing else: not main after the merge, not the host it runs on, not a screen, not a store. Every criterion must be witnessable from that tree, and the reviewer must name the witness:

CRITERION 1: met — tests/test_serve.py::StatusTests::test_lists_live_runs
CRITERION 2: not met — production requests /runs, not /runs?limit=1
CRITERION 3: unwitnessed — no test exercises the 404 path

A named test that does not exist in the tree is unwitnessed. Any criterion not met or unwitnessed makes the round changes_requested whatever the verdict line says. So:

  • Write criteria as "a test witnesses this", and mean it.
  • Keep visual passes, re-renders of gitignored output, and "on the writer host" checks out of the criteria; they are operator steps, recorded in the ledger after the merge.
  • Verify commands must be true of the candidate, not of a fixture you imagined: a negated grep that also matches a legitimate line (---) and a plural that a one-item fixture cannot produce both failed real runs.

Filing and editing

python3 factory.py TARGET --file-ticket TICKET.md --priority high [--state Todo|Backlog]
python3 factory.py TARGET --file-ticket TICKET.md --update KO-n

Both validate the file against the target, act on Linear, read the stored body back and validate that again, so a transfer that rewrites bold or autolinks an example identifier is caught at filing time rather than at claim time. Exit 1 means nothing was changed; exit 2 means the issue exists but its stored body needs a fix. Todo and In Progress are claimable; Backlog and Done are not. [loop] order = "priority" makes an Urgent or High ticket run first.

The ledger

Every run leaves a comment on its ticket: the rounds, their findings, the adjudications (ADDRESS, FOLLOW_UP, DECLINE), and any operator step taken after the merge with its time. The store holds the rows; FINDINGS.md renders the window; the ledger is the narrative. A contract revision is recorded there too, with what was wrong and why, so a rerun's reviewer can read the history.

Tickets the loop must not take

Tracking and design tickets have no verify command by design and stay in Backlog. A parent stays Backlog while its leaves run. A ticket whose contract needs a human decision is Backlog until the decision is in the body.