Review epic gh
Always Be Cooking - Claude Code plugin that drives features from plan → tracker sub-issues → parallel shipping → review → merge.
npx -y skills add semanticpixel/abc --skill review-epic-ghAssembled 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
GitHub · Review-only counterpart to /abc:ship-epic-gh. Self-arming /loop that watches a GitHub parent issue's managed `## Sub-issues` task-list, reviews each child PR 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 epic closes. Never merges. TRIGGER when the user says "/abc:review-epic-gh <owner>/<repo>#<n>", asks to "review this epic as it ships", or wants a standing reviewer session running parallel to /abc:ship-epic-gh.
SKILL.md
20.1 KB, ~4.9k tokens by cl100k_base, as published. Nobody here has run it
/abc:review-epic-gh — epic-context PR reviewer (GitHub)
Watch a GitHub parent issue with a managed ## Sub-issues task-list and review each child PR 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-gh (or /abc:ship-issue-gh 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 edits issue bodies. Its only writes are PR review comments and its own dedup markers.
The label scheme, task-list fence, and marker conventions are documented in ../scaffold-sub-issues-gh/github-conventions.md (re-exported locally as ./github-conventions.md).
Hard rules
-
Never merge, approve-with-merge, push, close, or label. Posting review comments and
<!-- review-epic:* -->markers is the entire write surface. -
Never review a PR twice at the same HEAD SHA. The dedup marker (Phase 2) is load-bearing — without it every tick re-reviews everything.
-
Never edit content inside the parent's
<!-- ship-epic:sub-issues:start/end -->fence — or anywhere else in the parent body. The shipping skills own it. -
Do not run this skill in the same Claude Code session as
ship-issue-gh/ship-epic-ghworkers. 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 GitHub 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 howship-*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
AskUserQuestionholds the Claude Code turn, and/loopruns 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:
<owner>/<repo>#<n>— a GitHub parent issue ID.- GitHub issue URL (
https://github.com/<owner>/<repo>/issues/<n>, optionally Enterprise host) → extract<owner>/<repo>#<n>.
Anything else (Linear IDs, bare #<n>, milestone refs, comma-lists) → reject with the two supported shapes. This skill requires an explicit GitHub parent issue.
Auth pre-flight. gh auth status --hostname <host>. If not authed → halt with the auth command.
Fetch parent and validate
gh issue view <n> --repo <owner>/<repo> --json number,title,state,stateReason,labels,body.- If
state=closed→ 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 andCronDeletethe entry — so the loop self-cancels instead of zombie-firing "epic closed" every 12 minutes. Only when no matching cron exists (a fresh invocation against an already-closed epic) print a one-line "epic closed — nothing to review" and exit without arming. - Locate the
<!-- ship-epic:sub-issues:start/end -->fence. If missing → reject: "Parent has no managed## Sub-issuestask-list. Run/abc:scaffold-sub-issues-ghfirst." Parse the- [ ] / - [x] <ref>entries to fully-qualified child IDs (same parse rule asship-epic-ghPhase 0).
If the parent read fails, classify the failure (an unknown epic state is never treated as "nothing to review"):
- Permanent (HTTP 404/410,
Could not resolve to an Issue, repo-not-found) → the parent is gone. Run Phase 5 termination (emit the reviewed-PRs summary andCronDeletethe cron) and exit, so a deleted/moved parent doesn't zombie-fire "epic closed" every tick. - Transient (timeout, 5xx, connection reset, auth blip) → halt this tick without
CronDeleteand retry next tick. If the same error repeats across consecutive ticks, surfacestalled on same error; run /loop cancelonce so the human can intervene.
Self-arm the loop
Mirror ship-epic-gh's cron-entry match rule with this skill's name:
A
CronListentry 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-ghvia 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-gh <raw-arg>(?![A-Za-z0-9_,/#-]).
- 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 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):
-
Parent issue body verbatim — the source of truth.
-
Per child (from the task-list): title, state, dependency labels (
blocks:*/blocked-by:*), and the acceptance-criteria section of its body — a## Acceptance criteriaheading 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 bodies 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 body, do not re-include it — cite its location ("ST-4 section of the parent"). Include a child's own body only where it diverges from the parent's section (edited mid-epic). The rule is the invariant; as an illustrative dated measurement (2026-06,
semanticpixel/carn#2, 11 children): parent ~29.7KB, child bodies another ~29.3KB of near-pure duplication — dedup brings bootstrap to the parent body alone, inside the budget. Re-measure if a different epic becomes the reference fixture. -
Merged sibling PRs: per PR, the summary review comment this skill previously posted (if any) plus a per-file change summary from the API:
gh api /repos/<owner>/<repo>/pulls/<n>/files --jq '.[] | "\(.filename) +\(.additions) -\(.deletions)"'(gh pr diffhas no--statflag — only--name-onlyand--patch). Full diffs (gh pr diff <n>) of merged siblings only when a current review needs to check a specific decision. -
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 body or the under-review child's acceptance criteria.
Phase 2: Enumerate review targets (dedup)
-
List candidate PRs: for each open child,
closedByPullRequestsReferencesplusgh pr list --repo <owner>/<repo> --state open --json number,headRefName,headRefOid,urlfiltered to branches matching<child-n>-*.0 matches from both sources — the
<child-n>-*pattern is the worker's branch derivation; a human (or a fix-up branch) may have named the real branch differently, and a PR without aClosestrailer never appears inclosedByPullRequestsReferences. Fall back to the child issue's timeline, preferring strong "implements" signals over loose mentions (in priority order):connectedevents — the GitHub Development-panel link (gh issue develop/ manual "link a pull request"). This is an explicit implements-relation, the strongest fallback signal:gh api /repos/<owner>/<repo>/issues/<child-n>/timeline --paginate --jq '.[] | select(.event=="connected") | .source.issue.number'(filter to entries whosesource.issue.pull_request != null).cross-referencedevents whose head branch contains<child-n>— a loose mention ("see #N", "related to #N") also emitscross-referenced, so an unfiltered match can pick an unrelated open PR that merely name-drops the child, post a full epic-context review on it, and burn the<!-- review-epic:reviewed-at -->marker there. Resolve eachcross-referencedPR viagh pr view <num> --repo <owner>/<repo> --json number,state,headRefOid,url,headRefNameand keep only those whoseheadRefNamecontains the child number as a word-bounded token.
Only when both tiers miss is the child
[no-pr-yet]. Mention-only PRs are deliberately excluded — a child reachable solely via a loosecross-referencedmention (noconnectedlink, no<child-n>in the branch) is treated as having no PR yet rather than risking a wrong-PR review; if that under-covers a real PR, the developer can add a Development-panel link or aClosestrailer to surface it. 2+ matches (branch reuse, closed-and-reopened, multiple linked PRs) — take the first open PR and flag the ambiguity in the tick output (e.g.[reviewed, 2 PRs matched — picked #N]). Same tiebreaker as the Linear variant's Phase 2. -
For each candidate PR, read its HEAD SHA (
headRefOid) and fetch its top-level comments (gh api /repos/<owner>/<repo>/issues/<pr>/comments). If a<!-- review-epic:reviewed-at:<sha> -->marker matching the current HEAD SHA exists → skip this PR with no further API calls. This dedup check is the only cost for unchanged PRs. -
A PR 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 PR, in task-list order:
-
Fetch the diff and pin the reviewed SHA.
gh pr diff <n> --repo <owner>/<repo>, and in the same step re-read the PR's current HEAD (gh pr view <n> --repo <owner>/<repo> --json headRefOid -q .headRefOid). If it differs from theheadRefOidPhase 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 against the new HEAD. Otherwise pin<reviewed-sha>to that value — it flows through the review POST'scommit_idand is the SHA written in the step-5 dedup marker, so the marker can never claim a SHA the review wasn't produced against. -
Spawn the existing
abc:reviewersubagent (Agenttool,subagent_type: abc:reviewer— plugin agents register namespaced; the barereviewerdoes not resolve in a live session) — do not editagents/reviewer.md; extend its input via the prompt. Pass:- The unified diff (its standard input contract), plus
- Platform + PR ref —
github,<owner>/<repo>, PR#<n>, and the reviewed SHA — so findings cite the concrete target. - The repo's review rules — the contents of
<workdir>/.claude/review-rules.mdwhen present, fetched viagh api /repos/<owner>/<repo>/contents/.claude/review-rules.md?ref=<reviewed-sha>(this session may hold no checkout of the child's repo); omit silently when absent. - Full files for every touched path, fetched via
gh api /repos/<owner>/<repo>/contents/<path>?ref=<reviewed-sha>— the diff alone hides surrounding context. Instruct the reviewer NOT to attempt local file reads: a cross-repo child may have no checkout in this session, so every byte it needs must be in the prompt. - 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.
-
Per-tick post gate (first review pass of this tick only): show the assembled review — inline comments plus summary — via
AskUserQuestionfor 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 andCronDeletevia the Phase 0 match rule. Later passes in the same tick skip this step entirely. -
Re-check PR state, then post. After gate approval and immediately before posting, re-read the PR state (
gh pr view <n> --repo <owner>/<repo> --json state,mergedAt); if it is merged or closed → skip this target with[merged-before-post], drop no marker — a review on a merged PR is noise and would burn the dedup marker. Otherwise post the review in onegh apicall:POST /repos/<owner>/<repo>/pulls/<pr>/reviewswithevent: 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 has moved), the reviewer's inline comments as thecommentsarray, and a summary body with explicit structure:- (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.
Post-failure guard: if the review POST returns 4xx, halt this PR's pass without dropping the step-5 dedup marker and surface the response body in the tick output. The marker is written only after the post succeeds — otherwise a malformed payload would burn the review and mark the HEAD reviewed, and the loop would never retry it.
-
Drop the dedup marker as a marker-only top-level PR comment (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> -->'where<reviewed-sha>is the pinned value from step 1 (the SHA the review was actually produced against), not a freshly re-read HEAD. -
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-url> posted. Run /compact now to free context before reviewing <next-pr-url>.
then end the tick (same end-the-wake rule as the workers — the dedup markers persist on the PRs, so the next tick's Phase 2 picks up exactly the remaining targets). Skip in no-compact mode, and when the just-reviewed PR was the only/last target. At most once per tick.
Phase 5: Termination
On every tick, before Phase 1, re-check the parent:
- Parent
state=closed(any reason) → terminal. To build the reviewed-PRs summary, re-run Phase 2 enumeration with the state filters dropped — all children (including[x]-completed task-list entries),gh pr list ... --state all— because by termination every reviewed child PR is merged/closed and the default--state openenumeration would find nothing to scan for<!-- review-epic:reviewed-at:* -->markers. Print the summary with thread links,CronDeletethe loop's own cron entry via the Phase 0 match rule, and exit cleanly. This lands within one tick of the parent closing. - User-invoked
Ctrl-C/ loop cancellation needs no cleanup — every tick re-derives from GitHub; 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-gh tick <timestamp>
Parent: <owner>/<repo>#<n> "<title>" (open, 3 of 6 children merged)
[reviewed] PR #43 (child #39) 5 inline, 2 spec-refs, 1 forward flag
[skipped] PR #44 (child #40) marker matches HEAD abc1234
[head-moved] PR #45 (child #41) HEAD advanced mid-tick; re-review next tick
[merged-before-post] PR #46 (child #42) merged after gate; no review posted
[no-pr-yet] child #50, #51
Next tick: /loop 12m /abc:review-epic-gh <raw-arg>
One line on no-op ticks: no-op tick — no new commits on any child PR.
Notes on persistence
Stateless across sessions — GitHub is the source of truth. The <!-- review-epic:reviewed-at:<sha> --> markers on the PRs 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.
What ships with it: 1 file
907 B alongside SKILL.md