Issue Triage
Let: T := bun ${CLAUDE_PLUGIN_ROOT}/skills/issue-triage/triage.ts — triage CLI | κ := complexity score
Default: T or T list (no args = list).
Create GitHub issues, assign Size/Priority labels, manage blockedBy dependencies and parent/child relationships.
Companion of /R-dev: run before /R-dev. Not a STEPS entry — /R-dev does not invoke this skill.
Instructions
- List all open issues:
T / T list | List untriaged only: T list --untriaged
- ∀ issue: determine Size, Priority, κ (see Complexity Scoring)
- Set values:
T set <number> --size <S> --priority <P>
- Create issues:
T create --title "Title" [--body "Body"] [--label "bug,frontend"] [--size M] [--priority High] [--type feat] [--lane b] [--parent 163]
- → ask userif unsure about Size ∨ Priority.
Size Guidelines
| Size |
Description |
Example |
| XS |
Trivial, < 1 hour |
Typo fix, config tweak |
| S |
Small, < 4 hours |
Single file change, simple feature |
| M |
Medium, 1-2 days |
Multi-file feature, requires testing |
| L |
Large, 3-5 days |
Complex feature, architectural changes |
| XL |
Very large, > 1 week |
Major refactor, new system |
Canonical labels written: size:S / size:F-lite / size:F-full. --size also accepts legacy XS / S / M / L / XL and aliases them (XS→S, M→F-lite, L/XL→F-full).
Priority Guidelines
| Priority |
Description |
Action |
| Urgent (P0) |
Blocking or critical |
Do immediately |
| High (P1) |
Important for current milestone |
Do this sprint |
| Medium (P2) |
Should be done soon |
Plan for next sprint |
| Low (P3) |
Nice to have |
Backlog |
Commands
list — Show open issues
| Flag |
Description |
| (none) |
Tree of all open issues with N-level parent-child hierarchy. Parents with ≥1 closed child show … ✓ Done. |
--untriaged |
Flat table of issues missing Size or Priority |
--json |
JSON output (all open issues); combine with --untriaged to filter |
set <num> — Update an existing issue
| Flag |
Description |
--size <S> |
Set size label — canonical S/F-lite/F-full or legacy XS/S/M/L/XL (canonical names alias to nearest legacy label) |
--priority <P> |
Set priority label (Urgent, High, Medium, Low) |
--blocked-by <REF>[,<REF>...] |
Add blocked-by dependency. REF = #N or owner/repo#N |
--blocks <REF>[,<REF>...] |
Add blocking dependency. REF = #N or owner/repo#N |
--rm-blocked-by <REF>[,<REF>...] |
Remove blocked-by dependency |
--rm-blocks <REF>[,<REF>...] |
Remove blocking dependency |
--parent <REF> |
Set parent issue. REF = #N or owner/repo#N |
--add-child <REF>[,<REF>...] |
Add child sub-issues |
--rm-parent |
Remove parent relationship |
--rm-child <REF>[,<REF>...] |
Remove child sub-issues |
--lane <L> |
Set lane label (optional, additive). Valid: a1, a2, a3, b, c1, c2, c3, d–o, standalone |
--type <T> |
Set org issueType (optional, additive). Valid: fix, feat, docs, test, chore, ci, perf, epic, research, refactor |
create — Create a new issue
| Flag |
Description |
--title "..." |
Issue title (required) |
--body "..." |
Issue body/description |
--label "l1,l2" |
Comma-separated labels |
--size <S> |
Set size on creation — canonical S/F-lite/F-full or legacy XS/S/M/L/XL accepted |
--priority <P> |
Set priority on creation |
--lane <L> |
Set lane on creation (label-only, additive). Valid: a1, a2, a3, b, c1, c2, c3, d–o, standalone |
--type <T> |
Set org issueType on creation (additive). Valid: fix, feat, docs, test, chore, ci, perf, epic, research, refactor |
--parent <REF> |
Set parent issue on creation. REF = #N or owner/repo#N |
--add-child <REF>[,<REF>...] |
Add existing issues as children |
--blocked-by <REF>[,<REF>...] |
Set blocked-by on creation |
--blocks <REF>[,<REF>...] |
Set blocking on creation |
Cross-repo create
Set GITHUB_REPO=<owner/repo> to retarget the CREATE to a different repo than the cwd's git remote:
# File a voiceCLI issue while cwd is lyra (or any other repo)
GITHUB_REPO=Roxabi/voiceCLI T create \
--title 'STT: audio dropout at segment boundary' \
--blocked-by Roxabi/lyra#728
Cross-repo relations (--blocked-by, --blocks, --parent, --add-child) accept OWNER/REPO#N natively — they work regardless of GITHUB_REPO.
Caveat — keep refs fully-qualified: GITHUB_REPO retargets the entire invocation. A bare #N in any ref resolves against the overridden repo, not the cwd repo → always use OWNER/REPO#N for any cross-repo ref when the env var is set.
Resolution order (detectGitHubRepo): (1) github_repo in dev-core config ∨ GITHUB_REPO env var (validated as owner/repo) → (2) fallback git remote get-url origin of the cwd.
Deferred Follow-Ups — Sibling Rule
Defer ≠ decomposition. When an issue A defers work to a new follow-up B (out-of-scope finding, post-merge gap, "do this later"), B is a sibling of A under their shared parent — NOT a child of A.
Epic E
╱ ╲
A ←—————— B B.parent = A.parent (= E)
blocked-by B.blocked-by = A (traceability of origin)
Why:
gh issue view E shows the full fan-out flat (A + B + future C…) — true scope of the epic, ¬nested cascade
/R-dev re-scan retombe correctement sur l'épic origin pour tout follow-up
- Multi-level deferrals (A→B→C) stay flat under E — ¬arbre profond ingérable
Decomposition vs deferral:
| Pattern |
Parent-child? |
Example |
| Epic → phase (planned decomposition) |
✓ child of epic |
/R-spec smart-splitting: phase 1, phase 2 are children of epic |
| Issue → follow-up (deferral, post-hoc) |
✗ sibling under shared parent |
/R-fix Phase 5 Defer: out-of-scope finding becomes sibling |
| Bug → regression (related ¬caused) |
✗ standalone |
New bug surfaced post-merge, ¬child, ¬sibling necessarily |
Recipe — defer A → create follow-up B:
# 1. Resolve A's parent (may be null if A is top-level)
A_PARENT=$(gh api graphql -f query="query{repository(owner:\"$OWNER\",name:\"$REPO\"){issue(number:$A){parent{number}}}}" \
--jq '.data.repository.issue.parent.number // empty')
# 2. Create B as sibling: same parent as A, blocked-by A
T create \
--title "{deferred title}" \
--body "**Origin:** #${A} (deferred from ...)\n\n{details}" \
--blocked-by "#${A}" \
${A_PARENT:+--parent "#${A_PARENT}"}
Edge cases:
Complexity Scoring
Assess κ ∈ [1,10] to inform tier (S / F-lite / F-full). Record by appending to issue body:
BODY=$(gh issue view <number> --json body --jq .body)
gh issue edit <number> --body "$BODY
<!-- complexity: <score> -->"
<!-- complexity: N --> is machine-parseable; downstream tools (e.g. /plan) read it.
Factors (each 1-10, weighted):
| Factor |
Weight |
1 (Low) |
5 (Medium) |
10 (High) |
| Files touched |
20% |
1-3 files |
5-10 files |
15+ files |
| Technical risk |
25% |
Known patterns |
New library/pattern in 1 domain |
New architecture |
| Architectural impact |
25% |
Single module |
Shared types, 2 modules |
Cross-domain, new abstractions |
| Unknowns count |
15% |
0 unknowns |
1-2 open questions |
3+ unknowns |
| Domain breadth |
15% |
1 domain |
2 domains |
3+ domains |
Formula: κ = round(files × 0.20 + risk × 0.25 + arch × 0.25 + unknowns × 0.15 + domains × 0.15)
Tier mapping:
| Score |
Tier |
Process |
Agent Mode |
| 1-3 |
S |
Worktree + direct implementation + PR |
Single session, no agents |
| 4-6 |
F-lite |
Worktree + subagents + /code-review |
Task subagents (1-2 domain + tester) |
| 7-10 |
F-full |
Bootstrap + worktree + agent team + /code-review |
TeamCreate (3+ agents, test-first) |
κ is advisory. Human judgment overrides. → ask userif score ≠ intuition.
Example Workflow
T list
T list --untriaged
T set 42 --size M --priority High
T set 91 --blocked-by 117
T set 117 --blocks 91,118
T set 91 --rm-blocked-by 117
T set 164 --parent 163
T set 163 --add-child 164,165,166
T set 164 --rm-parent
T set 163 --rm-child 166
# Cross-repo dependencies (owner/repo#N format)
T set 42 --blocked-by Roxabi/lyra#728
T set 42 --blocks Roxabi/voiceCLI#94
T create \
--title "research: compare against example/repo" \
--body "Deep analysis of example/repo" \
--label "research" \
--size S --priority Medium \
--parent 163
T create \
--title "epic: improve CI pipeline" \
--size L --priority High \
--add-child 150,151,152
# Lane and type (additive, optional)
T set 42 --lane b
T set 42 --type feat
T set 42 --size M --priority High --lane c1 --type fix
Completion
- Success: print one line:
Done. Next: /R-dev #N. Stop.
- Failure: return error.
$ARGUMENTS
1---2name: issue-triage3description: Issue Triage4---56# Issue Triage78Let: T := `bun ${CLAUDE_PLUGIN_ROOT}/skills/issue-triage/triage.ts` — triage CLI | κ := complexity score910Default: `T` or `T list` (no args = list).1112Create GitHub issues, assign Size/Priority labels, manage blockedBy dependencies and parent/child relationships.1314Companion of `/R-dev`: run **before** `/R-dev`. Not a STEPS entry — `/R-dev` does not invoke this skill.1516## Instructions17181. List all open issues: `T` / `T list` | List untriaged only: `T list --untriaged`192. ∀ issue: determine Size, Priority, κ (see [Complexity Scoring](#complexity-scoring))203. Set values: `T set <number> --size <S> --priority <P>`214. Create issues: `T create --title "Title" [--body "Body"] [--label "bug,frontend"] [--size M] [--priority High] [--type feat] [--lane b] [--parent 163]`225. → ask userif unsure about Size ∨ Priority.2324## Size Guidelines2526| Size | Description | Example |27|------|-------------|---------|28| **XS** | Trivial, < 1 hour | Typo fix, config tweak |29| **S** | Small, < 4 hours | Single file change, simple feature |30| **M** | Medium, 1-2 days | Multi-file feature, requires testing |31| **L** | Large, 3-5 days | Complex feature, architectural changes |32| **XL** | Very large, > 1 week | Major refactor, new system |3334**Canonical labels written:** `size:S` / `size:F-lite` / `size:F-full`. `--size` also accepts legacy `XS / S / M / L / XL` and aliases them (`XS`→`S`, `M`→`F-lite`, `L`/`XL`→`F-full`).3536## Priority Guidelines3738| Priority | Description | Action |39|----------|-------------|--------|40| **Urgent** (P0) | Blocking or critical | Do immediately |41| **High** (P1) | Important for current milestone | Do this sprint |42| **Medium** (P2) | Should be done soon | Plan for next sprint |43| **Low** (P3) | Nice to have | Backlog |4445## Commands4647### `list` — Show open issues4849| Flag | Description |50|------|-------------|51| *(none)* | Tree of all open issues with N-level parent-child hierarchy. Parents with ≥1 closed child show `… ✓ Done`. |52| `--untriaged` | Flat table of issues missing Size or Priority |53| `--json` | JSON output (all open issues); combine with `--untriaged` to filter |5455### `set <num>` — Update an existing issue5657| Flag | Description |58|------|-------------|59| `--size <S>` | Set size label — canonical `S/F-lite/F-full` or legacy `XS/S/M/L/XL` (canonical names alias to nearest legacy label) |60| `--priority <P>` | Set priority label (Urgent, High, Medium, Low) |61| `--blocked-by <REF>[,<REF>...]` | Add blocked-by dependency. REF = `#N` or `owner/repo#N` |62| `--blocks <REF>[,<REF>...]` | Add blocking dependency. REF = `#N` or `owner/repo#N` |63| `--rm-blocked-by <REF>[,<REF>...]` | Remove blocked-by dependency |64| `--rm-blocks <REF>[,<REF>...]` | Remove blocking dependency |65| `--parent <REF>` | Set parent issue. REF = `#N` or `owner/repo#N` |66| `--add-child <REF>[,<REF>...]` | Add child sub-issues |67| `--rm-parent` | Remove parent relationship |68| `--rm-child <REF>[,<REF>...]` | Remove child sub-issues |69| `--lane <L>` | Set lane label (optional, additive). Valid: `a1`, `a2`, `a3`, `b`, `c1`, `c2`, `c3`, `d`–`o`, `standalone` |70| `--type <T>` | Set org issueType (optional, additive). Valid: `fix`, `feat`, `docs`, `test`, `chore`, `ci`, `perf`, `epic`, `research`, `refactor` |7172### `create` — Create a new issue7374| Flag | Description |75|------|-------------|76| `--title "..."` | Issue title (**required**) |77| `--body "..."` | Issue body/description |78| `--label "l1,l2"` | Comma-separated labels |79| `--size <S>` | Set size on creation — canonical `S/F-lite/F-full` or legacy `XS/S/M/L/XL` accepted |80| `--priority <P>` | Set priority on creation |81| `--lane <L>` | Set lane on creation (label-only, additive). Valid: `a1`, `a2`, `a3`, `b`, `c1`, `c2`, `c3`, `d`–`o`, `standalone` |82| `--type <T>` | Set org issueType on creation (additive). Valid: `fix`, `feat`, `docs`, `test`, `chore`, `ci`, `perf`, `epic`, `research`, `refactor` |83| `--parent <REF>` | Set parent issue on creation. REF = `#N` or `owner/repo#N` |84| `--add-child <REF>[,<REF>...]` | Add existing issues as children |85| `--blocked-by <REF>[,<REF>...]` | Set blocked-by on creation |86| `--blocks <REF>[,<REF>...]` | Set blocking on creation |8788### Cross-repo create8990Set `GITHUB_REPO=<owner/repo>` to retarget the CREATE to a different repo than the cwd's git remote:9192```bash93# File a voiceCLI issue while cwd is lyra (or any other repo)94GITHUB_REPO=Roxabi/voiceCLI T create \95 --title 'STT: audio dropout at segment boundary' \96 --blocked-by Roxabi/lyra#72897```9899Cross-repo **relations** (`--blocked-by`, `--blocks`, `--parent`, `--add-child`) accept `OWNER/REPO#N` natively — they work regardless of `GITHUB_REPO`.100101**Caveat — keep refs fully-qualified:** `GITHUB_REPO` retargets the entire invocation. A bare `#N` in any ref resolves against the overridden repo, not the cwd repo → always use `OWNER/REPO#N` for any cross-repo ref when the env var is set.102103**Resolution order** (`detectGitHubRepo`): (1) `github_repo` in dev-core config ∨ `GITHUB_REPO` env var (validated as `owner/repo`) → (2) fallback `git remote get-url origin` of the cwd.104105## Deferred Follow-Ups — Sibling Rule106107**Defer ≠ decomposition.** When an issue A defers work to a new follow-up B (out-of-scope finding, post-merge gap, "do this later"), B is a **sibling** of A under their shared parent — NOT a child of A.108109```110 Epic E111 ╱ ╲112 A ←—————— B B.parent = A.parent (= E)113 blocked-by B.blocked-by = A (traceability of origin)114```115116**Why:**117- `gh issue view E` shows the full fan-out flat (A + B + future C…) — true scope of the epic, ¬nested cascade118- `/R-dev` re-scan retombe correctement sur l'épic origin pour tout follow-up119- Multi-level deferrals (A→B→C) stay flat under E — ¬arbre profond ingérable120121**Decomposition vs deferral:**122123| Pattern | Parent-child? | Example |124|---------|---------------|---------|125| **Epic → phase** (planned decomposition) | ✓ child of epic | `/R-spec` smart-splitting: phase 1, phase 2 are children of epic |126| **Issue → follow-up** (deferral, post-hoc) | ✗ sibling under shared parent | `/R-fix` Phase 5 Defer: out-of-scope finding becomes sibling |127| **Bug → regression** (related ¬caused) | ✗ standalone | New bug surfaced post-merge, ¬child, ¬sibling necessarily |128129**Recipe — defer A → create follow-up B:**130131```bash132# 1. Resolve A's parent (may be null if A is top-level)133A_PARENT=$(gh api graphql -f query="query{repository(owner:\"$OWNER\",name:\"$REPO\"){issue(number:$A){parent{number}}}}" \134 --jq '.data.repository.issue.parent.number // empty')135136# 2. Create B as sibling: same parent as A, blocked-by A137T create \138 --title "{deferred title}" \139 --body "**Origin:** #${A} (deferred from ...)\n\n{details}" \140 --blocked-by "#${A}" \141 ${A_PARENT:+--parent "#${A_PARENT}"}142```143144**Edge cases:**145- A has no parent → B has no parent either (both top-level). Consider whether A should be re-parented under a freshly-created epic if the fan-out grows.146- A is already top-level epic → defer creates child of A (epic decomposition pattern applies).147- Existing follow-up issue B mis-parented under A → fix retroactively:148 ```bash149 T set <B> --rm-parent150 T set <B> --parent "#${A_PARENT}"151 T set <B> --blocked-by "#${A}" # ensure traceability link152 ```153154## Complexity Scoring155156Assess κ ∈ [1,10] to inform tier (S / F-lite / F-full). Record by appending to issue body:157158```bash159BODY=$(gh issue view <number> --json body --jq .body)160gh issue edit <number> --body "$BODY161162<!-- complexity: <score> -->"163```164165`<!-- complexity: N -->` is machine-parseable; downstream tools (e.g. `/plan`) read it.166167**Factors (each 1-10, weighted):**168169| Factor | Weight | 1 (Low) | 5 (Medium) | 10 (High) |170|--------|--------|---------|------------|-----------|171| **Files touched** | 20% | 1-3 files | 5-10 files | 15+ files |172| **Technical risk** | 25% | Known patterns | New library/pattern in 1 domain | New architecture |173| **Architectural impact** | 25% | Single module | Shared types, 2 modules | Cross-domain, new abstractions |174| **Unknowns count** | 15% | 0 unknowns | 1-2 open questions | 3+ unknowns |175| **Domain breadth** | 15% | 1 domain | 2 domains | 3+ domains |176177**Formula:** `κ = round(files × 0.20 + risk × 0.25 + arch × 0.25 + unknowns × 0.15 + domains × 0.15)`178179**Tier mapping:**180181| Score | Tier | Process | Agent Mode |182|-------|------|---------|-----------|183| 1-3 | **S** | Worktree + direct implementation + PR | Single session, no agents |184| 4-6 | **F-lite** | Worktree + subagents + /code-review | Task subagents (1-2 domain + tester) |185| 7-10 | **F-full** | Bootstrap + worktree + agent team + /code-review | TeamCreate (3+ agents, test-first) |186187κ is advisory. Human judgment overrides. → ask userif score ≠ intuition.188189## Example Workflow190191```bash192T list193T list --untriaged194T set 42 --size M --priority High195T set 91 --blocked-by 117196T set 117 --blocks 91,118197T set 91 --rm-blocked-by 117198T set 164 --parent 163199T set 163 --add-child 164,165,166200T set 164 --rm-parent201T set 163 --rm-child 166202203# Cross-repo dependencies (owner/repo#N format)204T set 42 --blocked-by Roxabi/lyra#728205T set 42 --blocks Roxabi/voiceCLI#94206207T create \208 --title "research: compare against example/repo" \209 --body "Deep analysis of example/repo" \210 --label "research" \211 --size S --priority Medium \212 --parent 163213T create \214 --title "epic: improve CI pipeline" \215 --size L --priority High \216 --add-child 150,151,152217218# Lane and type (additive, optional)219T set 42 --lane b220T set 42 --type feat221T set 42 --size M --priority High --lane c1 --type fix222```223224## Completion225226- **Success:** print one line: `Done. Next: /R-dev #N`. Stop.227- **Failure:** return error.228229$ARGUMENTS