Worked examples
Four days with taskr
Each of these is a whole scenario, commands and output, against an invented billing project. None
of them adds a tool to your loop — they are the same five verbs reached for in different orders.
A model handoff
The case taskr exists for: your strongest model reads the problem and writes the plan, a cheaper
one executes it, and nothing is spent rebuilding context in between. Neither model has to
know the other exists — they meet on the issue.
Morning: the planner takes it
the planner, 09:03
taskr start PAY-31
PAY-31 — Stripe webhook retries double-charge
status: in_progress priority: high kind: bug
This is a fresh start — no prior park on this issue.
Tree state (as of 2026-08-24T09:03:11Z):
github.com/acme/billing @ feat/webhook-idempotency 4c8656a
worktree: /home/you/code/billing
Session 01a0352d-0758-70bb on your-laptop (/home/you/code/billing)
It reads the code, settles the design, and leaves both on the issue rather than in its own head.
The spec's body is uploaded, not its path — the next agent has the issue and not your
checkout.
the planner, 11:47
taskr doc add PAY-31 -f docs/specs/webhook-idempotency.md -t spec
Attached spec "Webhook idempotency" to PAY-31 as 01a0353f-11c2-7a41.
taskr step add PAY-31 "unique index on event_id" "make the handler idempotent" "replay test over a duplicated event"
Added step 01a0353f-2b90-7d18 to PAY-31.
Added step 01a0353f-2b90-7d2e to PAY-31.
Added step 01a0353f-2b90-7d3a to PAY-31.
taskr park -m "steps 1-3 are ready to execute; spec is attached, start at step 1" -r handoff
Parked PAY-31 (handoff).
The park reason is handoff, which is a different claim from blocked: nothing is stuck, the work is staged for someone else.
Afternoon: the builder resumes cold
the builder, 14:02
taskr start PAY-31
PAY-31 — Stripe webhook retries double-charge
status: in_progress priority: high kind: bug
Why work stopped: handoff (parked 2026-08-24T11:47:02Z)
What to do next:
steps 1-3 are ready to execute; spec is attached, start at step 1
Tree state (as of 2026-08-24T11:47:02Z):
github.com/acme/billing @ feat/webhook-idempotency 4c8656a
worktree: /home/you/code/billing
Documents:
[spec] Webhook idempotency (01a0353f-11c2-7a41)
Session 01a03545-9ab1-7c02 on your-laptop (/home/you/code/billing)
That packet is the product. A fresh session, a different model, possibly a different machine —
and it starts at the same commit, with the plan, the spec and a sentence saying what to do next.
No prompt engineering, no pasted context, no "catch me up".
the builder, 17:20
taskr step start PAY-31 1
taskr step done PAY-31 1 -m "migration 0042, unique on (account_id, event_id)"
taskr step start PAY-31 2
taskr park -m "handler is idempotent; the replay test in webhook_test.go is still red on the second POST" -r context_exhausted
Parked PAY-31 (context_exhausted).
Moving the steps is what makes the next resume land somewhere real: each one records the commit it
moved at, so the packet can say step 2 of 3, in progress since 4c8656a rather than
describing progress in prose.
A solo day
One person, one agent. The whole shape of a working morning:
taskr context
machine: your-laptop open issues: 12 writes as: user
No active session on this machine. Run `taskr next` or `taskr ls`, then `taskr start <ref>`.
Project: billing (billing)
branch format: tc/{key}-{n}--{slug}
pr target: main
taskr next
REF SCORE PRIORITY TITLE REASONS
PAY-31 8.4 high Stripe webhook retries double-charge triaged actionable; blocks PAY-30
PAY-27 5.1 medium Proration is wrong on mid-cycle plan triaged actionable
Needs a human:
PAY-19 — restore drill on the billing replica (`taskr check run 01a03488-7d21-7b09 --pass|--fail`)
next ranks and says why. The block underneath is the part no queue can rank — checks
only a person can run — so it is printed after the answer rather than mixed into it.
Then the thing that actually goes wrong on a real day: while fixing one bug you find another. Do
not fix it inline, and do not save it for your final message, where it evaporates the moment the
conversation ends.
taskr offload "Retry backoff ignores Retry-After on 429" -k bug -s medium -m "internal/stripe/client.go:88 sleeps a fixed 2s and never reads the Retry-After header, so a 429 storm keeps hammering. Found while fixing PAY-31; not the same bug, the double-charge is an idempotency problem. Done when the client honours Retry-After and a test covers a 429 carrying it."
Offloaded PAY-32 — Retry backoff ignores Retry-After on 429
The brief is the whole point. It gets read cold, days later, by someone with none of your context:
a file:line, what is wrong, what you already ruled out, and how to tell when it is
done. Two minutes now against an hour of re-derivation later.
Triage: is the report still real?
next only ranks issues with an actionable verdict, so a freshly imported backlog has
plenty of open work and an empty queue. Someone has to say which reports are real. That is one
verb, and it lists before it records:
taskr triage
REF REASON TITLE WHY
PAY-33 new Invoice PDF renders blank on Safari never triaged
PAY-21 rot Coupon applies twice on renewal the branch moved under it: 9f3c2ab1e77d -> 4c8656a0b2f1
PAY-14 expired Dunning email sent to cancelled accounts its verdict aged out (triaged 2026-08-04)
Read one with `taskr show <ref>`, then record what you found: `taskr triage <ref> <verdict> -e "<file:line or URL>"`.
taskr triage PAY-21 already_fixed -e "coupon guard landed in internal/billing/renew.go:58 via https://github.com/you/billing/pull/41"
Recorded verdict "already_fixed" for PAY-21.
taskr triage PAY-33 actionable -e "reproduces on 4c8656a: web/src/lib/pdf.ts:112 never awaits the font load"
Recorded verdict "actionable" for PAY-33.
Each row says why it is there. new has never been looked at. rot means
the branch moved under it — the two SHAs are the snapshot the issue was filed against and where the
repo is now. expired means a verdict aged out; they last a fortnight. ls is not a triage surface — it shows no verdicts and no reasons — and neither is next --untriaged, which is the ready pool and drops blocked issues and stale verdicts
alike.
-e names where you looked: a file:line, a commit, a full URL. A verdict
without evidence is the next reader's dead end. Only actionable puts an issue into next; already_fixed and stale say the work is not needed,
and needs_info says it cannot be judged yet.
Checks as done-when
Some work cannot be verified when it is written. It needs a deploy, a production run, or an action
only a person may take. A check records that condition on the issue so the issue can never read as
done-but-unverified.
taskr check add PAY-19 -m "hey -z 30s -c 50 GET /api/invoices" --expect "> 100 r/s, p95 under 400ms"
Check 01a03488-7d21-7b09 on PAY-19 (agent). Record a result with `taskr check run 01a03488-7d21-7b09 --pass|--fail`.
taskr check add PAY-19 -m "restore last night's snapshot onto the replica and diff invoice totals" --expect "zero diff" --human
Check 01a03488-7d21-7c44 on PAY-19 (human). Record a result with `taskr check run 01a03488-7d21-7c44 --pass|--fail`.
--human names who should carry out the procedure, not who records it. That
distinction is what puts a check into the "needs a human" block of taskr next and
onto the app's checks screen.
Then the close gate does its job:
taskr close PAY-19 -r "connection pool raised to 40"
PAY-19 has pending checks:
hey -z 30s -c 50 GET /api/invoices (agent) — run it with `taskr check run 01a03488-7d21-7b09 --pass|--fail`
restore last night's snapshot onto the replica and diff invoice totals (human) — run it with `taskr check run 01a03488-7d21-7c44 --pass|--fail`
Run them, or close anyway with `taskr close PAY-19 --despite-checks`.
close refused: 2 checks pending
taskr check run 01a03488-7d21-7b09 --pass --measure invoices.p95=0.31s --measure throughput=128rps --conditions "c50, 30s"
Recorded pass (run 01a0349a-05fe-7e11).
Numbers go in as numbers — --measure takes typed values so two runs can be compared,
and a failure keeps the check pending rather than settling it, because the latest run decides.
Closing anyway is --despite-checks, and each pending check is recorded as skipped on
the ledger rather than quietly dropped.
Onboarding a repo
Writes are refused against a repo taskr does not know, and taskr context says so
rather than failing quietly — an issue filed against an unclaimed checkout cannot be ranked,
scoped or resumed. Note where the hint sends you: at the taskr-onboarding skill, not
at a person. It names the things registration needs, which is what lets the agent standing in the
repo do this itself rather than handing you a task:
cd ~/code/billing
taskr context
machine: your-laptop open issues: 0 writes as: user
No active session on this machine. Run `taskr next` or `taskr ls`, then `taskr start <ref>`.
no repo is registered for this remote — run the taskr-onboarding skill
git remote get-url origin
git rev-parse --show-toplevel
gh repo view --json defaultBranchRef,url,owner,name
hostname
taskr project init billing --key PAY --branch-format 'tc/{key}-{n}--{slug}' --pr-target main
Initialized billing (PAY).
taskr project attach
Attached github.com/acme/billing to billing.
Two commands, two different claims: init creates the project and its ref prefix, attach says this checkout is one of its repos. Attach defaults both arguments to what
it reads from .git, which is why it takes none.
In a monorepo, a project owns a directory rather than the whole repo, and one repo can carry
several:
taskr project init payments --key PAY
Initialized payments (PAY).
taskr project attach --project payments --dir services/payments
Attached github.com/acme/platform (services/payments) to payments.
Setting the conventions at init is worth the extra flags. The branch shape, the commit style and
the branch PRs target are printed by taskr project ls and beside the project in every taskr context, which is how an agent follows your house style without being told
twice.