Contracts, Handoffs & Validation
trkr's ticket lifecycle borrows the verification architecture of long-horizon agent systems (notably Factory Missions): correctness is defined before code exists, every stage hands off through structured artifacts instead of prose, and the final judge never sees the source. The premise: verification density, not agent intelligence, is the bottleneck.
The validation contract
A spec's acceptance criteria are written as a validation contract — a numbered list of behavioral assertions with stable IDs:
VAL-1: `trkr new` with empty input exits 1 with a usage message
VAL-2: the created issue appears in the Backlog columnEach assertion is independently checkable — verifiable by a black-box tester running the artifact, without reading the source — and completed by exactly one ticket ("completes, not contributes to"). Where it lives:
- Intake tickets (
trkr new,trkr scope) carry a## Validation contractsection in the issue body. - Spec/diagnose stages commit
contract.md(VAL lines only) besidespec.md/diagnosis.md; the hard gate refuses the stage without it.
The review stage verifies each assertion and reports it as a CRITERION: pass|fail — VAL-n: … line; the validate stage exercises each one against the built artifact.
The structured handoff
The implement stage's implementation.md is a handoff, not a diary. The hard gate mechanically requires these sections:
| Section | Contents |
|---|---|
## Implemented | What was built, mapped to each VAL-n assertion |
## Left undone | In-scope work not finished, and why |
## Commands run | Verification commands with exit codes, verbatim |
## Discovered issues | Out-of-scope problems, BLOCKING — … / NOTE — …, never fixed inline |
Review cross-checks the handoff against the diff: an undisclosed gap is a BLOCKING finding.
Findings and the orchestrator
Review and validate print machine-readable findings:
FINDING: BLOCKING — src/poll.ts:42 — retry loop swallows the abort signal
FINDING: NOTE — src/poll.ts:60 — naming nitWhen a turn fails and turns remain, the orchestrator — a triage agent, on by default ("orchestrator": false disables) — sorts every BLOCKING finding into one of three buckets:
- retry — belongs to this ticket; the normal re-walk fixes it.
- fix ticket — real but out of scope; filed as a new boarded issue with the evidence quoted, optionally blocking the parent via a native
blocked_bydependency. - dismissed — not real; requires a specific written justification (≥ 20 characters — "low priority" is not one), posted on the ticket.
The orchestrator proposes; the daemon disposes. It can pull a human in early (block) but can never mark a verdict CLEAR, skip a gate, or grant extra turns. If its session fails for any reason, the deterministic re-walk/block behavior proceeds untouched.
The validate stage
With validate configured, every code workflow gains a terminal validate stage in a new Validate column — a black-box, user-testing validator in the Factory mold. Its isolation is mechanical, not prompt-deep: the daemon builds the artifact in the ticket's worktree (validate.build), then materializes a sandbox at .trkr/validate/<issue> containing exactly two things —
contract.md— from the committed artifact, or the issue body's## Validation contractsection as fallback;artifact/— the built outputs named byvalidate.artifacts.
The session runs in the sandbox, with no worktree, no diff, no implementer notes, and no issue thread. It installs the artifact the way a user would, exercises every assertion, and reports CRITERION lines plus a VERDICT. The daemon — not the validator — commits validation.md back to the ticket's branch as the paper trail. No contract, no build, or a missing artifact refuses the stage and feeds the turn loop; nothing runs against a partial sandbox.
{
"validate": {
"build": "pnpm build && npm pack",
"artifacts": ["*.tgz"]
}
}What stayed mechanical
Column moves, hard gates, turn budgets, plateau detection, the Blocked-on-a-human escalation, and no-auto-merge are unchanged and remain deterministic daemon code. The agents decide content (what to build, what a finding means); the daemon decides process.