| name | triage |
| description | Funnel tracker issues into `Todo` with sharp acceptance criteria so runway will pick them up. Vendor-agnostic — works with whatever tracker the repo declares (Linear today; GitHub Issues shipped). Use when the user wants to work the inbox, prepare issues for a runway run, decide which issues an agent can take vs. which require a human, or work the Triage queue. Reach for this skill any time the user mentions triaging, classifying, or moving issues toward an autonomous run — even if they don't explicitly say "triage." |
/triage — runway funnel
The purpose of this skill is to prepare tracker issues for runway.
runway scans the tracker for issues in Todo, hands each one to
@ai-hero/sandcastle (Claude Code in Docker), runs an adversarial
sub-agent review, and opens a PR. Trust comes from human PR review, not
from upstream gates.
So every triage decision answers one question:
Is this issue body sharp enough that Claude Code, reading nothing but
the issue, will produce a PR a human is happy to review?
If yes → ensure acceptance criteria are present and move the issue to
Todo.
If not yet → identify what's missing and either ask the reporter
(needs-info) or park it (backlog).
If never → eject it cleanly: needs-human, canceled, or duplicate.
The Agent-Brief-as-comment pattern is gone. runway reads the issue
body directly — there's no separate brief artifact for it to consume.
Triage's deliverable is a good issue body, in place. If the body needs
work, edit the body. If acceptance criteria are missing, post a Triage
Notes comment asking the reporter, but the source of truth Claude Code
sees is still the body itself.
Tracker independence
This skill speaks the canonical state machine and label vocabulary
defined in CONTEXT.md. All tracker operations
(fetch_issue, list_comments, post_comment, apply_labels,
set_status) go through the active tracker adapter, resolved at
session start by reading .afk/config.yml's tracker: field (default
linear if absent).
Tracker-specific knowledge (team mappings, vendor-native status names,
workspace bot accounts, MCP cheatsheets) lives in the adapter, never in
this skill. If you find yourself needing such knowledge here, that's a
sign the abstraction is leaking — fix the adapter.
AI disclaimer
Every comment created by this skill must start with this disclaimer
on its own line, before any other content:
> *This was generated by AI during triage.*
The disclaimer is delivered through whatever surface the active tracker
exposes for comments. The text itself is vendor-agnostic.
Reference docs
Capability checks
Before using vendor-specific features, query the adapter's capability
declaration:
| Capability | Used by triage when |
|---|
team_namespace | Filtering issues to the active team's namespace |
project_membership | Showing project-scoped queues |
cycle_membership | Showing cycle-scoped queues |
active_work_detection | Read-only-on-active-work protection (see State machine) |
If a capability is absent, degrade gracefully: skip the feature,
surface a one-line note to the maintainer (e.g., "this adapter doesn't
support active-work detection — proceed with caution"). Don't refuse
just because a vendor has fewer concepts than Linear.
State model
Canonical statuses
The skill speaks these values; the adapter maps to vendor-native names.
| Status | Meaning |
|---|
triage | Incoming. Has not been classified or has open questions. |
backlog | Ejected from active triage but not killed. Will revisit later. |
todo | Ready for runway, or marked needs-human for a human. Acceptance criteria are sharp. |
in-progress / in-review / reviewed | Active work. Off-limits to this skill — read-only. |
done | Closed normally. |
canceled | Won't fix. Permanent ejection. |
duplicate | Closed because another issue covers it. |
State labels (apply at most one)
| Label | Meaning |
|---|
needs-info | Specific, named information is missing — without it the issue body isn't sharp enough for runway. Status stays triage. |
needs-human | Cannot become runway-eligible (production access, judgment that doesn't reduce to acceptance criteria, cross-team coordination). Status moves to Todo so the human picks it up; runway skips issues carrying this label. |
Category labels (apply exactly one)
Bug — something is broken
Feature — new capability
Improvement — enhancement to existing capability
Invariants
- Every actively-triaged issue has exactly one category label.
- An issue has at most one state label. Conflicts are invalid —
flag and confirm before doing anything else.
in-progress / in-review / reviewed are read-only when the
adapter declares active_work_detection: reliable. When detection is
best-effort or none, warn before mutating.
State machine
The funnel direction is toward Todo. Each transition either
advances toward it, parks the issue, or ejects it.
| From | To | Direction | Trigger | Action |
|---|
New (triage, no labels) | + category, stays triage | enter | Skill on first look | Apply category, post recommendation comment. |
triage | + needs-info | park | Maintainer (skill) | Specific missing info named. Skill posts Triage Notes with what's blocking. |
triage / needs-info | todo (no state label) | advance | Maintainer (skill, after grilling) | Skill ensures the issue body has sharp acceptance criteria, removes needs-info if present, moves status to Todo. runway picks up from here. |
triage / needs-info | todo + needs-human | eject (HITL) | Maintainer (skill) | Body is sharp but reasons exist not to use runway (production access, judgment calls). Status moves to Todo; the needs-human label tells runway to skip. |
triage / needs-info | backlog | park | Maintainer (skill) | Acknowledged but deferred. Brief reasoning posted. |
triage / needs-info | canceled | eject | Maintainer (skill) | Polite explanation. For Feature/Improvement, write .out-of-scope/<concept>.md. |
triage / needs-info | duplicate | eject | Maintainer (skill) | Comment links the canonical issue. |
needs-info | back to triage for re-evaluation | advance | Skill (detects new reporter activity) | Reporter has commented since the last Triage Notes — re-evaluate. |
The maintainer can override directly via "Quick state override" below;
the skill should still flag unusual transitions.
Invocation
The maintainer invokes /triage and describes intent in natural
language. The active tracker is resolved silently from .afk/config.yml.
Examples:
- "What's closest to ready for runway?"
- "Show me what needs attention"
- "Let's look at issue 87" (or
VA-87, or owner/repo#42 — depends on tracker)
- "Move issue 42 to Todo"
- "Anything blocked on the reporter for over a week?"
If the tracker has a team-namespace concept and the namespace is
ambiguous, ask once: "Which team?" Then continue.
Workflow: show what needs attention
Order the queue by distance to runway-ready, not by age. The user's
time is best spent on issues nearest the funnel exit.
Query the adapter (scoped per the adapter's capabilities) and group
results:
- Promotable —
triage with category, body has clear
current/desired behavior, only missing sharp acceptance criteria.
(Skill should offer to draft them inline by editing the body.)
needs-info with new activity — reporter has commented since
the most recent Triage Notes. Could now be promotable. (Compare
comment timestamps via list_comments.)
- Stale
needs-info — needs-info for > 7 days with no reporter
activity. Candidates for nudging or ejecting to canceled.
- Raw
triage — no category yet. Still needs first classification.
For each issue: identifier (per adapter — VA-87, owner/repo#42,
file path), title, age, category if any, and a one-line summary of the
body. Show counts per bucket. Let the maintainer pick.
Workflow: triage a specific issue
Step 1 — gather context
fetch_issue(<id>) — body, labels, project, status, dates
list_comments(<id>) — every comment with author + timestamp
- Parse any prior Triage Notes comments authored by this skill (they
begin with the AI disclaimer) to recover prior progress
- Read all
.out-of-scope/*.md files and check for matching concepts
- For non-trivial cases, use
Agent with subagent_type=Explore to
find related code, types, tests, and recent history
Step 2 — present a recommendation
Lead with the runway question:
Can Claude Code, reading only this issue body, produce a PR a human
would be happy to review? What's missing?
Then state:
- Category —
Bug / Feature / Improvement, with reasoning.
- State recommendation — one of the canonical state labels or
status moves, with reasoning.
- What the body is missing — if recommending
needs-info, name the
specific facts that the body needs: a clear current behavior, a
clear desired behavior, testable acceptance criteria, repro steps for
bugs.
- Out-of-scope match — "This looks like
.out-of-scope/<name>.md — we rejected this before because X. Same
call?"
- Codebase findings — relevant interfaces, configs, tests, recent
history.
Then wait. The maintainer may agree, override, ask to flesh it out, or
want to discuss.
Step 3 — bug reproduction (bugs only)
If category is Bug, attempt reproduction before any grilling
session. Hand off to /diagnose for the full
six-phase loop, or do a lightweight repro check inline:
- Read the reporter's reproduction steps
- Trace relevant code paths
- Try to reproduce: run tests, execute commands, narrow the failure
- Reproduced — capture the observation. This becomes the body's
Current behavior and shapes the acceptance criteria.
- Could not reproduce — say so. May be environment-specific,
already fixed, or misreported.
- Insufficient detail to attempt — strong signal for
needs-info.
Step 4 — grilling session (if needed)
If the body is too vague to hand to runway, run a
/grill-with-docs
session targeted at the gaps:
- A clear Current behavior paragraph
- A clear Desired behavior paragraph
- At least one concrete, testable acceptance criterion (Claude Code
uses these as the spec)
- Repro steps (for Bugs)
The session output gets edited back into the issue body — that's the
artifact runway reads.
Step 5 — apply the outcome
Always: comment first, then labels/status. This preserves
reasoning before the issue moves.
| Outcome | Comment to post | Labels | Status |
|---|
| Runway-ready | (optional) "Promoting to Todo — runway will pick this up" | + category, − needs-info | todo |
| Human-only (HITL) | Human Brief — short paragraph explaining why human, not runway (production access, judgment, etc.) | + category, + needs-human, − needs-info | todo |
needs-info | Triage Notes — what's established + the specific missing facts (template below) | + category, + needs-info | triage |
| Won't fix (Bug) | Polite explanation | + Bug | canceled |
| Won't fix (Feature/Improvement) | Polite explanation and link to the .out-of-scope/ file | + category | canceled |
| Duplicate | Link to canonical issue | + category | duplicate |
| Backlog | One-paragraph note of why we're acknowledging but deferring | + category | backlog |
For a Feature / Improvement going to canceled, write or update
.out-of-scope/<concept>.md before the comment, so the comment can
include a real link.
Adapter-call sketch:
post_comment(issue: <id>, body: "<comment>")
apply_labels(issue: <id>, names: [...])
set_status(issue: <id>, name: <canonical-status>)
The adapter resolves the canonical names to vendor-specific IDs and
performs the calls. Cache resolved IDs within the session.
Workflow: quick state override
When the maintainer says "move issue 42 to Todo", trust them. Skip
grilling.
Still confirm before writing:
- Labels to add/remove
- Status to apply
- Whether to post a comment, and what kind
For Todo without prior grilling, ask once: "The issue body looks like
[summary]. Sharp enough for runway, or want to flesh it out first?" If
the body is genuinely thin, runway will produce a thin PR — say so and
let the maintainer decide.
Needs-info template
When the body isn't sharp enough yet, name the specific facts that
need to land in it. Vague "please provide more info" is useless; the
questions must let the maintainer (or reporter) edit the body once
they're answered.
> *This was generated by AI during triage.*
## Triage Notes
**What we've established so far:**
- point 1
- point 2
**What we need before this can move to Todo (@<reporter>):**
- A specific success criterion, e.g. "When X happens, the user should
see Y" — what's the testable outcome?
- Steps to reproduce on a fresh checkout (for Bugs)
- The current vs. desired behavior, in one paragraph each
Capture everything resolved during the grilling session in
"established so far" — that work must not be lost.
Resuming previous sessions
When triaging an issue with prior triage activity:
list_comments(<id>) and find prior comments authored by this skill
(they begin with the AI disclaimer).
- Parse what was already established.
- Check whether the reporter has answered any outstanding questions.
- Show the maintainer an updated picture: "Here's where we left off;
here's what the reporter said since."
- Continue the grilling session from where it stopped — never re-ask
resolved questions.
References