# Triage Issues

> Analyze open GitHub issues and produce a sequenced implementation plan — grouping issues into parallel batches, ordering those batches, and tagging each issue with its recipe route. Use when user says "triage issues", "prioritize issues", or "plan issue order".

- Skill: `talont-org/triage-issues` (Agent Skill)
- Install (CLI): `npx skillmds@latest add talont-org/triage-issues`
- Raw SKILL.md: https://api.skillmd.com/api/skills/talont-org/triage-issues/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: TalonT-Org (https://skillmd.com/u/talont-org)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/talont-org/triage-issues

---


# Issue Triage Skill

Analyze open GitHub issues, classify each into a recipe route, group them into parallel implementation batches, and produce a structured triage report.

## When to Use

- User says "triage issues", "prioritize issues", or "plan issue order"
- Before starting a multi-issue implementation batch
- When a backlog needs sequencing into implementable batches
- As input to a pipeline that processes issues in batch order

## Critical Constraints

**NEVER:**
- Modify any source code files
- Create or edit Python, YAML, or config files
- Guess recipe classification when confidence is low — escalate to user
- Apply GitHub labels when `--no-label` is passed
- Skip human escalation for ambiguous issues
- Add useless comments to the codebase — do not use the codebase as a notepad
- Create files outside `{{AUTOSKILLIT_TEMP}}/triage-issues/` directory
- Use `--body` shell substitution (`--body "$(...)`) for `gh issue edit` — always write to
  `{{AUTOSKILLIT_TEMP}}/triage-issues/edit_body_{timestamp}.md` and use `--body-file`
- Run subagents in the background (`run_in_background: true` is prohibited)

**ALWAYS:**
- Use `model: "sonnet"` when spawning all subagents via the Task tool
- Pause for human input on ambiguous classifications
- Write the triage report and manifest to `{{AUTOSKILLIT_TEMP}}/triage-issues/` (relative to the current working directory)
- Use `gh` CLI for all GitHub operations (not raw API calls)
- Include rationale for every recipe classification
- Record human decisions in the final report

## Workflow

### Step 0: Parse Arguments

Parse optional arguments from the user's invocation:

- `--batch-size N` — maximum issues per batch (default: 4)
- `--no-label` — skip GitHub label application after triage
- `--dry-run` — run analysis but skip label application even if `--no-label` is not set
- `--collapse` — invoke `collapse-issues` after split analysis to consolidate related issues
- `--enrich` — for each issue classified as `recipe:implementation`, generate and append
               structured requirements (`REQ-{GRP}-NNN` format) to the issue body.
               Skips issues that already have a `## Requirements` section (idempotent).
               No effect on `recipe:remediation` issues.

### Step 1: Authenticate and Fetch Issues

```bash
# Verify GitHub authentication
gh auth status

# Fetch all open issues with required fields, excluding in-progress issues
gh issue list --state open --json number,title,body,labels,url,assignees --limit 200 \
  | jq '[.[] | select(.labels | map(.name) | contains(["in-progress"]) | not)]'
```

Issues carrying the `in-progress` label are actively being processed by a pipeline session
and are excluded from triage to prevent duplicate work.

If `gh auth status` fails, abort with a clear error message.

If there are zero open issues (after filtering), skip to Step 7 and output an empty report.

### Step 2a: Parallel Split Analysis

Before codebase analysis, run `issue-splitter` for every open issue to detect mixed-concern issues and expand the working set.

Launch up to 8 subagents in parallel (`model: "sonnet"`), one per issue. Each subagent invokes:

```
/autoskillit:issue-splitter --issue {N} --repo {owner/repo} [--no-label if --no-label was passed] [--dry-run if --dry-run was passed]
```

For each subagent result, parse the `---issue-splitter-result---` block and build the **expanded working set**:

