SKILL.md
SKILL.mdBrowse 5 files
5,463 tokens
20,995 bytes
Token encoding: o200k_base
Snapshot 8bff5da
1---2name: n8n:community-pr-readiness-check3description: >-4 Checks if a community pull request is ready for human review. Verifies CLA5 signature, PR title format, description completeness, test coverage, and6 cubic-dev-ai issues, then triages to the right Linear team or recommends a7 close. Use when given a PR number or branch name to review, or when the user8 says /community-pr-readiness-check, or asks to check if a PR is ready for9 review.10allowed-tools: Bash(gh:*), Bash(git:*), Bash(node:*), Read, Glob, Grep11compatibility:12 requires:13 - mcp: linear14 description: Required for reading and updating Linear tickets during triage15 - cli: gh16 description: Required for PR inspection and triage actions. Must be authenticated (gh auth login)17---18 19# Community PR Readiness Check20 21Given a PR number or branch name, determine whether it is ready for human review and take the right follow-up action.22 23## Decision tree24 251. **Bot author** (`n8n-cat-bot` / `aikido-autofix`) → cleanup-only, no review. See "Internal automation PRs" below.262. **Auto-rejection screen matches** (typo-only / unsanctioned new node / low-value) → action path **D — close** with the matching template.273. **All checks pass** (`readyForReview === true`) → action path **B — triage to team**.284. **One or more checks fail** → action path **A** (if title is minor-fix only) then **C — post comment**.29 30## Step 1 — Resolve the PR31 32If given a branch name, find the PR number first:33 34```bash35gh pr view <branch> --repo n8n-io/n8n --json number --jq .number36```37 38## Step 2 — Fetch and pre-process39 40```bash41gh pr view <number> --repo n8n-io/n8n \42 --json number,title,body,author,headRefName,headRefOid,files,isDraft,state,labels43```44 45### Internal automation PRs (bot authors)46 47If `author.login` is one of n8n's internal bots — `n8n-cat-bot` / `app/n8n-cat-bot` or `aikido-autofix` / `app/aikido-autofix` — skip the PR entirely and perform the cleanup actions below. Do **not** emit any JSON output.48 491. Relabel the PR (both bots): swap `community` → `n8n team`:50 ```bash51 gh pr edit <number> --repo n8n-io/n8n --remove-label community --add-label "n8n team"52 ```532. Update the linked Linear ticket (extract `GHC-XXXX` per step 5):54 - **`n8n-cat-bot`** — cancel: use the available Linear MCP issue-update tool with `state: "Canceled"`, no labels.55 - **`aikido-autofix`** — route to Dev Platform: use the available Linear MCP issue-update tool with `team: "Developer Platform"`, `state: "Triage"`, no labels.56 57When reviewing a batch, omit the skipped PR from the output. For a single PR, emit a one-line note (e.g. `Skipped & cleaned up #30591 (n8n-cat-bot): relabeled to n8n team, cancelled GHC-8398.`).58 59### Collision guard60 61If `triage:in-progress` is already on the PR, another reviewer is mid-triage — **bail out** to avoid double-processing. Emit a one-line note (e.g. `Skipped #30205: already has triage:in-progress`) and move on to the next PR. Do not run the checks, do not modify labels, do not touch Linear.62 63If the user explicitly asks to re-process a PR that's stuck on `triage:in-progress` (e.g. a previous run crashed), they can clear the label manually and re-invoke.64 65### Otherwise — mark in-progress66 67Strip any existing `triage:*` state label before adding `triage:in-progress`, so the single-state invariant holds even when re-reviewing a PR that was previously sent back with `triage:needs-info` or `triage:tests-needed`:68 69```bash70gh pr edit <number> --repo n8n-io/n8n \71 --remove-label "triage:pending" \72 --remove-label "triage:needs-info" \73 --remove-label "triage:tests-needed" \74 --remove-label "triage:complete" \75 --add-label "triage:in-progress"76```77 78Only one of those `triage:*` labels will actually be present; `--remove-label` errors when a label is missing, so run each removal as its own call (or batch and ignore errors) and then do the add. A PR carries exactly one `triage:<state>` label at a time; the skill replaces `triage:in-progress` with a terminal state before exit (see `reference/label-flow.md`).79 80### Also fetch (in parallel)81 82```bash83# cubic-dev-ai PR review comments (for check E)84gh api --paginate "repos/n8n-io/n8n/pulls/<number>/comments" \85 --jq '.[] | select(.user.login == "cubic-dev-ai[bot]") | {body: .body, path: .path}'86 87# n8n-assistant issue comments (for the Linear ticket reference)88gh api --paginate "repos/n8n-io/n8n/issues/<number>/comments" \89 --jq '[.[] | select(.user.login == "n8n-assistant[bot]" or .user.login == "n8n-assistant") | .body] | join("\n")'90```91 92## Step 2.5 — Auto-rejection screen93 94Per `CONTRIBUTING.md`, three PR patterns should be closed outright rather than reviewed:95 96- **Typo-only PR** — diff is entirely spelling/grammar fixes with no logic or tests.97- **New-node PR** — adds a brand-new node, unless the n8n team has explicitly agreed to take it.98- **Low-value / automated PR** — diff is entirely whitespace/formatting/reordering, an unexplained mass rename or dependency bump, badge/comment-only tweaks, or a bulk scripted submission, with no functional change or rationale. Screen conservatively.99 100If any matches, set `checks.AutoReject` and skip directly to action **D**. Full rules and how to verify each pattern: see `reference/checks.md`.101 102## Step 3 — Run the readiness checks103 104Run when `AutoReject` is `null`. Full rules for each in `reference/checks.md`:105 106- **A. CLA** — `cla-signed` label present.107- **B. Title** — matches the conventional-commit regex. Authoritative rules in `.github/pull_request_title_conventions.md`.108- **C. Description** — every section heading and checklist item from `.github/pull_request_template.md` is present in the PR body. The template is read at check time, so changes to it propagate automatically.109- **D. Tests** — source logic changes have matching test files (a `fix` needs a regression test covering the bug). Skip for `docs/ci/chore/build` PRs.110- **E. cubic-dev-ai** — no unresolved comments (resolved = "Addressed in commit" marker).111- **F. Linked issue or forum discussion** — `fix` PRs link a GitHub issue; `feat`/`refactor`/`perf` PRs link an issue or `community.n8n.io` topic. Skip for `docs/ci/chore/build/test`.112- **G. Size** — sum of per-file `additions` in `files` (skipping lockfiles/generated) ≤ 1000 and one logical change; sets `Oversized`.113 114## Step 4 — Identify the responsible team115 116Run `node .github/scripts/owners.mjs` against the changed file list and map the winning GitHub team to a Linear team. Full mapping table, sub-agent fallback procedure, and label rules: see `reference/teams.md`.117 118## Step 5 — Extract the Linear ticket119 120n8n-assistant leaves a comment on every community PR containing `This PR has been added to our internal tracker as "GHC-XXXX"`. Search the concatenated n8n-assistant comment body for `\bGHC-\d+\b`, take the first match.121 122If no n8n-assistant comment exists (older PRs that predate the automation), `linearTicket` is `null`.123 124## Step 5b — Find related issue tickets and detect duplicates125 126The PR body often says `Fixes #NNNN` / `Closes #NNNN` / `Resolves #NNNN` (or links to `https://github.com/n8n-io/n8n/issues/NNNN`). Each of those issues usually has its own GHC ticket (or has already been triaged to a team). When a PR references an issue, that **issue ticket** becomes the source of truth: the action paths link the PR onto it and (when ready) cancel the PR's own review ticket. Surface the related tickets and any duplicate PRs here so the action paths can act on them.127 128### Related issue tickets129 1301. Extract every issue number from the PR body matching `\b(?:fix(?:es)?|close[sd]?|resolve[sd]?)\s+#?(\d+)\b` (case-insensitive) **or** URLs matching `github\.com/n8n-io/n8n/issues/(\d+)`. Deduplicate.1312. For each issue number, search Linear with the available Linear MCP issue-search tool (query `github.com/n8n-io/n8n/issues/<num>`, limit 50) and filter the result to issues whose `description` contains the exact URL `https://github.com/n8n-io/n8n/issues/<num>`. The n8n-assistant bot embeds that URL in the description of every community-issue ticket it creates, so the match is reliable. Use the default limit of 50 (not a smaller value): the `query` is a substring search ordered by `updatedAt`, so `issues/<num>` also matches longer issue numbers (e.g. searching `123` matches `1234`) and the exact ticket can sit anywhere in the result set — a tight limit would silently drop it. If 50 results come back full, paginate with `cursor` until the exact match is found or results are exhausted.1323. Collect the matching ticket IDs (e.g. `GHC-1234`, or wherever they've been routed since — `NODE-5678`, `CAT-3338`). Include cancelled/duplicate tickets too — the link is still useful for traceability.133 134Emit the result as `relatedIssueTickets` in the JSON. If no `Fixes/Closes/Resolves` references exist, return `relatedIssueTickets: []`. The linking action itself lives in step 7 (see "Linking a PR to its issue ticket").135 136### Duplicate PRs137 138When a PR references an issue, another contributor may already have an open PR for the same issue. Gather candidate duplicates from three signals — any hit means the action path should ask whether to close this PR (path D, duplicate template). Run for each referenced issue number:139 140```bash141# Other open PRs mentioning the same issue142gh pr list --repo n8n-io/n8n --state open \143 --search "#<num> in:body" --json number,title,author144 145# PRs cross-referenced / linked on the GitHub issue itself146gh api --paginate "repos/n8n-io/n8n/issues/<num>/timeline" \147 --jq '[.[] | select(.event=="cross-referenced") | .source.issue148 | select(.pull_request) | {number, title}]'149```150 151Also inspect the matched Linear issue ticket (from the search above) for an already-attached PR link in its `links`/attachments. Exclude the PR under review from every signal, deduplicate by PR number, and emit the result as `duplicatePRs` in the JSON (`[]` when none).152 153## Step 6 — Output JSON154 155```json156{157 "readyForReview": <true if all passing checks allow merge, false otherwise>,158 "messageForUser": "<Short message to the contributor listing what they need to address. 'N/A' if ready.>",159 "team": "<Linear team name (from reference/teams.md), or 'Engineering' as fallback>",160 "linearTicket": "<GHC-XXXX or null>",161 "relatedIssueTickets": [<"GHC-1234" | "NODE-5678" | ...>],162 "duplicatePRs": [<{ "number": <int>, "title": "<string>" }, ...>],163 "checks": {164 "AutoReject": <"typo-only" | "new-node" | "low-value" | null>,165 "CLA": <bool>,166 "Title": <bool>,167 "Description": <bool>,168 "TestsNeeded": <bool>,169 "TestsIncluded": <bool>,170 "CubicIssues": <true if unresolved cubic issues exist, false otherwise>,171 "LinkedIssueOrDiscussion": <true if the §1 issue/forum link is present or the type is skipped>,172 "Oversized": <true if the filtered per-file additions sum > 1000 and the work is separable>173 }174}175```176 177`readyForReview` is `true` only when: `AutoReject` is `null`; `CLA`, `Title`, `Description`, and `LinkedIssueOrDiscussion` are all `true`; `CubicIssues` and `Oversized` are both `false`; and either `TestsNeeded` is `false` or `TestsIncluded` is `true`. If `AutoReject` is set, `readyForReview` is always `false`.178 179Emit the JSON first, then take the appropriate action path below.180 181## Step 7 — Action paths182 183Ask the user for each prompt (presented as the listed options). Sub-agents called for analysis only should stop after step 6 and let the caller drive step 7.184 185### Linking a PR to its issue ticket186 187Invoked from **B** and **C** whenever `relatedIssueTickets` is non-empty. The referenced **issue ticket** becomes the source of truth; the PR is attached to it via Linear's link feature. For each ticket in `relatedIssueTickets`, call the Linear MCP issue-update tool:188 189```text190id = <issue ticket>191links = [{ url: "https://github.com/n8n-io/n8n/pull/<pr>",192 title: "Community PR #<pr>" }] # append-only — existing links are preserved193```194 195Then post a comment (Linear MCP comment tool, `issueId = <issue ticket>`) whose wording depends on whether the PR is ready:196 197- **ready + assign (path B):** *"The linked community PR #<pr> may resolve this issue."*198- **not ready (path C):** *"A community PR (#<pr>) is in progress that may resolve this, but it is still being triaged."*199 200State and ticket-closure handling differ by path and are described in **B** and **C** below.201 202### A — Minor title fix203 204A title issue is **minor** if it can be repaired by a deterministic transformation:205 206- Leading or trailing whitespace.207- First letter of the summary in the wrong case.208- Trailing period.209- Mixed case `revert:` requiring lowercase (no change needed, just flag).210 211If the *only* failing check is `Title` (or `Title` + `CubicIssues`) and the issue is minor, propose the fix and ask `Apply proposed / Edit before applying / Skip`. Apply with:212 213```bash214gh pr edit <number> --repo n8n-io/n8n --title "<new title>"215```216 217Then re-evaluate `Title` (now passes) and continue to **B** or **C**. Non-minor title problems (wrong/missing type, no colon, hyphenated scope) need contributor input — skip A and go to **C**.218 219### B — Triage to team (`readyForReview === true`)220 221**Duplicate guard first.** If `duplicatePRs` is non-empty, surface them (number + title) and ask whether to close this PR as a duplicate. On `Yes`, go to **D** (duplicate template). On `No`, continue with B below.222 223Destination state: `Review` for NODES, `Triage` for every other team — keyed off the PR's resolved owner `team`. Label composition: see `reference/teams.md`.224 225**If `relatedIssueTickets` is non-empty — the issue ticket is the source of truth.** Ask: *"PR is ready for review. Link it onto issue ticket(s) `<relatedIssueTickets>`, move them to <destination state>, and cancel the PR's own ticket `<linearTicket>`?"* Options: `Yes, link and triage` / `No, leave as-is`. On `Yes`:226 2271. Run "Linking a PR to its issue ticket" (ready variant) for each related ticket.2282. Move **each** related issue ticket's state via the Linear MCP issue-update tool (`id = <issue ticket>`, `state = <destination>`) — `Review` for NODES, `Triage` otherwise.2293. Cancel the PR's own review ticket: Linear MCP issue-update with `id = linearTicket`, `state = "Canceled"` (skip if `linearTicket` is `null`).2304. Apply the GitHub PR labels (only if the Linear updates succeeded), so reviewers still find the PR:231 ```bash232 gh pr edit <number> --repo n8n-io/n8n \233 --remove-label "triage:in-progress" \234 --remove-label "status:pending-assignment" \235 --add-label "team:<slug>" \236 --add-label "status:team-assigned" \237 --add-label "triage:complete"238 ```239 240**Otherwise (no related issue ticket) — classic path, unchanged.** Ask: *"PR is ready for review. Assign Linear ticket `<linearTicket>` to team `<team>` and move to <destination state>?"* Options: `Yes, assign and triage` / `No, leave as-is`. On `Yes`:241 242```bash243# 1. Linear — call the available Linear MCP issue-update tool with:244# id = linearTicket245# team = <team>246# state = <destination>247# labels = <computed labels>248# 2. GitHub (only if Linear succeeded) — see reference/label-flow.md249gh pr edit <number> --repo n8n-io/n8n \250 --remove-label "triage:in-progress" \251 --remove-label "status:pending-assignment" \252 --add-label "team:<slug>" \253 --add-label "status:team-assigned" \254 --add-label "triage:complete"255```256 257The PR's own review ticket is **not** canceled in the classic path — it remains the tracking ticket. If `linearTicket` is `null`, ask whether to create a new Linear ticket before triaging (older PRs predating n8n-assistant). Otherwise skip B and ask the user.258 259### C — Post contributor comment (`readyForReview === false`, no auto-reject)260 261If `relatedIssueTickets` is non-empty, run "Linking a PR to its issue ticket" (not-ready variant): add the PR link and post the "in progress, still being triaged" comment. **Leave each issue ticket's state unchanged and do not cancel the PR's own review ticket** — the PR isn't ready yet, so we only flag the in-progress work. (Optionally surface `duplicatePRs` to the user, but do not close here — closing is a path-D action driven from B.)262 263`messageForUser` should list every failing check the contributor must address, including a **missing linked issue / forum topic** (check F — ask them to open or link a GitHub issue for a `fix`, or a `community.n8n.io` topic for a `feat`/`refactor`) and an **oversized PR** (check G — ask them to split it into focused PRs *if the work is separable*).264 265Show `messageForUser` and ask `Post as-is / Edit before posting / Skip`. On post:266 267```bash268gh pr comment <number> --repo n8n-io/n8n --body "<final message>"269```270 271Then apply the right terminal triage label — exactly one, priority `triage:tests-needed` > `triage:needs-info` (a missing linked issue/forum topic or an oversized PR maps to `triage:needs-info`). See `reference/label-flow.md`. On `Skip`, leave the PR on `triage:in-progress` so the next loop picks it up.272 273Skip C entirely if A already handled the only failing check and the PR is now ready — run B instead.274 275### D — Close the PR276 277Used when the PR should be closed rather than reviewed. Three common triggers:278 2791. **Auto-rejection** (`AutoReject` set) — typo-only, unsanctioned new node, or low-value/automated.2802. **Duplicate** — `duplicatePRs` is non-empty (another open PR addresses the same change), confirmed via the duplicate guard in path B. Use `#<other-pr>` from `duplicatePRs` in the template.2813. **Out of scope / bundled** — multiple unrelated fixes that should be split, or scope n8n team has declined.282 283Ask `Close + comment / Edit before closing / Skip`. Templates below; pick one and adapt to the contributor and specifics.284 285**Typo-only:**286> Thanks for taking the time to send this in! Per our [contributing guide](../blob/master/CONTRIBUTING.md#community-pr-guidelines) we don't accept typo-only PRs — they create review overhead without changing functionality, and our spell-checker rules cover most cases automatically. Closing this for now; please feel free to open a PR that pairs a typo fix with a related logic change. 🙏287 288**New node:**289> Thanks for the contribution! n8n no longer accepts new nodes directly into the core monorepo unless the team has explicitly agreed to scope one in. Please publish this as a [community node](https://docs.n8n.io/integrations/creating-nodes/overview/) instead — that gives you full ownership and avoids the long review queue here. Closing this PR per our [contributing guide](../blob/master/CONTRIBUTING.md#community-pr-guidelines).290 291**Low-value / automated:**292> Thanks for taking the time to open this! We review every PR by hand, so per our [contributing guide](../blob/master/CONTRIBUTING.md#community-pr-guidelines) we only take changes that carry a clear functional benefit. This one doesn't change behaviour (or comes without a rationale we can act on), so we're closing it to keep the review queue focused. If there's a real fix or improvement behind it, please open an issue or forum topic describing the problem first and we'll be glad to look. 🙏293 294**Duplicate of another PR:**295> Thanks for the contribution! This change is already being handled in #<other-pr>, which is further along in review. Closing this in favour of that PR to keep the queue tidy — please feel free to chime in over there if there's anything missing.296 297**Bundled / out of scope:**298> Thanks for the contribution! Per our [contributing guide](../blob/master/CONTRIBUTING.md#community-pr-guidelines) we ask for one focused change per PR. This PR bundles <N> unrelated fixes — please reopen them as separate, focused PRs, each with the [template](../blob/master/.github/pull_request_template.md) filled in and a unit test that locks in the regression. Closing this one in the meantime. 🙏299 300Close action (same for every reason):301 302```bash303gh pr comment <number> --repo n8n-io/n8n --body "<final message>"304gh pr close <number> --repo n8n-io/n8n305gh pr edit <number> --repo n8n-io/n8n \306 --remove-label "triage:in-progress" \307 --remove-label "status:pending-assignment" \308 --add-label "status:internal-closed" \309 --add-label "triage:complete"310```311 312If `linearTicket` is set, also cancel it with the available Linear MCP issue-update tool (`id=linearTicket`, `state="Canceled"`). If `gh pr close` reports the PR is already closed (contributor beat you to it), proceed with the comment, labels, and ticket cancellation anyway.313 314## Notes315 316- **Draft PRs** — report all findings but note the PR is a draft.317- **Already merged or closed** — say so and skip the checks (don't apply triage labels).318- **Re-reviewing a PR you've already commented on** — use the GitHub Timeline API to detect contributor activity since the last skill touch. See `reference/re-review.md`.319- **Label state machine** — single `triage:<state>` label at any time; transitions documented in `reference/label-flow.md`.320 Discovery context
Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.
