
N8n:Community Pr Readiness Check
- 64 installs
- 199k repo stars
- Updated August 5, 2026
- n8n-io/n8n
Helps with ai & agent building tasks.
About
n8n:community-pr-readiness-check is a Claude Code skill in the AI & Agent Building category.
- n8n:community-pr-readiness-check
- AI & Agent Building
- AI-coding skill
N8n:Community Pr Readiness Check by the numbers
- 64 all-time installs (skills.sh)
- +5 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #6,160 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/n8n-io/n8n --skill n8ncommunity-pr-readiness-checkAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 64 |
|---|---|
| repo stars | ★ 199k |
| Last updated | August 5, 2026 |
| Repository | n8n-io/n8n ↗ |
What it does
Helps with ai & agent building tasks.
Files
Community PR Readiness Check
Given a PR number or branch name, determine whether it is ready for human review and take the right follow-up action.
Decision tree
1. Bot author (n8n-cat-bot / aikido-autofix) → cleanup-only, no review. See "Internal automation PRs" below. 2. Auto-rejection screen matches (typo-only / unsanctioned new node) → action path D — close with the matching template. 3. All checks pass (readyForReview === true) → action path B — triage to team. 4. One or more checks fail → action path A (if title is minor-fix only) then C — post comment.
Step 1 — Resolve the PR
If given a branch name, find the PR number first:
gh pr view <branch> --repo n8n-io/n8n --json number --jq .numberStep 2 — Fetch and pre-process
gh pr view <number> --repo n8n-io/n8n \
--json number,title,body,author,headRefName,headRefOid,files,isDraft,state,labelsInternal automation PRs (bot authors)
If 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.
1. Relabel the PR (both bots): swap community → n8n team:
gh pr edit <number> --repo n8n-io/n8n --remove-label community --add-label "n8n team"2. Update the linked Linear ticket (extract GHC-XXXX per step 5):
- `n8n-cat-bot` — cancel: use the available Linear MCP issue-update tool with
state: "Canceled", no labels. - `aikido-autofix` — route to Dev Platform: use the available Linear MCP issue-update tool with
team: "Developer Platform",state: "Triage", no labels.
When 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.).
Collision guard
If 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.
If 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.
Otherwise — mark in-progress
Strip 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:
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:pending" \
--remove-label "triage:needs-info" \
--remove-label "triage:tests-needed" \
--remove-label "triage:complete" \
--add-label "triage:in-progress"Only 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).
Also fetch (in parallel)
# cubic-dev-ai PR review comments (for check E)
gh api --paginate "repos/n8n-io/n8n/pulls/<number>/comments" \
--jq '.[] | select(.user.login == "cubic-dev-ai[bot]") | {body: .body, path: .path}'
# n8n-assistant issue comments (for the Linear ticket reference)
gh api --paginate "repos/n8n-io/n8n/issues/<number>/comments" \
--jq '[.[] | select(.user.login == "n8n-assistant[bot]" or .user.login == "n8n-assistant") | .body] | join("\n")'Step 2.5 — Auto-rejection screen
Per CONTRIBUTING.md, two PR patterns should be closed outright rather than reviewed:
- Typo-only PR — diff is entirely spelling/grammar fixes with no logic or tests.
- New-node PR — adds a brand-new node, unless the n8n team has explicitly agreed to take it.
If either matches, set checks.AutoReject and skip directly to action D. Full rules and how to verify each pattern: see reference/checks.md.
Step 3 — Run the five checks
Run when AutoReject is null. Full rules for each in reference/checks.md:
- A. CLA —
cla-signedlabel present. - B. Title — matches the conventional-commit regex. Authoritative rules in
.github/pull_request_title_conventions.md. - C. Description — every section heading and checklist item from
.github/pull_request_template.mdis present in the PR body. The template is read at check time, so changes to it propagate automatically. - D. Tests — source logic changes have matching test files. Skip for
docs/ci/chore/buildPRs. - E. cubic-dev-ai — no unresolved comments (resolved = "Addressed in commit" marker).
Step 4 — Identify the responsible team
Run 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.
Step 5 — Extract the Linear ticket
n8n-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.
If no n8n-assistant comment exists (older PRs that predate the automation), linearTicket is null.
Step 5b — Find Linear tickets for issues this PR claims to fix
The 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). Surface those so the assign action can cross-reference them.
1. 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. 2. 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. 3. 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 comment is still useful for traceability.
Emit the result as relatedIssueTickets in the JSON. During the assign action the cross-reference is posted both ways so both ends carry the link:
- On each related issue ticket — "FYI, [community PR #<pr>](https://github.com/n8n-io/n8n/pull/<pr>) claims to fix the issue tracked here; routed to <team> as <linearTicket>."
- On the PR's own ticket (
linearTicket), when it is non-null — a note pointing back to each related issue ticket.
If no Fixes/Closes/Resolves references exist, return relatedIssueTickets: [].
Step 6 — Output JSON
{
"readyForReview": <true if all passing checks allow merge, false otherwise>,
"messageForUser": "<Short message to the contributor listing what they need to address. 'N/A' if ready.>",
"team": "<Linear team name (from reference/teams.md), or 'Engineering' as fallback>",
"linearTicket": "<GHC-XXXX or null>",
"relatedIssueTickets": [<"GHC-1234" | "NODE-5678" | ...>],
"checks": {
"AutoReject": <"typo-only" | "new-node" | null>,
"CLA": <bool>,
"Title": <bool>,
"Description": <bool>,
"TestsNeeded": <bool>,
"TestsIncluded": <bool>,
"CubicIssues": <true if unresolved cubic issues exist, false otherwise>
}
}readyForReview is true only when: AutoReject is null; CLA, Title, and Description are all true; CubicIssues is false; and either TestsNeeded is false or TestsIncluded is true. If AutoReject is set, readyForReview is always false.
Emit the JSON first, then take the appropriate action path below.
Step 7 — Action paths
Ask 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.
A — Minor title fix
A title issue is minor if it can be repaired by a deterministic transformation:
- Leading or trailing whitespace.
- First letter of the summary in the wrong case.
- Trailing period.
- Mixed case
revert:requiring lowercase (no change needed, just flag).
If 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:
gh pr edit <number> --repo n8n-io/n8n --title "<new title>"Then 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.
B — Triage to team (readyForReview === true)
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.
Destination state: Review for NODES, Triage for every other team. Label composition: see reference/teams.md.
On Yes:
# 1. Linear — call the available Linear MCP issue-update tool with:
# id = linearTicket
# team = <team>
# state = <destination>
# labels = <computed labels>
# 2. GitHub (only if Linear succeeded) — see reference/label-flow.md
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:in-progress" \
--remove-label "status:pending-assignment" \
--add-label "team:<slug>" \
--add-label "status:team-assigned" \
--add-label "triage:complete"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.
C — Post contributor comment (readyForReview === false, no auto-reject)
Show messageForUser and ask Post as-is / Edit before posting / Skip. On post:
gh pr comment <number> --repo n8n-io/n8n --body "<final message>"Then apply the right terminal triage label — exactly one, priority triage:tests-needed > triage:needs-info. See reference/label-flow.md. On Skip, leave the PR on triage:in-progress so the next loop picks it up.
Skip C entirely if A already handled the only failing check and the PR is now ready — run B instead.
D — Close the PR
Used when the PR should be closed rather than reviewed. Three common triggers:
1. Auto-rejection (AutoReject set) — typo-only or unsanctioned new node. 2. Duplicate — another open PR addresses the same change. 3. Out of scope / bundled — multiple unrelated fixes that should be split, or scope n8n team has declined.
Ask Close + comment / Edit before closing / Skip. Templates below; pick one and adapt to the contributor and specifics.
Typo-only:
Thanks for taking the time to send this in! Per our contributing guide 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. 🙏
New node:
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 instead — that gives you full ownership and avoids the long review queue here. Closing this PR per our contributing guide.
Duplicate of another PR:
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.
Bundled / out of scope:
Thanks for the contribution! Per our contributing guide 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 filled in and a unit test that locks in the regression. Closing this one in the meantime. 🙏
Close action (same for every reason):
gh pr comment <number> --repo n8n-io/n8n --body "<final message>"
gh pr close <number> --repo n8n-io/n8n
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:in-progress" \
--remove-label "status:pending-assignment" \
--add-label "status:internal-closed" \
--add-label "triage:complete"If 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.
Notes
- Draft PRs — report all findings but note the PR is a draft.
- Already merged or closed — say so and skip the checks (don't apply triage labels).
- 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. - Label state machine — single
triage:<state>label at any time; transitions documented inreference/label-flow.md.
Community PR Readiness Check — checks
The full ruleset for the auto-rejection screen and the five readiness checks. Loaded when the skill is mid-review.
Contents
- Step 2.5 — Auto-rejection screening
- Typo-only PR
- New-node PR
- The five checks (run after auto-rejection screen clears)
- A. CLA signed
- B. PR title format
- C. PR description completeness
- D. Tests
- E. cubic-dev-ai issues
---
Step 2.5 — Auto-rejection screening
Before running the five checks, screen for PRs that the project policy in CONTRIBUTING.md (section "Community PR Guidelines") says should be rejected outright. If a PR matches one of these patterns, skip the five checks and recommend a polite close instead.
Typo-only PR
The diff consists entirely of:
- Spelling fixes in code comments, error messages, or user-visible description strings.
- No new tests, no logic changes, no behaviour changes, no config changes.
Per CONTRIBUTING.md: "Typos are not sufficient justification for a PR and will be rejected." Use gh pr diff <number> to verify the diff is purely text-edit. A PR that mixes a typo fix with a real logic fix is not typo-only — review it normally.
New-node PR
The diff adds a new node entry: a new *.node.ts (and usually a sibling credentials file) under packages/nodes-base/nodes/<NewNode>/ or packages/@n8n/nodes-langchain/nodes/<NewNode>/, with no corresponding node already existing in the codebase.
Per CONTRIBUTING.md: "PRs that introduce new nodes will be auto-closed unless they are explicitly requested by the n8n team." If the user calling the skill states the n8n team has agreed to take this node, proceed with the normal review; otherwise flag for close with a pointer to the Community Nodes flow.
When a screen matches
Skip the five checks. Set checks.AutoReject in the JSON output to "typo-only" or "new-node" and proceed to action path 7D in SKILL.md (close the PR).
---
The five checks
Run these in order when step 2.5 clears (AutoReject is null).
A. CLA signed
Check the PR labels returned from gh pr view:
cla-signedlabel present → ✅ signedcla-signedlabel missing → ❌ not signed
B. PR title format
The authoritative title rules live in .github/pull_request_title_conventions.md. Read that file at the start of the check — the allowed type list and scope rules come from there, not from this skill.
The matching regex below is a cached extraction of those rules. If the conventions file disagrees with the regex (a new type, a different scope syntax), trust the file and flag the divergence in your output.
For all types except revert:
^(feat|fix|perf|test|docs|refactor|build|ci|chore)(\([a-zA-Z0-9 ]+( Node)?\))?!?: [A-Z].+[^.]$For revert titles, the summary is the original commit header (which starts with a lowercase type), so capitalization is not enforced:
^revert(\([a-zA-Z0-9 ]+( Node)?\))?!?: .+[^.]$Quick recap of what the regex enforces (full detail in the conventions file):
- Type must be one of:
feat fix perf test docs refactor build ci chore revert. - Scope is optional, in parentheses; characters limited to
[a-zA-Z0-9 ]— hyphens like(nodes-base)fail. - Breaking changes:
!before the colon. - Summary: starts with capital letter (lowercase allowed for
revert:), no trailing period. - No Linear ticket IDs in the title (e.g.
N8N-1234).
C. PR description completeness
The PR template at .github/pull_request_template.md is the source of truth for what a complete PR description looks like. Read it at the start of the check — sections and checklist items shouldn't be hardcoded here.
Procedure:
1. Read the template file and extract:
- Every
## Heading(the expected section list). - Every
- [ ]checkbox item under the## Review / Merge checklistheading.
2. For each ## Heading from the template, check the PR body contains a heading with the same text. Treat headings case-insensitively but require the exact text — ## Related Issue does not satisfy ## Related Linear tickets, Github issues, and Community forum posts. 3. For the ## Summary heading specifically, require non-empty content below it (the HTML comment placeholder doesn't count as content). 4. For the checklist, require every extracted checkbox item to appear in the PR body. Match by the item text (after stripping - [ ] / - [x]); the boxes can be checked or unchecked — community PRs commonly leave them unchecked, which is fine. 5. For the "Related tickets" section: if the heading is present, accept any of these as content: a URL (http), a GitHub closing keyword (closes #N, fixes #N, resolves #N), or empty (only the HTML comment). Only flag if the heading is missing entirely.
When the check fails, report which section or checkbox item is missing — that's what the contributor message includes.
D. Tests
Skip this check entirely if the PR type (from the title) is docs, ci, chore, or build.
Otherwise:
1. Identify source files changed: non-test files under packages/ from the files list. 2. If there are source file changes, read the diff via gh pr diff:
gh pr diff <number> --repo n8n-io/n8n3. Use the diff to judge whether the changes introduce logic that warrants tests (new functions, bug fixes, behaviour changes, data transformations). Pure config changes, type-only changes, and trivial renames do not require tests. 4. Look for matching test files (*.test.ts, *.spec.ts, files inside __tests__/) among the changed files.
Fallback only if needed: if the diff alone is insufficient (e.g. you need to read the full surrounding function body to assess whether a refactor preserved behaviour), check out a temporary worktree:
git fetch origin pull/<number>/head:pr/<number>
git worktree add /tmp/pr-<number>-review pr/<number>
# … inspect files …
git worktree remove /tmp/pr-<number>-review --force
git branch -D pr/<number>Always clean up the worktree afterwards. In sub-agent contexts, git fetch and git worktree add are often denied by the sandbox — that's fine, the gh pr diff path handles almost every PR on its own.
Report:
- ✅ Tests present, or change does not require tests
- ❌ Source logic changed but no test files found
E. cubic-dev-ai issues
Fetch the PR review comments:
gh api --paginate "repos/n8n-io/n8n/pulls/<number>/comments" \
--jq '.[] | select(.user.login == "cubic-dev-ai[bot]") | {body: .body, path: .path}'cubic-dev-ai[bot] leaves a comment for every issue it finds. An issue is resolved if its comment body contains an "addressed in commit" marker — typically ✅ Addressed in [<sha>] or Addressed in <sha>. Resolved issues count as if they were never raised.
- No comments, every comment explicitly states no issues were found, or every issue is marked addressed → ✅
- One or more unresolved comments → ❌ report the unresolved count and priority breakdown (e.g. "3 unresolved issues: 1× P1, 1× P2, 1× P3")
Community PR Readiness Check — triage label flow
A PR should carry exactly one triage:<state> label at any time. The skill drives the PR through this state machine.
States
| State | Meaning |
|---|---|
triage:pending | Auto-applied by n8n-assistant when the PR opens. Skill removes it. |
triage:in-progress | Skill is actively reviewing. Set in step 2, replaced before exit. |
triage:complete | PR has been triaged to a team (or closed). Terminal. |
triage:needs-info | Comment posted; contributor needs to address something non-test. |
triage:tests-needed | Comment posted; contributor needs to add tests (priority over info). |
Transitions
Skill entry (step 2)
Before transitioning, check for an existing `triage:in-progress` label. If present, another reviewer is mid-triage — bail out without touching labels, Linear, or comments. This prevents two parallel runs from double-processing the same PR.
Strip 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:
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:pending" \
--remove-label "triage:needs-info" \
--remove-label "triage:tests-needed" \
--remove-label "triage:complete" \
--add-label "triage:in-progress"Only 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.
Skill exit — branch by outcome
Triaged to a team (action path 7B)
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:in-progress" \
--remove-label "status:pending-assignment" \
--add-label "team:<slug>" \
--add-label "status:team-assigned" \
--add-label "triage:complete"The team:<slug> is taken from reference/teams.md. Apply only after the Linear save_issue call succeeds — if Linear fails, leave the PR on triage:in-progress for the next loop run. status:pending-assignment may not be present (e.g. older PRs that predate the convention) — if --remove-label errors, drop the flag and retry, or run it as a separate call.
Closed (action path 7D)
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:in-progress" \
--remove-label "status:pending-assignment" \
--add-label "status:internal-closed" \
--add-label "triage:complete"Apply alongside gh pr close <number> and the close-comment template (see SKILL.md). If the PR is already closed (gh pr close reports "already closed"), still apply the labels and the Linear cancellation.
Comment posted, contributor must act (action path 7C)
Pick one terminal label using this priority:
| Condition | Label |
|---|---|
TestsNeeded === true and TestsIncluded === false | triage:tests-needed |
Any other failing check (CLA, Title, Description, CubicIssues) | triage:needs-info |
When both conditions hold, triage:tests-needed wins — it's the more specific signal and the comment already covers everything else.
gh pr edit <number> --repo n8n-io/n8n \
--remove-label "triage:in-progress" \
--add-label "triage:tests-needed" # or triage:needs-infoSkill aborted before posting
If the user picks Skip at the "post this comment?" prompt, leave the PR on triage:in-progress — don't apply a terminal label without an accompanying contributor message. The next loop run will pick it up.
status:* labels
These are independent of the triage state and signal where the PR sits in the n8n internal flow:
| Label | Applied when |
|---|---|
status:team-assigned | PR triaged to a Linear team (path 7B). |
status:internal-closed | PR closed via path 7D. |
status:pending-assignment | Default before the skill runs (set elsewhere). |
The skill writes status:team-assigned (path 7B) or status:internal-closed (path 7D) and clears status:pending-assignment at the same time, so a triaged or closed PR drops out of any status:pending-assignment queue. It does not touch other status:* labels — reconciliation between team-assigned/internal-closed happens elsewhere if needed.
Community PR Readiness Check — re-reviewing a PR you've already commented on
When looping over the GHC queue, skip PRs you reviewed before unless the contributor has actually done something. This avoids burning agent runs on PRs that are still waiting on the contributor.
What does "the contributor did something" mean?
Any of:
- New commit pushed (
committedevent) - Force-push / rebase (
head_ref_force_pushed) - Comment added (
commented) - Review submitted (
reviewed)
Label changes, description edits by us, and skill-posted comments don't count.
Use the GitHub Timeline API
The timeline endpoint returns every event on the PR with actor.login and a timestamp, so we can distinguish "contributor activity" from "skill activity" cleanly. This replaces an earlier heuristic that compared committedDate to the last skill comment — too narrow, since it missed contributor comments and force-pushes.
SKILL_USER=$(gh api user --jq .login)
gh api --paginate "repos/n8n-io/n8n/issues/<number>/timeline" \
-H "Accept: application/vnd.github+json"Procedure
1. Resolve the skill user's login ($SKILL_USER above). This is the GitHub handle the skill is running as. 2. Fetch the full timeline. 3. Find the last skill-authored event — any event where actor.login == $SKILL_USER (labels applied, comments posted, etc.). Take its timestamp as lastSkillAt. If no skill activity exists → first-time review, run the full check. 4. Scan for contributor activity after `lastSkillAt` — events where:
eventis one ofcommitted,head_ref_force_pushed,commented,reviewed,- AND, for
commented/reviewed/head_ref_force_pushed,actor.login != $SKILL_USER(skill comments shouldn't trigger re-review of themselves), - AND the event timestamp >
lastSkillAt.
Timestamp field varies by event type:
| Event | Timestamp field |
|---|---|
committed | committer.date |
head_ref_force_pushed | created_at |
commented | created_at |
reviewed | submitted_at |
5. Decide:
- One or more qualifying events → run the full review (steps 2–7 in SKILL.md).
- None → skip the PR. Report "no contributor activity since previous review" and move on.
For committed events, there's no actor.login — the actor is the commit author email. Since the skill never pushes commits, treat every committed event as contributor activity unconditionally.
Edge case: description-only edits
Body edits don't generate timeline events, so a contributor who only updates the PR description (e.g. fills in a missing template section) won't be picked up. That's a GitHub API limitation; the skill can't see body edits without diffing against a cached snapshot.
If the user explicitly mentions a PR they edited, just re-review it — that's faster than working around the API.
Community PR Readiness Check — teams and labels
Owner resolution, the GitHub-team → Linear-team → label mapping, and the Linear ticket label rules used when triaging.
Contents
- Identifying the owning team
- GitHub team → Linear team → GitHub label
- Linear ticket label rules
- Always-on label
- Type label (based on PR title prefix)
- Team-specific extra label
- Worked examples
---
Identifying the owning team
Use the canonical owners script at .github/scripts/owners.mjs. It parses .github/OWNERS with last-match-wins semantics and returns allocations sorted by file count with a share percentage. Using the script keeps this skill consistent with whatever CI uses.
1. Write the PR's changed file paths (from the files list) to a temp file, one per line:
printf '%s\n' <path1> <path2> ... > /tmp/pr-<number>-files.txt2. Run the script:
node .github/scripts/owners.mjs /tmp/pr-<number>-files.txt3. The script prints JSON of the form:
{
"totalFiles": 12,
"allocations": [
{ "team": "@n8n-io/ai", "fileCount": 10, "share": 83, "files": [...] },
{ "team": "@n8n-io/catalysts", "fileCount": 2, "share": 17, "files": [...] }
]
}Allocations are already sorted by fileCount descending — take the first entry as the winning team. 4. Clean up: rm /tmp/pr-<number>-files.txt. 5. Strip the @n8n-io/ prefix from allocations[0].team — the GitHub team slug is nodes, iam, ai, etc. If allocations is empty (no file matched any rule, which is possible only if .github/OWNERS lost its catch-all), fall back to catalysts. 6. Map the GitHub team slug to its Linear team name and PR label using the table below. The team field in the JSON output is the Linear team name. If the resolved GitHub team slug has no entry in the table, fall back to Engineering.
Sub-agent fallback: if node execution is denied by the sandbox, read .github/OWNERS directly and apply last-match-wins by hand. All the active rules fit on one screen.
GitHub team → Linear team → GitHub label
GitHub team (@n8n-io/…) | Linear team | GitHub team label |
|---|---|---|
catalysts | Catalysts | team:cats |
adore | Adore | team:adore |
ai | AI | team:ai |
nodes | NODES | team:nodes |
design | Design | team:design |
iam | Identity & Access | team:identity |
ligo | Lifecycle & Governance | team:lifecycle |
instance-ai | instanceAI | team:instance-ai |
frontend | Adore | team:adore |
qa-dx | Developer Platform | team:qa-dx |
migrations-review | Catalysts | team:cats |
The GitHub team label column is what gets applied to the PR after a successful Linear assignment (see reference/label-flow.md).
Linear ticket label rules
When calling the available Linear MCP issue-update tool to assign a ticket to a team, pass a labels array composed of three pieces:
Always-on label
GitHub— every community PR ticket carries this. In the Linear UI it renders assource > GitHub; thesourceprefix is a label-group parent, not part of the API name.
Type label (from PR title prefix)
| PR title type | Linear label |
|---|---|
feat | feature |
fix | bug |
anything else (perf, refactor, docs, ci, chore, build, test, revert) | enhancement |
Pass the child label name only — Linear silently drops unknown labels, so don't include type > prefixes.
Team-specific extra label
| Destination team | Extra label |
|---|---|
| Catalysts | Community PR |
| NODES | community-pr |
| other teams | _(no extra)_ |
Worked examples
featPR going to Catalysts:["GitHub", "feature", "Community PR"]fixPR going to NODES:["GitHub", "bug", "community-pr"]perfPR going to AI:["GitHub", "enhancement"]chorePR going to Lifecycle & Governance:["GitHub", "enhancement"]
Destination state
| Destination team | Linear state |
|---|---|
| NODES | Review |
| any other team | Triage |
NODES has a dedicated Review lane; every other team handles routing inside their own triage.