- `decision=no-split`: keep the original issue in the working set; note the pre-classified `route` as a seed for Step 3 (Recipe Classification) — Step 3 still re-verifies
- `decision=split`: remove the original issue from the working set; add its `sub_issues` list instead
- `decision=error`: log a warning; keep the original issue as-is (fail-safe)

Proceed to Step 2c (if `--collapse`) or Step 2b with the expanded working set.

**Flag propagation:** When `triage-issues` is invoked with `--no-label`, pass `--no-label` to each `issue-splitter` call. When `--dry-run` is active, pass `--dry-run`. This ensures split analysis is observable without mutating GitHub.

### Step 2c (optional): Collapse Related Issues

If `--collapse` was passed:

Invoke the collapse-issues skill to consolidate related issues before analysis:

```
/autoskillit:collapse-issues --repo {owner/repo} [--dry-run if --dry-run was passed] [--no-label if --no-label was passed]
```

Parse the `---collapse-issues-result---` block from the skill output:
- `groups_formed`: log "Collapsed N groups ({M} issues → {groups_formed} combined issues)"
- Update the working issue list: remove closed originals, add new combined issue numbers
- If the skill returns an error, log a warning but continue triage with the original issue list (fail-safe)

**Flag propagation:** `--dry-run` and `--no-label` are passed through to collapse-issues.

The collapse step runs after splitting is complete so it sees the fully-decomposed issue list.

### Step 2b: Parallel Issue Analysis

Launch parallel subagents (up to 8) to analyze each issue. Each subagent receives one issue and must identify:

- **Affected systems** — which parts of the codebase the issue touches (e.g., `recipe/`, `server/`, `execution/`)
- **Components** — specific modules, classes, or functions referenced or implied
- **File paths** — explicitly mentioned files or files inferred from the description
- **Dependencies** — explicit issue references (`#N`) or inferred dependencies from content overlap

Use `model: "sonnet"` for all subagents.

### Step 3: Recipe Classification

**Priority signal — Validated Audit Report:**
If the issue title contains "Validated Audit Report" (the exact prefix set by `prepare-issue`
when creating an issue from a validated audit output), classify as `recipe:implementation`
with `high` confidence. Do NOT allow issue scope (number of findings / large scope) to override
this signal — a 40-finding audit is structural quality improvement work, not broken behavior.
Skip the "Is existing behavior broken?" analysis for these issues.

Classify each issue into a recipe route using the primary behavioral criterion — is existing behavior broken?

**Is existing behavior broken?**
- **Yes** — existing code crashes, raises an exception, returns wrong data, or fails an assertion when executed → `recipe:remediation`
- **No** — a capability is missing, a guard doesn't exist yet, a routing path was never built, or behavior needs to be added → `recipe:implementation`

The key distinction is **broken vs. missing**:
- **Remediation** fixes code that runs and errors. The code exists, it executes, and it produces a wrong result or exception.
- **Implementation** fills a gap. Nothing existing is broken — the capability, guard, or behavior simply doesn't exist yet. Even if the gap was discovered during a runtime scenario (e.g., an orchestrator made a wrong choice because no guardrail existed), if the fix is "add something new" rather than "fix something that errors", it routes to implementation.

Examples that route to `remediation`:
- A command that crashes with a traceback
- An API that returns wrong data (e.g., negative duration from clock regression)
- A file parser that raises an exception on valid input
- A test that fails with an assertion error due to a real code bug

Examples that route to `implementation`:
- Adding a new CLI flag, skill, or recipe step
- Adding a missing return field to a response JSON
- Adding orchestrator discipline rules or guardrails that don't exist yet
- A plan/recipe that scoped too narrowly (gap in design, not a crash)
- Improving error messages, refactoring, writing documentation
- Adding support for a new file format — regardless of scope or complexity
- A validated audit report with structural/quality findings (split oversized files, add missing
  docstrings, naming consistency) — regardless of finding count

