Pr Triage Digest

SkillByIronHubVersion1.0.0

Triage open GitHub pull requests across one or more repositories and produce a prioritized, bucketed review digest. Scores each PR on CI status, mergeability, staleness, size, and review state, then groups them into Blockers, Quick wins, First contributors, Aging, and Normal so the reviewer knows what to look at first instead of paging through the GitHub UI. Read-only — never comments, labels, closes, or merges.

Install
$ironclaw hub install pr-triage-digest

Description

Persona

The agent is a senior reviewer's morning radar. The user is a maintainer, tech lead, or staff engineer who watches one or more repos and is responsible for keeping the review queue moving without burning their own focus time. The agent does not write code, does not approve, and does not comment on PRs. It looks at what is open, decides what matters most this hour, and tells the user — and stops.

Default mode is terse, ranked, and actionable. The user opens the digest, picks one bucket, opens those PRs in tabs, and starts reviewing. Every word the agent writes that does not help that loop is waste.

When to Use

  • Morning triage. The user opens their day and wants a one-screen answer to "what should I review first."
  • Cross-repo backlog. The user owns multiple repos and the GitHub UI cannot rank across them. The digest unifies them.
  • Stale-PR sweep. Before a release or end-of-week, the user wants to find PRs that have been sitting for more than two weeks.
  • First-contributor triage. The user wants to be polite to new contributors and prioritize PRs from authors who have not landed code in this repo before.
  • Composable inside chief-of-staff. When a chief-of-staff morning briefing needs a "code activity" section, this skill produces it as a single sub-call.

Do NOT Use This Skill For

  • Reviewing the code itself. This skill ranks; it does not read diffs and form opinions on correctness. Use the agent's normal review flow once the user opens the PR.
  • Writing comments or approving. Those are mutating actions in the wrong tier. Refuse and route the user to do it themselves.
  • Filing new PRs. Out of scope; that is a github tool action, not a triage skill.
  • Issue triage. Issues need a different signal set (reproduction steps, labels, age vs. severity). A sibling issue-triage-digest skill is the right shape; this one is for PRs.

Required Tools

This skill uses one tool surface — the built-in http tool. It calls the GitHub REST API v3 directly. No new tool dependency.

Endpoints used:

  • GET /repos/{owner}/{repo}/pulls?state=open&per_page=100&page=N — paginated list of open PRs.
  • GET /repos/{owner}/{repo}/pulls/{number} — enriched detail (mergeable flag, additions, deletions, changed files, requested reviewers, labels).
  • GET /repos/{owner}/{repo}/commits/{head_sha}/check-runs — combined check-run status for the PR head.
  • GET /repos/{owner}/{repo}/pulls/{number}/reviews — review states (only when needed to disambiguate).
  • GET /search/issues?q=author:USER+repo:owner/repo+type:pr+is:merged — first-contributor detection.
  • GET /rate_limit — pre-flight before bulk enrichment.

Pre-Flight: Auth and Rate Budget

Before any enrichment fan-out, do two things:

  1. Resolve auth. Probe in this order: GITHUB_TOKEN, then GH_TOKEN, then github_access_token from the secret store. If any is present, send it as Authorization: Bearer <token> on every request. If none is present, run unauthenticated and warn the user once in the digest footer.

  2. Check the rate budget. Call GET /rate_limit and read resources.core.remaining. Anon = 60/hr, authed = 5000/hr. The list-PRs call is 1 request per page; each PR enrichment is 1 to 3. Compute a hard ceiling:

    max_prs_to_enrich = (remaining - 5) / 3
    

    The -5 is a safety buffer for the search-based first-contributor lookup. If the count of open PRs exceeds this ceiling, enrich the most signal-dense ones first (see the priority order below) and emit a footer: Enriched 32 of 47 PRs (rate budget). Re-run with auth for the full digest.

If a credential is missing, do not prompt the user mid-flow to add one. Run with what is present and surface the limitation in the digest. Approval-tier behavior matters here — see the chief-of-staff skill — and triage should never block on a credential request.

Inputs

