os-backlog: deterministic ticket-filing surface — labels/body-file in the Ruby CLI plus a filer skill or agent #186
Labels
No labels
P0
P1
P2
P3
bug
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#186
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Filing the seven gap-analysis tickets (#179–#185) on 2026-07-31 required the main session to hand-roll
teainvocations instead of using os-backlog at all. The friction was not one missing flag; it was that the deterministic parts of ticket management have no code surface, so the session had to rediscover them.What actually happened, in order:
plugins/os-backlog/bin/issue-create— does not exist. The real entry point isbin/os-backlog issue-create, a subcommand of the dispatcher. Cost: one failed invocation plus a--helpread.issue-createaccepts--title/--body/[--priority]only. The tickets neededenhancement+ready-for-agent(six of them) andready-for-human(one). No flag exists. Perskills/capture/SKILL.md:28, that is deliberate: "issue-createis structurally incapable of applying state labels; there is no flag for one, by design."tea issues create --labels …, which meant re-deriving by hand: that this repo isforgejo:jared/cc-os(read from.cc-os/config), thatteais the tool and notgh, that the label names are exactlyenhancement/ready-for-agent/ready-for-human(verified withtea labels), thatteahas no--description-fileso bodies must go through-d "$(cat …)", and that large bodies passed as args can hang (known quirk, hence per-calltimeout 90).tea issues <n>does not print labels usefully, so confirming the labels landed took a separatetea issues ls --fields index,title,labels.Every one of those steps is deterministic. None of them needed a model. The main session spent context and turns on mechanics, and a different session filing the same batch would rediscover all of it from scratch.
The design gap
captureis built for one use case: cheap mid-session capture of work that will not be done now, deliberately with no state labels, so the AI never pre-triages. That design is correct and should not change.There is a second, distinct use case with no surface at all: filing a batch of already-triaged, fully-specified tickets — the output of a gap analysis, a lint-mining run, a review. These tickets are triage-complete by construction;
ready-for-agentis precisely the accurate label, and withholding it makes them invisible to label-driven queues. (Three earlier tickets from the 2026-07-30 batch, #176/#177/#178, are unlabeled for what looks like exactly this reason.)Right now that use case routes around os-backlog entirely, which loses the tracker abstraction: raw
teahardcodes forgejo, so the same workflow would silently not work in agithub:orrepo:project.What to build
1. Extend the Ruby CLI so the mechanics are code.
bin/os-backlog issue-createshould gain:--label NAME(repeatable) or--labels a,b, validated against the labels that actually exist on the resolved tracker, failing with the available list on a typo rather than silently creating an unlabeled issue.--body-file PATH— reads the body from a file, so bodies never transit an argv and thetealarge-body hang stops being something each caller must defend against withtimeout.tea issues lsconfirmation step.--dry-runthat prints the resolved tracker, slug, and final command without executing.Hard constraint carried forward:
nextis human-curated per ADR-0042 and the AI never applies or removes it. A--labelflag must rejectnextoutright at the CLI level, not merely by convention in a skill body. Encoding that in code is strictly stronger than today's "there is no flag" approach, which enforces it only by omission and is why the session bypassed the CLI in the first place.Whether this belongs on
issue-createor on a sibling subcommand (e.g.issue-file, keepingcapture's no-state-label guarantee untouched) is an open design question — decide it before implementing, and record it if it changes the capture contract.2. Add a delegation surface so the main session never handles this. Either a
/os-backlog:fileskill or abacklog-filersubagent whose whole job is: take a set of title + body-file + labels tuples, resolve the tracker, create each issue, verify labels round-tripped, and report back the numbers and titles. The main session hands over a spec and gets back a list. The AI is present only for surprises — a label that does not exist, a tracker not configured, a partial batch failure — not for the mechanics.A subagent is likely the better fit for batches: filing N issues is N tool calls of pure bookkeeping that should not consume main-session context. This matches the user's existing preference for handing ticket comment/close/label API sequences to a cheap subagent.
3. Fix the discoverability miss.
plugins/os-backlog/CLAUDE.mdnamesbin/os-backlogcorrectly, but nothing steers a session that reaches forbin/issue-createfirst. Whatever surface lands should be named in os-backlog's CLAUDE.md and indocs/implementation-status/os-backlog.md.Acceptance criteria
bin/os-backloginvocation with no rawteaanywhere, and works unchanged against agithub:tracker.--label nextis rejected by the CLI with a clear message.Verify
Re-file a two-issue batch through the new surface, then confirm with
tea issues ls --fields index,title,labelsthat both carry their intended labels. Then repeat the--label nextcase and confirm rejection.Provenance
Surfaced 2026-07-31 while filing #179–#185, the output of a gap analysis of os-aidd-lint's clean scan on
plugins/cc-architect/skills/cc-architect/SKILL.md. Related:plugins/os-backlog/bin/os-backlog(issue-createat ~line 200),plugins/os-backlog/skills/capture/SKILL.md, ADR-0042 (git-issues-only,nextis human-only),.cc-os/config. Reported directly by Jared.Related unlabeled issues from the prior batch that this would have prevented: #176, #177, #178.
Design amendment from Jared, 2026-07-31: should the filing surface just invoke
/triageafter creating the issue, so labeling stays owned by the skill built for it?Half yes. Splitting it:
Do not auto-invoke
/triageafter every create.~/.claude/skills/triage/SKILL.mdis a maintainer-in-the-loop state machine, not a labeling function. Step 2 recommends a category/state and then waits for direction; step 3 reproduces the bug or checks out the PR; step 4 may run/grilling; step 5 posts an agent-brief comment carrying a mandatory "generated by AI during triage" disclaimer. It also setsdisable-model-invocation: true— it is deliberately never model-fired. Calling it programmatically after each create would route around that choice and would either block acapture(whose entire point is not interrupting the session) or force the AI to answer on the maintainer's behalf, which reintroduces the AI pre-triage that capture's no-label design exists to prevent.There is also a redundancy cost in the batch case: for tickets the session just authored,
/triage's redundancy and specification checks re-derive context the author already has.But the underlying point is right: os-backlog must not own label semantics. Those are already owned by
docs/agents/triage-labels.md(five state roles, two category roles, plus the standing rule thatwaiting/review/next/P0-P3are os-backlog's and must not be repurposed for triage). Revised design for this ticket:--labelflag validates against the live tracker labels, and its documentation points atdocs/agents/triage-labels.mdrather than restating the taxonomy. Restating it would be a duplicated-authority defect: two owners, silent drift on the next edit.needs-triagerather than nothing. That is/triage's own documented entry state, so a later/triagerun discovers the issue in its "unlabeled / needs-triage" bucket naturally. This closes the real gap behind #176/#177/#178 — issues invisible to label-driven queues — without any skill-to-skill call./triagestays exactly where it is: the human-invoked path for issues whose category, validity, or specification is genuinely unknown.Net effect: labeling stays consistent and centrally defined, os-backlog stays deterministic, and the expensive judgment path is entered on purpose rather than on every create.
jared referenced this issue2026-08-03 14:42:18 +00:00
Per spec #250 (os-backlog lifecycle implementation): the shared tracker lib built in #253 implements this deterministic ticket-filing surface — this issue closes when that lib lands, not as separate work.
Partial progress check: --label + scope-gate validation shipped (ADR-0115, bin/os-backlog issue-create). But --body-file flag and the delegation surface (filer skill/subagent) are still absent — grep for body-file/filer in plugins/os-backlog/ finds nothing. #253's template validator ships a different slice (tier/section validation), not this ticket's surface. Leaving open.
Frozen in the 2026-08-16 backlog reset — see #419 for the expiry procedure. Do not work unless a live run rediscovers this issue.