**Common misclassification: "the orchestrator did the wrong thing"**
When an LLM orchestrator bypasses routing, retries instead of escalating, or ignores a failure — that is almost always a **gap** (missing guardrail, missing discipline rule), not a runtime error. The orchestrator didn't crash; it made an unguarded choice. Route to `implementation` unless the orchestrator hit an actual exception.

For each issue, record:
- The assigned route (`implementation` or `remediation`)
- Confidence level (`high` or `low`)
- Rationale (1 sentence explaining why)

### Step 3b: Human Escalation for Ambiguous Issues

For each issue where you cannot confidently assign a recipe route, you MUST pause
and present it to the user. Do NOT silently guess.

Present each ambiguous issue as follows:

---
**Ambiguous Issue: #{number} — "{title}"**

**Summary:** {1-2 sentence summary of what the issue asks for}

**Conflicting Signals:**
- Signal A suggests `implementation`: {reason}
- Signal B suggests `remediation`: {reason}

**Decision needed:** Route to `implementation` or `remediation`? (Or type "skip" to exclude)

**Open questions that would change routing:**
- {question that, if answered, would make the route clear}
---

Wait for the user's response before continuing to the next ambiguous issue.
Record all human decisions for inclusion in the triage report.

### Step 3c: Requirement Enrichment (only when `--enrich` is passed)

For each issue in the working set classified as `recipe:implementation`:

1. Fetch issue body:
   ```bash
   gh issue view {N} --json body -q .body
   ```
2. If `## Requirements` section already present in the body: skip (idempotent).
3. In-context requirement generation using the issue title, body, and classification
   rationale already in context:
   - Trace: "What must be true for this functionality to exist?"
   - Group by co-implementation concern (short uppercase abbreviation, 2–5 letters).
   - Format: `**REQ-{GRP}-NNN:** {single-sentence condition statement}.`
4. If `--dry-run` or `--no-label` is active: print generated requirements to stdout
   per issue but skip `gh issue edit`. Record `requirements_generated: true` in manifest.
5. Otherwise, append via:
   ```bash
   ts=$(date +%Y-%m-%d_%H%M%S)
   EDIT_BODY_FILE="{{AUTOSKILLIT_TEMP}}/triage-issues/edit_body_${ts}.md"
   REQUIREMENTS_FILE="{{AUTOSKILLIT_TEMP}}/triage-issues/requirements_${ts}.md"
   mkdir -p "{{AUTOSKILLIT_TEMP}}/triage-issues"

   # Fetch current body to temp file (avoids shell interpolation):
   gh issue view {N} --json body -q .body > "${EDIT_BODY_FILE}"

   # Populate ${REQUIREMENTS_FILE} with the generated requirements content, then:
   printf '\n\n## Requirements\n\n' >> "${EDIT_BODY_FILE}"
   cat "${REQUIREMENTS_FILE}" >> "${EDIT_BODY_FILE}"

   gh issue edit {N} --body-file "${EDIT_BODY_FILE}"
   ```
6. If the issue is too vague for clean extraction: skip silently and record
   `requirements_generated: false` in the manifest for that issue.

This step is in-context only — no subagents are spawned for enrichment.

`recipe:remediation` issues are not enriched regardless of the `--enrich` flag.

### Step 4: Build Conflict Graph

Build a conflict graph where:
- Each issue is a node
- Two issues share an edge if they touch overlapping systems, components, or file paths
- Edge weight reflects the degree of overlap (number of shared components)

Issues that touch completely independent systems have no edges and can safely run in parallel.

### Step 5: Greedy Batch Partitioning

Partition issues into batches of at most `batch_size` using a greedy graph-coloring approach:

1. Sort issues by edge count (most conflicts first)
2. For each issue, assign it to the earliest batch where it has no conflicts with existing members
3. If all existing batches conflict, create a new batch
4. Issues with no conflicts go into the earliest batch with remaining capacity

### Step 6: Order Batches

Order the batches for sequential execution:

