agentsclimarketplace

Review epic

Skill semanticpixel/abc/plugins/abc/skills/review-epic

Always Be Cooking - Claude Code plugin that drives features from plan → tracker sub-issues → parallel shipping → review → merge.

Install
npx -y skills add semanticpixel/abc --skill review-epic

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Linear · Review-only counterpart to /abc:ship-epic. Self-arming /loop that watches a Linear parent issue's sub-issues, reviews each child's PR/MR (GitHub or GitLab, routed via the `repo:` label) as it surfaces against the FULL epic context (parent spec + merged-sibling decisions + pending children's criteria), posts inline + spec-cross-referenced summary comments via the abc:reviewer subagent, and exits when the parent reaches Done. Never merges. TRIGGER when the user says "/abc:review-epic PARENT-ID", asks to "review this epic as it ships" against a Linear parent, or wants a standing reviewer session running parallel to /abc:ship-epic.

SKILL.md

25.3 KB, ~6.2k tokens by cl100k_base, as published. Nobody here has run it

/abc:review-epic — epic-context PR/MR reviewer (Linear)

Watch a Linear parent issue with sub-issues and review each child's PR/MR against the full epic context — the parent spec, the design decisions already taken in merged sibling PRs, and the acceptance criteria of children still pending. This is the reviewer half of the two-session epic-shipping pattern: /abc:ship-epic (or /abc:ship-issue on the parent) implements in one session; this skill reviews in another, holding the top-down spec context the per-PR view can't see.

Review-only. This skill never merges, never pushes, never closes issues, never transitions Linear states. Its only writes are PR/MR review comments and its own dedup markers — and those live on the PR/MR, never on the Linear issue (see ./linear-conventions.md), so the dedup convention is byte-identical with ../review-epic-gh/SKILL.md.

The per-PR review logic is shared with the GitHub variant via the abc:reviewer subagent; what differs here is plumbing — Linear MCP resolution, per-child platform routing, and the GitLab posting path. Linear↔VCS mapping lives in ./linear-conventions.md.

Hard rules

  • Never merge, approve-with-merge, push, close, label, or transition Linear state. Posting PR/MR review comments and <!-- review-epic:* --> markers is the entire write surface. No Linear write tools are granted — by construction.

  • Never review a PR/MR twice at the same HEAD SHA. The dedup marker (Phase 2) is load-bearing — without it every tick re-reviews everything.

  • Never post markers on the Linear issue. Markers live on the PR/MR so dedup is platform-agnostic and shared with review-epic-gh — a future migration of an epic between trackers keeps its review history.

  • Do not run this skill in the same Claude Code session as ship-issue / ship-epic workers. The dual-context perspective is the whole point: the implementer session holds per-PR context, this session holds the epic-wide spec. One session holding both collapses the benefit (and bloats context twice as fast).

  • Always self-cancel the cron on termination (Phase 5), mirroring the ship-* family contract.

  • Per-tick post gate (conservative by design). The repo convention gates posting reviewer comments behind AskUserQuestion. Each tick re-derives state from Linear + the VCS alone and no consent marker is stored anywhere, so consent cannot outlive a tick: the gate fires on the first review pass of each tick that has a review to post (Phase 3 step 3), and approval covers every post in that tick. Declining halts the loop and self-cancels the cron. No-op ticks never ask. Stated trade-off: a tick holding a pending review blocks until a human answers — the reviewer session is walk-away between reviews, not during them. (The alternative — dropping the gate and posting unattended with an explicit Hard-Rule exception, mirroring how ship-* post status comments unattended — was considered and deferred; see the two-session workflow section of the top-level README.)

    Concurrency while a gate is open. A blocked AskUserQuestion holds the Claude Code turn, and /loop runs one turn per session serially — the next 12m cron fire does not spawn a second concurrent reviewer process against the same targets. The pending prompt holds the turn until answered; the cron's interim fire is absorbed by the single-session model rather than racing it. So "walk away during a gate" is non-destructive but stalling: no double-review, no marker race — the loop simply makes no further progress until the human answers, then resumes from the next tick's fresh Phase 2 derivation (any HEAD that advanced meanwhile is naturally re-targeted). This is why the dedup marker (Phase 2) only needs to guard posted reviews, not in-flight ones — there is never more than one in-flight review per session.

Phase 0: Parse input and self-arm

Normalize the arg

Flag extraction (before shape detection): detect and strip a trailing --no-compact flag from $ARGUMENTS. When present, set no-compact mode for this invocation — the compact-between-reviews prompt (Phase 4) is skipped. The flag stays in the raw arg string used for cron arming/matching, so the opt-out survives every subsequent tick. Contract: ../_shared/compact-on-merge.md.

$ARGUMENTS is one of:

  1. Bare Linear ID (e.g. PROJ-100) — the parent issue.
  2. Linear issue URL (https://linear.app/<org>/issue/<id>) → strip prefix, extract TEAM-N.

Anything else (GitHub <owner>/<repo>#<n> refs, comma-lists, milestone: refs, project URLs) → reject with the two supported shapes. This skill requires an explicit Linear parent issue; GitHub parents go through /abc:review-epic-gh.

Fetch parent and validate

  1. mcp__claude_ai_Linear__get_issue with id: PARENT-ID, includeRelations: true. Read description, labels, statusType.
  2. If statusType is completed (Done) or canceled → the epic is done, but how to exit depends on whether a cron is armed. Run the cron-entry match rule (below): if a matching entry exists (this is a loop tick), terminate via Phase 5 — emit the reviewed-PRs summary and CronDelete the entry — so the loop self-cancels instead of zombie-firing "epic done" every 12 minutes. Only when no matching cron exists (a fresh invocation against an already-done epic) print a one-line "epic done — nothing to review" and exit without arming.
  3. Resolve sub-issues: mcp__claude_ai_Linear__list_issues with parentId: PARENT-ID, no status filter — Linear's native parent/child relation replaces the -gh family's managed task-list fence. If the list is empty → reject: "PARENT-ID has no sub-issues. Run /abc:scaffold-sub-issues first."

If any of these reads fails, classify the failure (an unknown epic state is never treated as "nothing to review"):

  • Permanent (Linear entity not found / 404 — the parent was deleted or moved out of scope) → run Phase 5 termination (emit the reviewed-PRs summary and CronDelete the cron) and exit, so a deleted parent doesn't zombie-fire "epic done" every tick.
  • Transient (MCP error, timeout, 5xx, auth blip) → halt this tick without CronDelete and retry next tick. If the same error repeats across consecutive ticks, surface stalled on same error; run /loop cancel once so the human can intervene.

Self-arm the loop

Mirror ship-epic's cron-entry match rule with this skill's name:

A CronList entry matches when its command string contains <command-name> <raw-arg> followed by a word boundary — the next character (if any) must NOT be alphanumeric, -, or ,. <command-name> is the literal slash-command name Claude Code injects (e.g. /abc:review-epic via plugin namespace) — read it from the <command-name> tag, never hardcode. Fallback regex: (?:^|[^A-Za-z0-9])(?:[A-Za-z][A-Za-z0-9_-]*:)?review-epic <raw-arg>(?![A-Za-z0-9_,-]).

Note this skill's name is a prefix of review-epic-gh — the required space between <command-name> and <raw-arg> is what disambiguates: an entry for /abc:review-epic-gh <owner>/<repo>#<n> never contains review-epic followed by a Linear ID, so cross-matching is impossible.

  • Match found → no-op (the common loop-tick path), proceed to Phase 1.
  • No match → Skill(skill: "loop", args: "12m <command-name> <raw-arg>"), then proceed to Phase 1 — the first tick also does the first iteration's work.

12-minute cadence sits in the ~10–15 min target: slower than the 6m workers (a review is only actionable once a PR exists or gains commits), fast enough that a worker's pr-open window usually gets its review before the human merges.

Phase 0.7: Resolve platform per child (repo routing)

For each open sub-issue, resolve where its PR/MR lives — same repo: label convention as ship-issue Phase 1:

  1. Collect label names starting with repo:. Exactly one expected; 0 → skip the child this tick with a [no-repo-label] line in the output; 2+ → same skip, noting the ambiguity.
  2. Extract <name> from repo:<name> → workdir is the <cwd>/<name>/ subdirectory. Missing subdir → skip with a [no-workdir] line.
  3. Detect the platform from git -C <workdir> remote get-url origin: contains github.comgh; contains gitlab.<host>glab; anything else → skip with [unknown-platform]. Capture the repo identity from the same remote URL: GitHub → <owner>/<repo>; GitLab → the project path (e.g. group/subgroup/project).
  4. Confirm CLI auth (gh auth status / glab auth status --hostname <host>) once per platform per tick. Derive the GitLab <host> from the ABC_GITLAB_HOST env var if set, else parse it from git remote get-url origin; fall back to bare glab auth status if no GitLab remote resolves (never hardcode a GitLab hostname). Not authed → halt with the auth command — a reviewer that can read but not post would burn the dedup-free review work every tick.

Skipped children are not errors — this skill is review-only, so it degrades by narrowing coverage and saying so, rather than halting the whole loop the way a worker must. Cache the {workdir, platform, cli, repo} tuple per child for this tick; re-resolve fresh next tick.

Repo routing on every VCS call (load-bearing in multi-repo epics). The loop's cwd is not the child's workdir, so neither CLI may rely on cwd-based repo resolution: every gh call passes --repo <owner>/<repo> explicitly, every glab mr call passes -R <project-path>, and every glab api call substitutes the URL-encoded project path for the project segment (projects/<group%2Fproject>/merge_requests/...) instead of the cwd-resolved :id placeholder — :id would silently resolve against the wrong project (or none) from the loop's cwd.

Phase 1: Bootstrap context (per tick, ≤30KB)

Load fresh each tick (the tick interval keeps the prompt cache warm; re-fetching also picks up mid-epic spec edits):

  1. Parent issue description verbatim — the source of truth.

  2. Per child (from the parentId listing): title, statusType, dependency relations, and the acceptance-criteria section of its description. Relations come from a per-child get_issue with includeRelations: true — the parentId listing doesn't carry them (same pattern as ship-epic Phase 1). They're bootstrap context, not load-bearing; skip the per-child fetches when the tick is already over budget. The acceptance criteria live under a ## Acceptance criteria heading or an - **acceptance:** block, whichever convention the scaffold used. Skip scope / out-of-scope prose unless a review needs it.

    Dedup against the parent (load-bearing for the budget). Scaffolded child descriptions are usually verbatim ST-sections of the parent PLAN, so naive parent+children assembly roughly doubles the spec bytes. When a child's spec text already appears in the parent description, do not re-include it — cite its location ("ST-4 section of the parent"). Include a child's own description only where it diverges from the parent's section (edited mid-epic). Same rule and reference measurement as review-epic-gh Phase 1.

  3. Merged sibling PRs/MRs: per PR/MR, the summary review comment this skill previously posted (if any) plus a per-file change summary: GitHub — gh api /repos/<owner>/<repo>/pulls/<n>/files --jq '.[] | "\(.filename) +\(.additions) -\(.deletions)"' (gh pr diff has no --stat); GitLab — glab api "projects/<encoded-project-path>/merge_requests/<iid>/diffs" --paginate for the file list (new_path per entry; the API doesn't expose per-file +/− counts — fall back to glab mr diff <iid> -R <project-path> only when a review needs a specific decision).

  4. Pending children's acceptance criteria — the forward-compat lens: what will later sub-issues exercise? (Subject to the same parent-dedup rule.)

Budget: keep the assembled context under ~30KB. When over, trim in this order: (1) merged-sibling full diffs → per-file change summary only, (2) per-file summaries → PR title + summary-comment only, (3) pending children's criteria → titles only. Never trim the parent description or the under-review child's acceptance criteria.

Phase 2: Enumerate review targets (dedup)

  1. List candidate PRs/MRs: for each open child that survived Phase 0.7, take the child's gitBranchName (Linear provides this on the issue object) and the issue's attachments/links, then: GitHub — gh pr list --repo <owner>/<repo> --state open --head <gitBranchName> --json number,headRefOid,url; GitLab — glab mr list --source-branch <gitBranchName> -R <project-path> --output json.

    0 matchesgitBranchName is Linear's suggested slug; the developer may have named the real branch differently. Fall back to the child's Linear attachments/links: extract a PR/MR URL and resolve it directly (gh pr view <url> --json number,state,headRefOid,url / glab mr view <url> --output json). Only when both the branch lookup and the attachments miss is the child [no-pr-yet]. 2+ matches (branch reuse, closed-and-reopened) — take the first open PR/MR and flag the ambiguity in the tick output (e.g. [reviewed, 2 PRs on branch — picked #N]). The same 0/2+ rule applies to URLs from the attachments fallback.

  2. For each candidate, read its HEAD SHA (GitHub: headRefOid; GitLab: diff_refs.head_sha from glab mr view <iid> -R <project-path> --output json) and fetch its top-level comments (GitHub: gh api /repos/<owner>/<repo>/issues/<pr>/comments; GitLab: glab api "projects/<encoded-project-path>/merge_requests/<iid>/notes" --paginate). If a <!-- review-epic:reviewed-at:<sha> --> marker matching the current HEAD SHA exists → skip this PR/MR with no further API calls. This dedup check is the only cost for unchanged PRs.

  3. A PR/MR whose markers all reference older SHAs has new commits → it's a review target (the stale marker stays; history is the audit trail).

No targets this tick → print the one-line no-op summary (Phase 6) and return.

Phase 3: Review each target

For each target, in sub-issue order:

  1. Fetch the diff and pin the reviewed SHA. GitHub — gh pr diff <n> --repo <owner>/<repo>, then re-read headRefOid (gh pr view <n> --repo <owner>/<repo> --json headRefOid). GitLab — glab mr diff <iid> -R <project-path>, capturing the diff_refs (base_sha / start_sha / head_sha) from the same glab mr view <iid> -R <project-path> --output json the diff was produced against; these refs build the inline position in step 4, so reading them once here guarantees the position matches the bytes reviewed. If the freshly-read HEAD (headRefOid / diff_refs.head_sha) differs from the SHA Phase 2 selected this target on, the branch advanced mid-tick → abort this target's pass with [head-moved], drop no marker, and let the next tick re-derive. Otherwise pin <reviewed-sha> to that value — it flows through the GitHub commit_id / the GitLab position, and is the SHA written in the step-5 marker, so the marker can never claim a SHA the review wasn't produced against.

  2. Spawn the existing abc:reviewer subagent (Agent tool, subagent_type: abc:reviewer — plugin agents register namespaced; the bare reviewer does not resolve in a live session) — do not edit agents/reviewer.md; extend its input via the prompt. Pass:

    • The unified diff (its standard input contract), plus

    • Platform + PR/MR refgithub/gitlab, the repo/project identity, PR #<n> / MR !<iid>, and the reviewed SHA — so findings cite the concrete target.

    • The repo's review rules — the contents of <workdir>/.claude/review-rules.md when that file exists; omit silently when absent.

    • Full files for every touched path — Linear children resolve to a local checkout at the Phase 0.7-cached <workdir>. Before offering it for local reads, assert the checkout is on the reviewed commit: git -C <workdir> rev-parse HEAD must equal <reviewed-sha> from step 1.

      • On match — state the absolute path in the prompt ("repo root for all paths in this diff: <abs-path>") and tell the reviewer it may read full files there for surrounding context, rather than working from the hunk alone.
      • On mismatch (the ship worker checked out a different commit, the branch advanced locally, etc.) — do not let the reviewer read local bytes that don't match the reviewed diff. Either abort the pass with [head-moved] (consistent with step 1, drop no marker) or fall back to fetching each file via gh api /repos/<owner>/<repo>/contents/<path>?ref=<reviewed-sha> like the -gh sibling. Otherwise step 5 would write a marker claiming <reviewed-sha> against bytes the review never saw — silently defeating the SHA-pinning guarantee on the Linear path.

      (This local-read path is the GitHub variant's one divergence: there every file is fetched via gh api ?ref=<reviewed-sha> unconditionally, because a cross-repo child may have no checkout — so the -gh side reads pinned bytes by construction and needs no HEAD assertion.)

    • Cross-cutting epic context from Phase 1: the parent spec, this child's acceptance criteria verbatim with their sub-issue ID, merged-sibling decisions, and pending children's criteria — with the instruction to additionally evaluate (a) which acceptance bullets this diff satisfies/misses, citing them by sub-issue ID and bullet, and (b) forward-looking flags where a pending sub-issue will exercise this code differently.

  3. Per-tick post gate (first review pass of this tick only): show the assembled review — inline comments plus summary — via AskUserQuestion for a single go/no-go. Approval covers this and every subsequent post in this tick (see Hard Rules — consent can't persist across ticks because no consent marker is stored); decline → halt the loop and CronDelete via the Phase 0 match rule. Later passes in the same tick skip this step entirely.

  4. Re-check PR/MR state, then post. After gate approval and immediately before posting, re-read the target's state (GitHub: gh pr view <n> --repo <owner>/<repo> --json state,mergedAt; GitLab: glab mr view <iid> -R <project-path> --output jsonstate); if it is merged/closed → skip this target with [merged-before-post], drop no marker — a review on a merged target is noise and would burn the dedup marker. Otherwise post the review:

    • GitHub — one call: POST /repos/<owner>/<repo>/pulls/<pr>/reviews with event: COMMENT, commit_id: <reviewed-sha> (pins the review to the exact SHA reviewed — without it GitHub attaches to the latest HEAD and inline comments mis-anchor when the branch moved), the reviewer's inline comments as the comments array, and the summary as the review body.
    • GitLab — no batch review API: post each inline comment as a positioned discussion via glab api "projects/<encoded-project-path>/merge_requests/<iid>/discussions" -f body=<text> with the full six-field position object (API doc): position[base_sha] / position[start_sha] / position[head_sha] built from the diff_refs captured in step 1 (the same response the diff came from — never a fresh read, which could have advanced past the reviewed bytes), position[position_type]=text (literal), and position[new_path] + position[new_line] for the new side of the diff (use old_path / old_line for deletion and context-only lines). Omitting any of the last three returns a generic 400 "the position is invalid" that's easy to mis-attribute to diff_refs. Then the summary as one glab mr note <iid> -R <project-path> --message <body>.

    Post-failure guard (both platforms): if any posting call returns 4xx, halt this PR/MR's pass without dropping the step-5 dedup marker and surface the response body in the tick output. The marker is only written after every post for that target succeeds — otherwise a malformed position would burn the review and mark the HEAD as reviewed, and the loop would never retry it.

    The summary body has explicit structure either way:

    • (a) Inline comments — one-line index of what was flagged.
    • (b) Spec cross-reference — "satisfies ST-N bullet X … misses ST-N bullet Y", citing specific acceptance bullets by sub-issue ID, never free-text paraphrase.
    • (c) Forward-looking flags — "ST-N+1 will exercise this path differently; current shape will need rework", citing the pending child.
  5. Drop the dedup marker as a marker-only top-level comment on the PR/MR (the marker is the entire body, matching the <!-- ship-issue:* --> marker-only convention): gh pr comment <n> --repo <owner>/<repo> --body '<!-- review-epic:reviewed-at:<reviewed-sha> -->' or glab mr note <iid> -R <project-path> --message '<!-- review-epic:reviewed-at:<reviewed-sha> -->' where <reviewed-sha> is the pinned value from step 1 (the SHA the review was actually produced against), not a freshly re-read HEAD. Never on the Linear issue.

  6. Compact-between-reviews boundary — see Phase 4 before starting the next target.

Phase 4: Compact between reviews

Consumer of ../_shared/compact-on-merge.md at the "between two PR reviews" boundary: after a review pass completes (marker dropped) and one or more un-reviewed targets remain in this tick's queue, print — as the last output of the tick —

🗜 Review of <pr-or-mr-url> posted. Run /compact now to free context before reviewing <next-url>.

then end the tick (same end-the-wake rule as the workers — the dedup markers persist on the PRs/MRs, so the next tick's Phase 2 picks up exactly the remaining targets). Skip in no-compact mode, and when the just-reviewed PR/MR was the only/last target. At most once per tick.

Phase 5: Termination

On every tick, before Phase 1, re-check the parent:

  • Parent statusType is completed (Done) or canceled → terminal. To build the reviewed-PRs/MRs summary, re-run Phase 2 enumeration with the state filters dropped — every sub-issue regardless of statusType, and gh pr list ... --state all / glab mr list ... --state all — because by termination every reviewed child PR/MR is merged/closed and the default open-only enumeration would find nothing to scan for <!-- review-epic:reviewed-at:* --> markers. Print the summary with thread links, CronDelete the loop's own cron entry via the Phase 0 match rule, and exit cleanly. This lands within one tick of the parent transitioning to Done.
  • User-invoked Ctrl-C / loop cancellation needs no cleanup — every tick re-derives from Linear + the VCS; markers already posted keep dedup correct on any future re-arm.

If CronDelete fails, print a note ("couldn't auto-cancel; run /loop cancel") and continue — the summary is the authoritative surface.

Phase 6: Output contract (every tick)

/review-epic tick <timestamp>
Parent: PROJ-100  "<title>"  (open, 3 of 6 children merged)

  [reviewed]            PR #43 (PROJ-103)  5 inline, 2 spec-refs, 1 forward flag
  [skipped]             MR !12 (PROJ-104)  marker matches HEAD abc1234
  [head-moved]          PR #44 (PROJ-105)  HEAD advanced mid-tick; re-review next tick
  [merged-before-post]  PR #45 (PROJ-106)  merged after gate; no review posted
  [no-pr-yet]           PROJ-107
  [no-repo-label]       PROJ-108
  [no-workdir]          PROJ-109  repo:foo → no <cwd>/foo/ subdir
  [unknown-platform]    PROJ-110  remote is neither github.com nor gitlab.*

Next tick: /loop 12m /abc:review-epic <raw-arg>

One line on no-op ticks: no-op tick — no new commits on any child PR/MR.

Notes on persistence

Stateless across sessions — Linear and the VCS are the sources of truth. The <!-- review-epic:reviewed-at:<sha> --> markers on the PRs/MRs are the entire dedup store; closing the terminal mid-loop is safe, and a force-push that discards a marker simply triggers a benign re-review. Append markers, never edit them. Because the markers live on the PR/MR rather than the tracker, the dedup state is identical regardless of which review-epic variant posted it.

What ships with it: 1 file

1.9 KB alongside SKILL.md

Keep looking

Skills are one crate of 326,970. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.