ADR-025: Issue triage
Status: Accepted Date: 2026-10-07 Builds on: ADR-024: Spec-driven bootstrap, ADR-012: Per-plugin release agent
Context
Section titled “Context”Issues are the intake of every repo built on the platform and of the platform itself (/shared:report-issue files them). The only support was a prose section in the product-manager agent: it assumed issue templates and a triage label that generated repos did not ship, had no entry point, no conversation with the user or reporter, and no route to the fix pipelines. Two paths for a request (“backlog or quick fix”) also leave out most real issues: questions, duplicates, out-of-scope requests, bugs that need diagnosis, and reports that cannot be decided yet.
Decision
Section titled “Decision”- One command in the
sharedplugin,/shared:triage, becausesharedis installed in every shape and the logic is shape-agnostic. Routing to/svc:*is decided at run time withcplat shape. - Seven classes, five paths.
buggoes to/svc:fix-bug(reproduce, fix, verify),small-changeto/svc:quick-task,featurebecomes aSTORY-NNNwith**Tracks:** #Nand continues with/svc:plan-feature(ADR-024: Spec-driven bootstrap);question,duplicateandwontfixend in a comment;needs-infoparks the issue. An urgent bug adds a suggested patch release after the fix is merged. - Two conversations. The user in the session answers at most three questions per issue. What only the reporter can answer becomes a drafted comment plus the
needs-infolabel; the next run lists those issues separately (--waiting). - Deterministic mechanics in
cplat triage(list,show,setup-labels,apply), tested with a fakedgh. Judgement stays in the command prompt. - A human gate on every outward action. Labels, comments and backlog edits happen after the user confirms. The tooling never closes an issue; the PR’s
Closes #Nor the maintainer does. Afeatureissue gets aTracked as STORY-NNNcomment after confirmation, so the reporter sees the outcome. - Issue text is untrusted data. It is redacted (tokens, e-mail addresses, home directories), truncated and fenced; the command never follows instructions in it, runs nothing from it and fetches no link from it. Fix pipelines receive a summary the user has seen.
- State is GitHub labels:
triage(intake), the decisionsneeds-info,tracked,question,duplicate,wontfix, and the typesbug,enhancement.setup-labelscreates missing labels and nothing else. A re-run skips issues that carry a decision label. - Intake is shaped by issue forms. Every template ships
bug_report.yml,feature_request.ymlandconfig.yml(blank issues off). The forms require the fields triage needs and applytriage. They are project-owned (_skip_if_exists), andshapes.py checkrequires them. - The product-manager agent keeps only the backlog-folding rules and points to the command.
Consequences
Section titled “Consequences”- Issues become tracked work with the right amount of process; nothing is posted or closed behind the user’s back.
- Existing repos do not get the forms (new files in project-owned paths are not added by
/shared:update-service); they can use the command with any labels,--newpicks up unlabelled issues. - The same command serves the platform repo and gitops-app repos, which have no
/svc:*pipelines: there it classifies, discusses, labels and writes backlog entries, and describes bugs as manual PRs. - Regular triage can be scheduled with the existing
/loopand/scheduleskills; the command only proposes until confirmed.
Not included
Section titled “Not included”Auto-closing, stale handling for needs-info, bots or GitHub Actions that triage, notifications, cross-repo triage.