Tickets & the inbox
The folder inbox is the second dispatch surface: drop a Markdown ticket and the daemon claims it by atomic rename, runs the agent in an isolated worktree, verifies, runs the diff-vs-spec critic, and opens a draft PR — the same pipeline the GitHub label loop feeds (see how it works).
Anatomy
A ticket is one file: YAML frontmatter between --- delimiters, then a Markdown plan
body sent to the agent as the prompt. Four flavors, selected in this order:
assess:present — audit ticket: parked findings, never a PR. Machine-composed byjunco assess.analyze:present — investigation ticket: parked comment draft, never a PR. Machine-composed byjunco analyze.repo:present — PR-flow ticket: worktree, agent, verification, critic, draft PR.- none of the three — Q&A ticket: answered in place, no git, tools defaulting to a read-only subset (
read, grep, find, ls).
The field-by-field frontmatter contract lives on the
ticket schema page; junco schema prints the same
data as JSON Schema.
The plan shape that works
Plan quality is the single biggest lever on agent performance: in testing, a structured plan ran
several times faster and used far fewer tokens than a loose prompt doing the same work. The shape,
from the shipped examples/pr-ticket.md:
- Why — one paragraph of intent, plus pre-flight context (build tool, test runner, conventions) and reference signatures verified at plan time. Every Read avoided at execute time saves ~30 seconds.
- Scope, three tiers, in plain words — in scope (what to do); ask before
touching (stop and report, don't unilaterally fix — the escape hatch that prevents silent
scope expansion, the most expensive failure mode); out of scope (files not to touch).
Behavior-level forbidden actions come on top: no force-push or history rewrites, no
lockfile edits by hand, no error-suppression comments, no
--no-verify, nogit pushorgh pr create— the worker handles both. - Files table — path, action, notes. Every path here must reappear in a Step.
- Numbered Steps — each one atomic change ending in exactly one commit. No uncommitted limbo states.
## Verification— a fenced bash block junco runs in the worktree after the session. Exact commands that check exit 0, not narrative; paired 1:1 with testable Behavior assertions where the plan has them.- Done when — observable outcomes, including the expected commit count.
- Notes for the agent (strict) — copied verbatim, never reworded. This is the anti-loop
payload: trust the spec instead of re-exploring; never run
git log/status/diffto re-verify a commit that exited 0; never resend the full todo phase list (it wipes progress memory and triggers re-planning loops); no scope expansion; and a graceful one-sentence stop on spec mismatch, so the failure note explains what to fix instead of a supervisor kill with no information. Dropping this block is the difference between a few minutes and 20+ minutes of wall clock.
cd inside the ## Verification
block. Junco runs it with cwd set to the worktree; a leading cd moves out of it and
the verification fails for the wrong reason. Plan-lint rejects it outright.wc -l < file pads with whitespace on BSD;
sed -i needs '' there; date -v (BSD) and
date -d (GNU) differ. When in doubt, prefer awk or Python one-liners.Plan-lint
A deterministic linter checks every ticket before the agent starts. Failures route straight to
failed/ with a phase_error naming the rule — before any tokens are
spent. Enforced rules:
- No
cdlines inside the## Verificationfenced bash block. - Every
### Step Nblock contains exactly onegit commitline. - Every path in the Files table appears in at least one Step body.
- Every label in frontmatter exists on the repo — a nonexistent label fails
gh pr createafter push, leaving a branch but no PR. - The strict Notes block is present at the end.
- No forbidden phrases:
TBD,Similar to Step N,think carefully,consider all cases— placeholders and vague exhortations feed loops.
Submitting and managing
junco submit ./my-ticket.md cat my-ticket.md | junco submit - junco list failed junco retry --all junco rm add-util-2026-07-18
junco submit queues a ticket (also reads stdin);
junco list [box] lists tickets newest-first per queue
box (inbox|processing|done|failed);
junco retry <name…|--all> moves failed tickets
back to the inbox — note a retried ticket is cut at the first appended
<!-- junco-result separator, so a body containing that literal line loses its
tail; junco rm <name> deletes a still-queued
ticket from the inbox.
Templates and examples
examples/ ships three shapes: qa-ticket.md (no repo:,
answered in place), pr-ticket.md (the full plan shape above), and
amend-ticket.md (amends_pr: — new commits on an existing PR branch,
with an amend-mode strict Notes block that forbids rebasing, squashing, and force-pushes).
templates/plain/task.md and task-code.md are blank starters in plain
Markdown; templates/task.md and task-code.md are the same with Obsidian
Templater date/title placeholders for vault-based dispatch. The bundled junco-dispatch skill
scaffolds tickets to this exact shape and submits them for you.