How It Works
The board column is a contract boundary
Everything trkr does hangs off one idea: off-board is human territory, on-board is daemon territory.
- Off-board: intake, classification, grilling, spec-writing. A ticket only boards when it is "agent-sufficient" — a clear spec with independently checkable acceptance criteria and no open questions.
- On-board: the daemon owns the pipeline, with three human touchpoints. You start a ticket by dragging it out of the inert Backlog column into its first stage's working column (every preset's first stage is
manual); from there every transition is automatic. Mid-pipeline the only touchpoint is thetrkr:blockedlabel (pull-based — you come to it). At the end, the ticket lands in Done with your review requested on the PR — you merge.
Anatomy of a ticket
A ticket is a GitHub issue on a Projects v2 board with two single-select fields:
- Status — the built-in field; its options are trkr's columns.
- Workflow — a trkr-managed field naming which stage pipeline this ticket walks (
feature,bugfix,chore, …). Workflows are per-ticket, not per-repo.
Dependencies between tickets use GitHub's native blocked_by issue dependencies. The daemon's frontier query is simply "open blocker count == 0".
The stage loop
Each workflow is an ordered list of stages. A stage declares three columns: from (waiting), during (working), to (success). The daemon:
- Finds a ticket sitting in a stage's
fromcolumn with no open blockers. (manualstages — every preset's first — are the exception: the daemon waits for a human to move the ticket into the stage'sduringcolumn instead.) - Moves it to
duringand spawns a fresh agent session in the ticket's git worktree (.trkr/worktrees/<issue>), with the stage's prompt. - On success — including passing the stage's hard gate, a mechanical check that the stage's artifacts actually exist — moves it to
to.
Sessions carry no memory between stages. The issue thread and committed artifacts (docs/tickets/<n>-<slug>/) are the only state — except on retry turns, where the stage prompt also carries the ticket's failure history (below).
Turns and blocking
A turn is one full walk of the verification loop. A ticket gets one turn counter across all gates, and 3 turns max (configurable via max_turns). Gate failures with actionable findings kick the ticket back for another turn; the third failure applies the trkr:blocked label and stops. A human fixes the impasse and runs trkr unblock <issue>, which clears the turn counter and restarts the workflow.
Retry turns are guarded so the loop converges instead of thrashing:
- Error memory — every retry turn's stage prompt includes a "Prior failed turns" section with the findings from each earlier turn, plus an instruction to change only what the findings demand. Sessions can't repeat a fix that already bounced.
- Progress metric — the failing-findings count must fall between turns. If the same stage's gate bounces two consecutive turns with no improvement, the ticket blocks immediately (a
turn.plateauevent in the trace) rather than burning its remaining turns. - Fresh start on the final turn — the last turn's prompt tells the session to stop patching and regenerate the affected code from the spec if earlier patches kept failing.
Hand-off
Every workflow terminates in the review stage. When it passes (a committed review.md with VERDICT: CLEAR), trkr requests your review on the open PR — falling back to an @mention when GitHub refuses a self-request on a solo repo — records the CI check state in the trace, moves the ticket to Done, and removes the ticket's worktree. trkr never merges; the PR waits for you. If every linked PR is later closed without merging, the daemon labels the ticket trkr:blocked.