Move issues and external PRs through the triage state machine, categorise, verify, write Agent Briefs, and route each to its next phase. Use when triaging a new issue, reviewing what needs attention on the tracker, or starting a unit of work from an issue.
Resources
2Install
npx skillscat add noahdgrant/nightshift/ns-triage Install via the SkillsCat registry.
Triage
Move issues on the project issue tracker through a small state machine, and route each one to the phase that picks it up next. This is the outer loop's entry point.
Read docs/agents/issue-tracker.md (how to reach the tracker, who counts as the team) and docs/agents/triage-labels.md (the label string for each role). If either is missing, load the ns-setup skill.
If the tracker config treats external pull requests as a request surface, triage covers them too: a PR is an issue with attached code, using the same roles and states, with the deltas marked "for a PR" below. Resolve a bare #42 to an issue or PR per the tracker config.
Every comment or issue posted to the issue tracker during triage must start with this disclaimer:
> *This was generated by AI during triage.*Reference docs
- agent-brief.md: how to write durable Agent Briefs
- out-of-scope.md: how the
.out-of-scope/knowledge base works
Roles
Two category roles: bug (something is broken) and enhancement (new feature or improvement).
Seven state roles, each routing to a next phase:
| State | Meaning | Next |
|---|---|---|
needs-triage |
new, not yet evaluated | triage |
needs-info |
waiting on the reporter | reporter, then triage |
needs-repro |
a bug with no reproducible failure yet | ns-troubleshoot |
needs-define |
intent unclear, or the change is large | ns-define |
ready-for-agent |
Agent Brief written, small enough to build directly | ns-build |
ready-for-human |
brief written, but needs a human (hardware access, a judgement call, credentials) | a person |
wontfix |
will not be actioned | none |
For a PR, the states read against the attached code: ready-for-agent means a brief is attached and an agent should take the next step on the diff; ready-for-human means it's ready for a human to merge.
Every triaged issue carries exactly one category and one state. If state labels conflict, flag it and ask the maintainer before doing anything else.
State transitions: an unlabeled issue normally goes to needs-triage first, and from there to any other state. needs-info returns to needs-triage once the reporter replies. The maintainer can override at any time. Flag transitions that look unusual and ask before proceeding.
Gate policy
The gate policy comes from the prompt that started the run, and decides who applies the outcome. With no policy given, use stop.
gates: stop: recommend, then wait for the maintainer's direction before applying anything.gates: auto: apply your own recommendation and continue. Grilling needs a human, so route an issue that needs it toneeds-defineorneeds-infoinstead. A comment addressed to someone outside the team follows the tracker config'sExternal commentssetting:allowposts it,wait(the default) drafts it in your report and stops.
Invocation
The maintainer describes what they want in natural language. Interpret the request and act. Examples:
- "Show me anything that needs my attention"
- "Let's look at #42" (issue or PR)
- "Move #42 to ready-for-agent"
- "What's ready for agents to pick up?"
Show what needs attention
Query the issue tracker and present three buckets, oldest first:
- Unlabeled: never triaged.
needs-triage: evaluation in progress.needs-infowith reporter activity since the last Triage Notes: needs re-evaluation.
When PRs are in scope, include external PRs and tag each line [PR] or [issue]. Discovery surfaces only external PRs, so a collaborator's in-flight PR is not triage work. An explicitly named PR is always triaged regardless of author.
Show counts and a one-line summary per item. Let the maintainer pick.
Triage a specific issue or PR
Gather context. Read the full issue or PR (body, comments, labels, author, dates; for a PR, the diff too). Parse any prior Triage Notes so you don't re-ask resolved questions. Explore the codebase using the domain glossary and ADRs (see
docs/agents/domain.md). Run three checks, and report where you looked for each:- Duplicates: search open and closed issues for the same request by domain concept, not just the reporter's wording. A match is a duplicate: link it, and close this one as
wontfix(duplicate) or merge the new detail into the original. - Redundancy: search the codebase for an existing implementation of the requested behavior. If found, it's an already-implemented
wontfix(step 5). - Prior rejection: read
.out-of-scope/*.mdand surface any that resembles this request.
- Duplicates: search open and closed issues for the same request by domain concept, not just the reporter's wording. A match is a duplicate: link it, and close this one as
Recommend. State your category and state recommendation with reasoning, the next phase it routes to, and a brief codebase summary relevant to the request. Under
stop, wait for direction. Underauto, carry on.Verify the claim. Before any grilling, check that the claim holds up. For a bug, reproduce it from the reporter's steps using the commands in
docs/agents/stack.md. For a PR, check out the diff and run the relevant tests. Report the result: confirmed (with code path), failed, or insufficient detail. A bug you can't reproduce routes toneeds-reprowhen the report is plausible and detailed, orneeds-infowhen it lacks the detail to try. A confirmed verification makes a much stronger brief.Grill (if needed). If a few rounds of questions would settle the request, load the
ns-grillingandns-domain-modelingskills and grill it into shape, sharpening domain terms and updatingGLOSSARY.md/ADRs inline as decisions land. If it needs a spec or a redesign, route it toneeds-defineinstead.Apply the outcome. Set the labels, then:
ready-for-agent: post an Agent Brief comment (agent-brief.md), then start the unit (below).ready-for-human: same structure as an Agent Brief, plus why it can't be delegated (judgement calls, hardware or external access, design decisions, manual testing).needs-repro: post Triage Notes with the reporter's steps, what you tried, and what happened. Next:ns-troubleshoot.needs-define: post Triage Notes with what's settled and the open questions. Next:ns-define.needs-info: post Triage Notes (template below).wontfix: close the issue, with the comment depending on why:- Already implemented: point to where it lives. Leave
.out-of-scope/alone: that KB is for rejected requests, not built ones. - Duplicate: link the original.
- Rejected (bug): give a polite explanation, then close.
- Rejected (enhancement): write to
.out-of-scope/, link to it from a comment, then close (out-of-scope.md).
- Already implemented: point to where it lives. Leave
needs-triage: apply the label. Optional comment if there's partial progress.
Hand off. Under
stop, report the outcome and the next phase, and stop. Underauto, load the next phase's skill (ns-build,ns-troubleshootorns-define) forready-for-agent,needs-reproandneeds-define. If that skill isn't installed, report the routing and stop. Every other state stops.
Start the unit (ready-for-agent)
The unit ID is <issue-number>-<slug>, with a short kebab-case slug from the title (142-uart-timeout).
- Run
ns worktree new <unit-id>and readpathandartifactsfrom its JSON. Withoutns:git worktree add ../<repo-name>.worktrees/<unit-id> -b ns/<unit-id>, create.ns/<unit-id>/inside it, and append.ns/to$(git rev-parse --git-common-dir)/info/excludeif it isn't listed. - Write the same Agent Brief to
<worktree>/.ns/<unit-id>/brief.md, with a link to the issue under it:
---
unit: 142-uart-timeout
phase: triage
status: pass
base: main # the branch the worktree came from
updated: 2026-10-08T21:14:00Z
---
Issue: <issue URL>
## Agent Brief
...The unit is started when the issue carries ready-for-agent, the brief comment is posted, and brief.md exists in the worktree with status: pass.
Quick state override
If the maintainer says "move #42 to ready-for-agent", trust them and apply the label directly. Confirm what you're about to do (label changes, comment, close), then act. Skip grilling. If moving to ready-for-agent without a grilling session, ask whether they want an Agent Brief written. The unit starts only once a brief exists.
Needs-info template
## Triage Notes
**What we've established so far:**
- point 1
- point 2
**What we still need from you (@reporter):**
- question 1
- question 2Capture everything resolved during grilling under "established so far" so the work isn't lost. Each question is specific and actionable ("which firmware version was on the board?"), never a generic request for more info.
Resuming a previous session
If prior Triage Notes exist on the issue or PR, read them, check whether the reporter has answered any outstanding questions, and present an updated picture before continuing. Skip questions already resolved.