Reap the current repo's open tickets against the speed-over-robustness rule — park belt-and-suspenders work in Maybe Someday, close what is moot, post one digest. Use when asked to reap or sweep the tracker, and as step 0 of /maintain-repo.
Install
npx skillscat add surreptakos/claude-dotfiles/ticket-reaper Install via the SkillsCat registry.
Ticket reaper
Dan, 2026-09-18: "We are being way too careful for internal tools that I will only ever share
with a small scrappy team. I need speed over robustness. We shouldn't be building solutions to
problems that don't exist yet. Defensive coding is bad." The reaper holds the tracker to that rule,
so it stays clear of the cautious tickets the 2026-09-18 sweep closed (77 of them). It runs as step
0 of the weekly /maintain-repo, with no standalone Routine (owner ruling, issue 664).
Setup
- Repo:
git remote get-url origin. Read every open issue, one page at a time:gh api "repos/<owner>/<repo>/issues?state=open&per_page=100&page=N"until a short page.
Skip pull requests (pull_requestkey) and anything already in the Maybe Someday
milestone. - The digest lands on the open issue titled Ticket reaper digest (label
orchestrator).
If it is missing, create it with that title and label and say so in the digest. - The parking milestone is Maybe Someday. If it is missing, create it.
Done when every open issue is listed, and the digest issue and the milestone both exist.
Classify each ticket — read the body, not just the title
Three outcomes. When in doubt between park and leave, leave; when in doubt between park and
close, park.
Park in Maybe Someday — the ticket solves a problem nobody has hit, or hardens something
that works:
- guards against a failure seen zero times ("could", "would", "in case", "a future machine")
- a fallback or retry for a path that has not failed, a second check for something one check
already covers, an exemption path for an advisory, an audit of an audit - a "third pass" on wording, a doc note about a trap nobody fell into, a memory note about a
capability nothing needs, an upgrade-table row, a wikilink spelling - a probe or measurement with no decision waiting on it
- a discovery-triage chore (triaging a fleet run's bullets) — the bullets are speculative by
construction; a real failure files its own ticket - consolidation or hygiene with no user-visible change
Close as not planned — the ticket is moot, not merely cautious:
- its subject was deleted, retired or superseded (a mirror, a flow, a design replaced by a PRD)
- it is a duplicate of an open ticket (say which)
- it is already delivered (the text or code it asks for is on the default branch — quote it)
Leave alone — a real problem that bit, or work on a live route:
- a bug with a date and an observed failure in its body
- work a live route depends on today: a cut-over step, a PRD child whose body names the failure
it removes. Judge the body: being a PRD child, carrying a milestone or having a ruling on it
earns nothing by itself. - anything labelled
ready-for-human,orchestratororwayfinder:map(rulings and state
containers are never the reaper's) - anything Dan pulled back out of Maybe Someday, for ever after: check the issue's events for a
demilestonedon Maybe Someday before parking.
Windows-only or PC-only work is judged like any other ticket: the desktop and the cloud are both
allowed venues for the master orchestrators (ADR 0004, docs/adr/0004-cloud-is-an-allowed-venue.md).
Done when every ticket read has exactly one outcome.
Land it
- Park:
gh api --method PATCH repos/<owner>/<repo>/issues/<n> -f milestone=<number>(MCPissue_writein a container). A ticket already in another milestone moves the same way; the
digest names the milestone it left. The digest is the record, so the ticket gets no comment. - Close: one comment naming the reason class and the evidence (the open duplicate's number, the
commit or line that delivered it). Then nullify every unticked box in the body — append— not planned: <reason class>, ticket reaper <date>to each- [ ]line, leaving the box
unticked (a tick claims verified;not plannedis the wordingtools/tracker-audit.jsaccepts
as a legitimate closure). Then close withnot_plannedand swap the state label forwontfix.
The label is what the audit exempts; the annotated boxes are what a reader sees. - Digest: one comment on the digest issue, three lists (parked, closed, left with a one-clause
reason where the call was close), counts at the top. A run that changes nothing still posts a
one-line digest, so a silent week reads differently from a week the sweep did not run.
Done when every park and close has landed and the digest comment is posted.
Rails
- Never close a ticket that is parked, labelled
ready-for-human, or younger than 48 hours. - Edit a ticket body only for the box annotation on a close, and its labels only for the state
swap on a close. - One run touches at most 40 tickets; list the rest under "not reached" in the digest so the next
run starts there.
In a cloud container
Same steps. gh api REST works through the proxy; gh issue (GraphQL) does not — use MCPlist_issues, issue_write, add_issue_comment for the same calls. Page explicitly
(&page=N) rather than --paginate.