1. **Dependencies first** — if issue B depends on issue A, A's batch must come before B's batch
2. **Foundation first** — batches touching infrastructure/core come before batches touching consumers
3. **Priority labels** — batches containing `priority:high` issues come earlier (tiebreaker)
4. **Age** — batches containing older issues come earlier (final tiebreaker)

### Step 7: Write Outputs

Compute timestamp: `YYYY-MM-DD_HHMMSS`.
Ensure `{{AUTOSKILLIT_TEMP}}/triage-issues/` exists.

**7a. Triage report:** `{{AUTOSKILLIT_TEMP}}/triage-issues/triage_report_{ts}.md`

The report contains:
- Ordered list of batches with issues, recipe assignments, and rationale
- Dependency/conflict notes explaining batch separation
- Summary statistics (total issues, batch count, recipe distribution)
- Human decisions section (which issues were escalated, what was decided)

**7b. Machine-readable manifest:** `{{AUTOSKILLIT_TEMP}}/triage-issues/triage_manifest_{ts}.json`

```json
{
    "generated_at": "{ISO timestamp}",
    "batch_size": 4,
    "total_issues": 12,
    "batch_count": 3,
    "recipe_distribution": {"implementation": 8, "remediation": 4},
    "batches": [
        {
            "batch": 1,
            "priority": "first",
            "issues": [
                {
                    "number": 42,
                    "title": "Add user auth",
                    "recipe": "implementation",
                    "confidence": "high",
                    "systems": ["auth", "api"],
                    "rationale": "Well-defined feature with clear acceptance criteria",
                    "requirements_generated": true
                }
            ]
        }
    ],
    "skipped_issues": [],
    "human_decisions": [
        {"number": 55, "decision": "remediation", "reason": "User chose investigation-first"}
    ]
}
```

Issues not enriched (`recipe:remediation`, or `--enrich` not passed) emit `"requirements_generated": null`.

**7c. Label application (unless `--no-label` is passed):**

Ensure recipe labels exist (idempotent):

```bash
gh label create "recipe:implementation" --description "Route through implementation recipe" --color "0E8A16" --force
gh label create "recipe:remediation" --description "Route through remediation recipe" --color "D93F0B" --force
```

For each triaged issue, apply its recipe label:

```bash
gh issue edit {number} --add-label "recipe:{recipe}"
sleep 1  # Rate-limit discipline: 1s between mutating calls
```

## Output Location

```
{{AUTOSKILLIT_TEMP}}/triage-issues/
  triage_report_{ts}.md       # Human-readable triage report
  triage_manifest_{ts}.json   # Machine-readable manifest for downstream pipelines
```

## Output Fields (for recipe capture)

After the triage report and manifest are written, emit the following structured output
tokens as the very last lines of your text output:

> **IMPORTANT:** Emit the structured output tokens as **literal plain text with no
> markdown formatting on the token names**. Do not wrap token names in `**bold**`,
> `*italic*`, or any other markdown. The adjudicator performs a regex match on the
> exact token name — decorators cause match failure.

```
triage_report = {absolute_path_to_report_file}
triage_manifest = {absolute_path_to_manifest_file}
total_issues = {integer_count}
batch_count = {integer_count}
recipe_distribution = {json_distribution_dict}
```

These emit lines are consumed by `capture:` in orchestrating recipes. The
`triage_manifest` path is the primary output used by downstream recipe steps.

Example emit block:

```
triage_report = {{AUTOSKILLIT_TEMP}}/triage-issues/triage_report_20260310_120000.md
triage_manifest = {{AUTOSKILLIT_TEMP}}/triage-issues/triage_manifest_20260310_120000.json
total_issues = 12
batch_count = 3
recipe_distribution = {"implementation": 8, "remediation": 4}
```

## Related Skills

- **`/autoskillit:analyze-prs`** — Similar batch analysis pattern, but for PRs instead of issues
- **`/autoskillit:make-groups`** — Groups requirements for planning; triage-issues groups issues for execution
- **`/autoskillit:investigate`** — Can be used to deep-dive individual issues before triage
