Claude
Skills
Sign in
Back

pm-triage

Included with Lifetime
$97 forever

Use this skill to triage a raw or untriaged issue — one that lacks a type tag, an execution-mode label, or template structure — and turn it into workable, labeled, reviewed work. pm-triage orchestrates classification (pm-templates), structural review (pm-review), and enrichment (pm-improve), then applies the type and hitl/afk labels on the operator's confirmation, and hands the now-workable issue off to pm-organize so it leaves the Triage state instead of parking there. It is the action counterpart to pm-status's triage view. Trigger whenever the user says "triage this issue", "triage NTH-42", "triage the backlog", "clean up untriaged issues", "what needs triage", "/pm-triage", or when pm-status has just surfaced Untriaged / Unknown-tag / Needs-info buckets and the operator wants to act on them. Also use it proactively after a pm-status briefing flags untriaged work, and whenever an issue clearly needs labels plus a structural pass before it can be picked up — even if the user never says the word "triage".

Code Review

What this skill does


# PM Triage

Take a raw or untriaged issue and make it workable. pm-triage is the **action counterpart to the triage *view*** that `pm-status` renders: pm-status *finds* what needs triage (the Untriaged / Unknown-tag / Needs-info buckets); pm-triage *performs* it.

## Usage

```
/pm-triage [<issue-id>]
```

- `<issue-id>` *(issue key e.g. `FUNC-12`, optional)* — the issue to triage; omit to work the whole triage backlog.

Writes are confirmation-gated: the type + `hitl`/`afk` labels (and any enrichment comment) are applied only on the operator's confirmation.

It does that by orchestrating three skills that already exist — it does not reimplement them:

- `pm-templates` — classify the issue's type tag
- `pm-review` — structural check against that tag's template
- `pm-improve` — enrichment (repo context, related issues, clarifying questions)

…and then it adds the one thing none of those three do: on the operator's confirmation, it **applies the triage decision** — the type-tag label and the `hitl`/`afk` execution-mode label — so the issue actually leaves the untriaged state. (`pm-review` only *flags* a missing execution-mode label; `pm-improve` only posts a comment. Neither labels the issue. That gap is pm-triage's reason to exist.)

## When to use this

- An issue is raw — filed by a stakeholder, auto-mirrored from GitHub, or filed in a hurry — and lacks a type tag, an execution-mode label, or template structure.
- `pm-status` surfaced a triage bucket and the operator wants to act on it.
- The operator says "triage this issue", "triage NTH-42", "clean up the triage backlog", "/pm-triage".

**When not to use it.** If the issue is already tagged, labeled, and structured, there is nothing to triage — running pm-triage just produces noise. If the operator wants *only* a structural review, that is `pm-review`; *only* enrichment, that is `pm-improve`. pm-triage is the bundle — reach for it when you want the whole pass, not one slice.

## The triage pipeline (one issue)

Steps 1-6 are read-only and run without confirmation — they cost nothing but tokens, and the operator needs the whole proposal before deciding anything. Step 7 applies the labels (always confirmed). Step 8 hands the now-workable issue off to `pm-organize` so it leaves the Triage state — also confirmed, and routed entirely through `pm-organize`'s existing write path (pm-triage adds no new write codepath of its own).

### Step 1: Fetch the issue

Read it from the tracker — the same fetch `pm-review` uses (`linear issue view <ID> --json` / `gh issue view <NUM> --repo <org/repo> --json ...`). If the tracker is unreachable, stop with a transport error; do not triage from cached state — the cache can carry stale labels, and triage decisions hinge on the current label set.

### Step 2: Classify the type tag

Delegate to `pm-templates`' classifier (the logic lives there — do not duplicate it). It resolves in order: an explicit `[tag]` title prefix → a tracker label that maps to a manifest tag → inference from the body against each tag's `description`.

The classification result determines the path:

- **Explicit or label-matched** → the tag is firm; continue.
- **Inferred** → a *soft* classification. Surface it — "I read this as a `bug`; confirm or correct" — and wait. Everything downstream (which template `pm-review` checks against, which enrichment streams `pm-improve` weights) depends on the tag being right, so a wrong guess wastes the whole pass.
- **A tag-shaped signal that is not in the manifest** (e.g. a `[migration]` prefix, but the project's manifest has no `migration` tag) → this is the *Unknown-tag* case. Do not invent a tag. Surface it as a taxonomy decision: register the tag via `pm-templates`, or re-classify the issue under an existing tag. pm-triage stops here for this issue until the operator decides.
- **No signal at all** → propose the best-fit tag from the manifest descriptions and confirm before continuing.

### Step 3: Structural review

Run `pm-review` against the issue at the classified tag. Capture its findings — which required sections are present, missing, or placeholder-only; whether the acceptance criteria are testable.

pm-triage does **not** rewrite the body to close those gaps. Carry the gap list into the proposal instead — the operator decides which gaps to fill, and fills them by editing the issue (a `pm-issues` update) or by answering the clarifying questions from the next step. Auto-rewriting someone's issue body is a surprise; reporting what is missing is a service.

### Step 4: Enrichment

Run `pm-improve` against the issue. Capture its enrichment draft — repo context, related issues, clarifying questions. Hold it as a *proposed comment*; do not post it yet.

### Step 5: Propose an execution mode

Every workable issue carries a `hitl` (human-in-the-loop) or `afk` (agent-runnable) label. `pm-review` deliberately refuses to guess this for an existing issue — and it is right not to, because a silent guess that lands wrong routes work to the wrong queue.

pm-triage's job is to *propose*, not to guess silently. Start from the tag's `hitl_default_hint` in the manifest, then adjust for what the issue actually says:

- Needs manual verification, a design decision, or UX judgment → `hitl`.
- Well-specified, mechanically checkable, no judgment call → `afk`.

Present the proposal **with its reason** so the operator can correct it in one word. The reason is what makes the proposal reviewable instead of a coin flip.

### Step 6: Assemble the triage proposal

Combine Steps 2-5 into one proposal (format below) — one screen with everything the operator needs to say yes or adjust.

### Step 7: Confirm and apply

Show the proposal. On the operator's confirmation:

- **Apply the labels.** Add the type-tag label (the canonical/first entry from the manifest's `labels.<tracker>`) and the chosen `hitl`/`afk` label to the issue:
  - Linear: `node "$CLAUDE_PLUGIN_ROOT/hooks/bin/pm-issue.js" update --id <ID> --add-label "<type>" --add-label "<hitl|afk>"`
    The verb fetches existing labels via GraphQL, merges (idempotent on both add and remove), and writes the union back in one call. Do **not** call `linear issue update --label` directly — its `--label` flag is replace-semantics, not add-semantics, and any labels not explicitly listed are silently dropped (NTH-511).
  - GitHub: `gh issue edit <NUM> --repo <org/repo> --add-label "<type>" --add-label "<hitl|afk>"`
    `gh` has native add/remove flags, so the raw CLI is safe here.
  - If a label does not exist in the tracker yet, create it first (`linear label create -n <name>` / `gh label create <name>`) — a label named in the manifest but absent from the tracker matches nothing.
- **Offer the enrichment comment separately.** Posting the `pm-improve` draft as a comment is a second, distinct write — ask for it on its own ("post the enrichment as a comment on <ID>?"), because the operator may want the labels without the comment.
- **Verify the apply landed (optional, Linear only).** If verification is needed, run `node "$CLAUDE_PLUGIN_ROOT/hooks/bin/pm-cache.js" verify-issue --slug <slug> --id <ID>` — it fetches fresh state via GraphQL, compares to the cache, and reports any divergence. Add `--update` to also merge the fresh state back so the next briefing sees it without a full delta-sync.

For a single issue, one confirmation covers the labels and the enrichment comment is its own yes/no. For a batch, see below.

> Label application on Linear MUST go through `pm-issue.js update` (NTH-511, landed in 0.28.0). The verb wraps the fetch+merge so the replace-semantics of `linear issue update --label` can't accidentally drop labels. GitHub's `gh issue edit --add-label/--remove-label` are add/remove-semantics natively, so the raw `gh` call is fine.

### Step 8: Hand off to pm-organize

Labeling makes the issue *workable*. It does not move it out of the tracker's **Triage** state — without this step the issue stays parked in Triage even though

Related in Code Review