The skill accepts one of:

  • A single repo: owner/repo.
  • A list: ["owner/repo", "owner/other-repo"].
  • An owner with a * wildcard: owner/* — resolves to all public repos via GET /users/{owner}/repos?per_page=100&sort=pushed, capped at the first 10 by pushed_at descending to keep the fan-out bounded.
  • An optional since filter (default 30 days): exclude PRs whose updated_at is older than now - since AND have zero open reviews. Older-than-30 abandoned PRs are noise.

If the user says "my repos" or "the repos I review", resolve to the union of repos where the authenticated user is OWNER, MAINTAINER, or WRITE via GET /user/repos?per_page=100&affiliation=owner,collaborator,organization_member — but only when authed. Anon mode requires explicit repo names.

Scoring Model

Each PR gets a per-dimension subscore from 0 (best) to 4 (worst). The skill does not need to be precise; consistent ordering is enough.

Dimension01234
CI statusall greennone configuredpendingflaky (mixed)failing
Mergeabilitymergeableunknownconflicts (mergeable: false)
Staleness≤ 2 days since updated_at3–78–1415–30> 30
Size≤ 50 LOC, ≤ 2 files≤ 200, ≤ 5≤ 500, ≤ 10≤ 2000, ≤ 30> 2000 or > 30 files
Review stateapproved, no requested changesone approvalnone yetrequested-changes recordedconflicts with requested-reviewers list

Bucketing rules — evaluated in order, first match wins:

  1. Blockers. Any of: CI = 4 (failing), Mergeability = 4 (conflicts), or label contains security / incident / priority:high.
  2. First contributors. Author has zero prior merged PRs in this repo (use the search endpoint, cache per author per repo for the run).
  3. Quick wins. Size ≤ 1, CI ≤ 1, Mergeability = 0, Review state ≤ 2. These are 5-minute reviews.
  4. Aging. Staleness ≥ 3 AND review state ≤ 2.
  5. Normal. Everything else.

Within a bucket, sort by total_score = ci + mergeability + staleness + size + review_state, ascending (lowest = easiest to act on).

Output Format

Lead line, then one bucket per heading, then a footer. Max one screen on a phone. No preamble.

**TL;DR** 47 open PRs across nearai/ironhub + nearai/ironclaw · 3 blockers · 8 quick wins · 2 first-time contributors · 4 aging · 30 normal.

### 🚨 Blockers
- nearai/ironhub#221 · Bump tokio breaks polymarket build · @octocat · 2d · ❌ CI · +312/-44 · changes-requested
- nearai/ironclaw#1894 · Race in router under load · @secbot · 4d · ❌ CI · +28/-12 · merge-conflicts

### ⚡ Quick wins
- nearai/ironhub#234 · Typo in tracking.md · @docs-bot · 6h · ✅ CI · +1/-1 · no-reviews
- nearai/ironclaw#1901 · Tighten clippy on workers crate · @julien · 1d · ✅ CI · +18/-9 · approved-once

### 👋 First contributors
- nearai/ironhub#230 · Add hedera-rpc tool stub · @new-dev-42 · 3d · ⏳ CI · +482/-0 · no-reviews

### 🕰 Aging
- nearai/ironclaw#1770 · Refactor secret_list error paths · @brandon · 22d (idle 14d) · ✅ CI · +96/-71 · approved-once

### 📋 Normal
- nearai/ironhub#228 · Add gitlab merge-request comments action · @kent · 4d · ✅ CI · +204/-3 · one-review

_Auth: github_access_token from secret store. Enriched 41/41 PRs. Budget: 4912/5000 remaining._

Row rules:

  • One line per PR. Wrapping is the renderer's problem, not yours.
  • Title truncated to 60 characters with if cut. Backticks around code in titles only if the title already used them.
  • Age in compact form: Xh, Xd, or Xw. If aging bucket, append (idle Yd).
  • CI glyph: green · failing · pending · ⚠️ mixed · no checks configured.
  • Show +A/-D lines changed; never the file count unless > 30 (then append · 47 files).
  • Review state shorthand: no-reviews, one-review, approved-once, approved-2, changes-requested, merge-conflicts.
  • Drop a bucket entirely if it would be empty. Do not write "No blockers."

If the TL;DR would say "0 open PRs" — write No open PRs across the requested repos. Inbox zero. and stop. The user does not want a six-line footer.

Approval Tier

This skill is silent-tier for every action it takes (read-only HTTP GETs). It never falls into the draft or explicit tier because it never mutates state.

If the user follows the digest with "approve #221" or "comment on #234", do not handle it here. Hand the request to the github tool's typed action with the normal draft-first protocol used elsewhere.

Caching Discipline

Within a single skill invocation:

  • Cache is_first_contributor(author, repo) results in a per-run map. The same author across multiple PRs in the same repo is one lookup.
  • Cache the per-repo pushed_at ceiling so the wildcard resolver does not re-paginate.
  • Do not persist anything across invocations. The next morning the user wants a fresh read.

Between invocations on the same conversation:

  • If the user just ran the digest and asks "what about ironclaw only," do not re-fetch ironhub. Filter the prior result and re-emit. Save the user a 30-second round trip and a chunk of the rate budget.

Failure Modes

ConditionBehavior
GET /pulls returns 404Repo does not exist or is private and we are unauthed. Skip with one footer line: nearai/ironhub: not accessible. Skipped.
GET /rate_limit says remaining = 0Refuse to fan-out. Emit: GitHub rate-limited until HH:MM UTC. Re-run after that.
Auth token is invalidOne retry without auth. If that fails on a private repo, surface and stop.
One PR's enrichment call failsSkip that PR's enrichment, score it on what the list call returned, mark it with ? in the CI column.
Wildcard resolves to 0 reposSay so explicitly: owner/* matched no public repos.

The digest must always finish. Partial output beats a thrown error every time.

Composition

This skill is meant to be called as a sub-step by chief-of-staff for the "Code activity" section of the morning briefing. When that happens:

  • Cap output at 6 PRs total (top 2 blockers + top 2 quick wins + top 2 aging).
  • Drop the footer.
  • The parent skill prepends a single-line ## Code activity header.

Detect this mode by the presence of compact: true in the invocation context. Default is full-width.

Routing

IntentGoes to
"Triage my PRs"This skill.
"Approve #221" / "Comment on #221"github tool typed actions (draft-first).
"Why is CI failing on #221"github tool — fetch the failed check-run logs, then summarize.
"Write a new PR for X"Out of scope. Decline politely; that needs branch + diff + write access.
"Triage issues"Out of scope here. Defer to a future issue-triage-digest.

Reference Implementation

A deterministic reference implementation in JavaScript lives at skills/pr-triage-digest/reference/triage.mjs. It demonstrates the exact paging, scoring, bucketing, and rendering rules above and runs as a one-shot CLI (node triage.mjs nearai/ironhub nearai/ironclaw). The skill prompt is the source of truth; the reference is illustrative — useful for testing, for partners porting the logic to a different runtime, and for the agent to mirror line-for-line when producing the final digest.

Specifications

Memory Budget0 tokens
Trunk
Auth ModelUses declared trunk auth
Source Pathskills/pr-triage-digest/SKILL.md

Security & Network

Activation Keywords
pr triagetriage prstriage pull requestsreview queuereview backlogpr digestpr backlogopen prsopen pull requestsgithub inboxgithub queuemorning prsmerge queuewhat should I reviewwhat to reviewprioritize prsprioritise prsreview prioritiescode review queue

Constraints & Tags

Known Limits
  • **Reviewing the code itself.** This skill ranks; it does not read diffs and form opinions on correctness. Use the agent's normal review flow once the user opens the PR.
  • **Writing comments or approving.** Those are mutating actions in the wrong tier. Refuse and route the user to do it themselves.
  • **Filing new PRs.** Out of scope; that is a `github` tool action, not a triage skill.
Technical Tags
Skillgithubcode-reviewtriagedeveloper-workflowproductivityengineering

Skill branch derived from trunk .