CityRP Documentation Repository

Triage states

Last updated

Triage works in the org's exclusive status/ and resolution/ labels (issue-tracker.md). Because each scope is exclusive, an issue is in exactly one state at a time, and adding a new status/ label replaces the old one.

State Meaning
status/new Filed; nobody has evaluated it yet
status/needs-info Waiting on the reporter
status/awaiting-review Evaluated, and now waiting on a staff or maintainer decision before it can be triaged, such as sign-off on a suggestion. Every type/spec sits here: the decision it waits on is the breakdown, which /to-tickets makes. So does every type/stub, and every type/suggestion that passes triage: the decision each waits on is the grilling, which /grill-to-spec runs
status/triaged Ready to pick up (see below)
Closed, with a resolution/ label Won't be actioned (see Closing)

Transitions: status/new → status/awaiting-review (when a decision is needed) → status/triaged. Any state can move to status/needs-info, and it returns to status/new once the reporter replies, so the issue resurfaces for triage.

GitHub's review-required label, which stood in for status/new before the migration, did not come across.

The triaged state

An issue is status/triaged when it carries exactly one type/ label, status/triaged, and everything its type needs below.

Type Priority Effort Also needs
bug required required The claim verified: reproduced, or the faulty code path found. A brief: expected against actual behaviour, and acceptance criteria that prove the fix
task required required A brief, or a body that works as one: what to build and how to tell it is done. Blockers wired as native dependencies, and Part of #<parent> when it has one
epic required none A ## Tickets checklist of its children, each opening Part of #<epic>. No brief of its own: its children carry them
initiative required none A body that states the goal, and a ## Tickets checklist of its epics and tasks, each opening Part of #<initiative>. No brief of its own
research required required The question, and what an answer has to cover
wayfinder required none The map, as /wayfinder leaves it
discussion, feedback, pull-request none none Nothing further

A type/spec never reaches status/triaged. It has the problem and the decisions but no acceptance criteria, so it is not a brief; /to-tickets turns it into issues that are. Nor does a type/stub, which has less still: an idea and its open questions. It carries a priority and no effort, and /grill-to-spec rewrites it in place as a spec.

Migrated issues were backfilled as triaged from their labels alone, before the brief requirement existed. They stay triaged; add the brief when one is picked up.

Triage's terminal move is: set the type, set priority and effort as far as the type needs them, post the brief where the type needs one, then add status/triaged.

Some issues are born triaged rather than passing through status/new. /to-tickets creates its tickets and its epic at status/triaged, and an initiative, which is set up by hand as planning, starts there too. A spec does not: whether it came from /to-spec or /grill-to-spec, it is born status/awaiting-review, and becomes triaged only once /to-tickets has broken it down. A stub is born status/awaiting-review as well, and keeps that status when /grill-to-spec re-types it as a spec.

A type/suggestion never reaches status/triaged either. One that passes triage's checks (the feature rules, the codebase for an existing implementation, .out-of-scope/ for a prior rejection, and the pillars for the one it serves) gets a priority and a condensed brief, and moves to status/awaiting-review for /grill-to-spec, which re-types it as a spec in place. It takes no effort: the spec's breakdown sizes the work. Suggestions triaged with a full brief before this rule stay status/triaged, and /wave grills them all the same.

Finding each bucket

.claude/skills/triage/scripts/buckets.sh prints every bucket, oldest first, and dates a migrated issue by its original GitHub filing rather than the migration. It wraps the queries in the gitea-issues skill, so it never uses tea issues list --labels, which silently ignores org labels.

/triage bucket buckets.sh argument What it selects
Unlabeled, never triaged unlabelled Open issues with no status/ label
New new status/new
Needs info, with reporter activity needs-info status/needs-info issues the reporter has commented on since the label was applied
Waiting on the maintainer awaiting-review status/awaiting-review. Show it when the maintainer asks what needs their attention
Specs waiting for a breakdown specs type/spec. These are for /to-tickets, not /triage: don't write a brief for one
Ready to pick up triaged Unassigned status/triaged issues with no open blocker and no state/blocked, other than epics, initiatives and wayfinders, highest priority first. Prints the first 20, since blockers cost a call per issue

With no argument it prints the first three; all prints all five.

Two sources feed the unlabeled bucket:

  • Blank issues. .gitea/ISSUE_TEMPLATE/config.yml leaves blank issues enabled, and a blank issue carries no labels.
  • The website's feedback sync. Some player bug reports arrive with only the repo bug label. Leave that label where the sync put it, and add the type/ label at triage.

Issues filed by renovate-bot are left out of the bucket. Its Dependency Dashboard is a standing report, not a ticket, and carries no labels on purpose (dependencies.md).

Migrated issues. The issues migrated from GitHub were backfilled from GitHub's own type, priority and effort data. An open one that had everything its type needs is status/triaged; the rest are status/new and sit in the triage queue like any other new issue.

Closing

Close reasons are resolution/ labels, applied before closing. See issue-tracker.md. A closed issue with no resolution/ label counts as completed. Always comment the reasoning before closing, and apply the label before the close: the feedback sync shows players the status it implies, and a late label notifies the post's author a second time.