Claude
Skills
Sign in
Back

pm-review

Included with Lifetime
$97 forever

Use this skill when reviewing the quality and completeness of an existing Linear or GitHub issue — does it have a type tag, does it match the template for that type, are the required sections filled, is the acceptance criteria testable. Trigger phrases "review this issue", "review issue <ID>", "is this issue ready", "check the issue", "audit this issue", "/pm-review", "review the bug I just filed", "lint the issue". Also use proactively when an issue is about to be picked up for work (handoff into "In Progress") and the operator wants a sanity pass first. Produces a structured review report; with `--apply`, posts the findings as a comment on the issue.

Security

What this skill does


# PM Review

Audit an existing issue against the template for its type. The review is **structural** — it checks whether the issue has a type, whether the body matches the registered template, and whether each required section has substantive content. It does not enrich (that's `pm-improve`) and it does not rewrite the issue (only the operator does that, after deciding which findings to accept).

The reviewer's job is to be honest, specific, and *useful* — flag what's actually missing, not generic "could be more detailed." When everything is fine, say so. Reviewer fatigue ("everything always has 17 nits") destroys the signal.

## What the reviewer checks

For an issue ID, in order:

1. **Has a type tag.** The issue body has a `[bug]` / `[feature]` / `[task]` / `[spike]` prefix in the title, **or** the issue carries a tracker label that maps to a known tag in the `pm-templates` manifest (`labels.linear[]` / `labels.github[]`).
2. **Matches a known tag.** The classified tag exists in `_manifest.json`. Unknown → flag and stop; suggest registering the tag via `pm-templates` or correcting the label.
3. **Has an execution-mode label** (`hitl` or `afk`). Every issue must carry one or the other. Missing → flag with "Issue has no execution-mode label — file under `hitl` (human-in-loop) or `afk` (agent-runnable)." Don't infer; ask the operator to apply the label explicitly. The `hitl_default_hint` from the manifest is for *creation*, not for retroactive classification.
4. **Has every required section.** For each entry in the tag's `required_sections`, the body contains a markdown `## <Section>` heading **and** non-placeholder content underneath it. Placeholder content (`[One sentence: ...]`, empty bullets, `TBD`, `TODO`, just the heading) counts as missing.
5. **Acceptance criteria are testable.** For tags whose required_sections include "Acceptance Criteria", each criterion must be a verifiable statement. Reject vague verbs without targets: "improve performance", "enhance UX", "handle edge cases" with no specifics. A criterion is OK when a reviewer could write a test or click-through to confirm pass/fail.
6. **Conventions check (if configured).** If `<repo>/.claude/pm/conventions.md` exists, the reviewer consults `pm-conventions` for project-specific rules (issue title format, required labels, naming patterns) and flags conventions violations as a **separate** report section so the user can tell structural issues from house-style issues.

The reviewer does **not** check whether the work is a good idea, scope it, or estimate it. Those are upstream decisions; the reviewer only validates that what's been written is reviewable.

## Inputs

- An issue ID (Linear `ENG-42`) or GitHub issue number (`#123`).
- Optionally, a tag override (`--tag feature`) if the operator wants to review against a specific template instead of classifying.
- Optionally, `--apply` to post the review as a comment on the issue. Without it, the review prints to the terminal and goes nowhere.

## Workflow

### Step 1: Fetch the issue

```bash
# Linear (CLI preferred — falls back to MCP if missing)
linear issue view <ID> --json

# GitHub
gh issue view <NUMBER> --repo <org/repo> --json number,title,body,labels,assignees,url
```

If both fail, stop with a transport error — do not review from cached state (cache lacks the up-to-date labels the classifier needs).

### Step 2: Classify the tag

Apply `pm-templates`' classifier (the resolution logic lives there; do not duplicate it here):

1. If `--tag <X>` was passed, use it directly.
2. Else inspect the title for a `[<tag>]` prefix.
3. Else read the issue's labels and look up the canonical tag via the manifest's `labels.<tracker>` array.
4. Else infer from the body content + the manifest's `description` field. **Mark this as a soft classification** in the report so the user knows the tag was inferred.

If nothing matches, the review's first finding is "**No type tag** — issue cannot be reviewed until a tag is assigned." Stop checks 3-4.

### Step 3: Load the template

Resolve the template body for the classified tag via `pm-templates` (repo override → user overlay → plugin default). Read the `_manifest.json` entry to get `required_sections` and `optional_sections`.

### Step 4: Section-by-section check

For each `required_sections` heading:

- **Present** — the body contains `## <Section>` exactly. Headings with extra suffixes (`## Goal (revised)`) count as present.
- **Filled** — at least one non-placeholder, non-empty line under the heading. Placeholder detection: lines that match the template's example text verbatim, lines that are just `TBD` / `TODO` / `?`, or empty bullet lists (`- [ ]` with no text after).
- **Testable** (acceptance criteria only) — each `- [ ]` item names a behavior or outcome that a reviewer could verify. Apply the heuristic: would a tester know whether this passed?

For each `optional_sections` heading: if present, just note it. If absent, do not flag.

### Step 5: Conventions check (if a conventions file exists)

If `<repo>/.claude/pm/conventions.md` exists, pass it through `pm-conventions` to extract rules that apply to issues (title format, required labels, prefix patterns, banned phrases). Run them against the issue and emit a separate "**Conventions**" block in the report.

If no conventions file exists, skip — do not invent house style.

### Step 6: Write the report

Use this exact structure:

```markdown
# Review: <ID> · <Title>

**Tag:** <tag>  (<classification source: explicit-prefix | label | inferred>)
**Mode:** <hitl | afk | MISSING>
**Template:** <path used>

## Structural findings

- [PASS] Execution mode — `hitl` label present
- [PASS] What's Broken — present, filled
- [PASS] Steps to Reproduce — present, 2 numbered steps
- [FAIL] Acceptance Criteria — heading present, but all criteria are placeholders

## Conventions findings   *(only if conventions_path is set)*

- [PASS] Title starts with `[bug]`
- [FAIL] Missing required label `area:*` (see conventions §labels)

## Verdict

- [ ] Ready to start work
- [x] Needs more information before starting

**Suggested next step:** Open the issue and fill the empty acceptance criteria, then re-run `/pm-review <ID>`.
```

Keep findings to one line each. The verdict is binary — ready or not. If everything passes, the report is short and that's the right shape:

```markdown
# Review: ENG-42 · Add JWT refresh
**Tag:** feature  (label)
All required sections present and filled. No conventions violations.
**Verdict:** Ready to start work.
```

When the tag had to be **inferred** (no explicit `[bug]` prefix, no matching tracker label — Step 2 fell through to description-matching), call that out in the same `Tag:` line so the operator sees what the classifier guessed and can correct it before the rest of the review is trusted:

```markdown
# Review: ENG-58 · Refactor auth middleware
**Tag:** task  (inferred — soft)
**Template:** ${CLAUDE_PLUGIN_ROOT}/skills/pm-templates/templates/task.md

> ⚠ Tag was inferred from issue content (no `[tag]` prefix, no mapped tracker label).
> If this is actually a `feature` or `bug`, the structural checks below may be against the wrong template.
> Add the right label in the tracker or re-run with `--tag <X>`.

## Structural findings
- [PASS] Goal — present, filled
- [PASS] Acceptance Criteria — present, 3 testable items
```

The `(inferred — soft)` marker in the `Tag:` line is the canonical format. The warning callout is what the operator actually reads — most people won't notice the parenthetical alone.

### Step 7: Apply (optional)

If the operator passed `--apply`, post the report as a comment on the issue. The comment is signed so future reviewers know it was machine-generated:

```bash
# Linear (CLI or GraphQL API)
linear issue comment create <ID> --body-file "$REVIEW_FILE"

# GitHub
gh issue comment <NUMBER> --repo <org/repo> --body-file "$REVIEW_FILE"
```

### The comment header — visible provenance + machine marker

Every posted co

Related in Security