| name | pr:validate |
| description | Validate a PR title and branch name against repository contributing guidelines |
| argument-hint | PR_TITLE: "<title>" BRANCH_NAME: "<branch>" PR_AUTHOR: "<author-login>" |
| allowed-tools | ["Bash(gh *)","Read"] |
Input
Arguments: $ARGUMENTS
Expected form (typically supplied by awinogradov/code-review-action):
PR_TITLE: "<title>" BRANCH_NAME: "<branch>" PR_AUTHOR: "<author-login>"
Input resolution
PR_TITLE — $ARGUMENTS → gh pr view --json title --jq .title as a fallback when invoked interactively.
BRANCH_NAME — $ARGUMENTS → gh pr view --json headRefName --jq .headRefName → git branch --show-current as a last resort. Empty is allowed for bot PRs (skip branch validation).
PR_AUTHOR — $ARGUMENTS → gh pr view --json author --jq .author.login.
Do NOT prompt the user. If all fallbacks fail, return a structured validation error in the output JSON.
Task
$ARGUMENTS
Rules
You are validating the PR title and branch name provided above. Apply the following rules exactly. These rules are the canonical encoding of CONTRIBUTING.md — do not invent alternatives.
Standard PR Title Format
<Business-valuable description>
Standard title rules:
- Title is a business-readable description only. The GitHub issue number MUST NOT appear in the title — it is linked from the PR description via magic words.
- Must start with an uppercase letter
- Must be business-focused and understandable without reading the code
- No period at the end
- Total length must be under 120 characters
- Must NOT use Conventional Commits format (e.g.,
feat(scope): ... is invalid as a PR title)
Linear PR Title Format (Linear-tracked repositories)
<LINEAR-ID>: <Business-valuable description>
Linear title rules:
- Valid only when the repository's
package.json agents.trackers lists a linear entry whose effective keys (the entry's keys, defaulting to [team]) include the ID's key — read package.json with the Read tool. Without a matching linear tracker, a TEAM-N: prefix is invalid.
LINEAR-ID is the uppercase ticket id matching [A-Z][A-Z0-9]*-[0-9]+, followed by a colon and a single space
- When BRANCH_NAME matches the Linear branch format below, the ID MUST equal the branch's
<team>-<number> uppercased — branch frtns-28-pr-gate requires the title to start with FRTNS-28: exactly; a different id (e.g., FRTNS-82:) or a missing prefix is invalid
- A Linear-prefixed title on an
issue-<number>- or special-prefix branch is invalid
- When BRANCH_NAME is empty, accept the Linear title on the two rules above alone — there is no branch to cross-check
- Description after the prefix follows the standard rules (capitalized, no period, under 120 chars total)
Special Prefixes (bypass standard validation)
These prefixes are valid alternatives to a plain business description:
HOTFIX: — Emergency production fixes
TRIVIAL: — Changes not affecting production: typos, docs, comments, formatting
MAINTENANCE: — Infrastructure updates: deps, CI, configs
PROPOSAL: — Suggest a change without filing an issue first; discussion happens on the PR
SECURITY: — Fixes for GitHub code-scanning alerts (alerts close on re-scan, not via PR magic words; no Closes #)
Special prefix rules:
- Prefix must be fully uppercase, followed by a colon and a space
- Description after the prefix follows the same rules (capitalized, no period, under 120 chars total)
Release PR Titles (bypass standard validation)
Release PRs are created automatically by release workflows.
Release [<name>] <version>
Release title rules:
Release keyword is required, capitalized exactly as shown
name is the package or service name (optional for single-package repos, required for monorepos)
version is a SemVer number (e.g., 1.2.0, 22.0.0) — no v prefix
- No colon after
Release
Branch Name Format
If BRANCH_NAME is provided and not empty, validate it against one of these formats:
Standard format:
issue-<number>-<short-description>
issue- is the literal lowercase keyword
number is the GitHub issue number (digits only, no #)
short-description is required, lowercase, hyphens only (no underscores)
- Aim for under 60 characters; must be under 100
Linear ticket format (Linear-tracked repositories):
<team>-<number>-<short-description>
- Valid only when
package.json agents.trackers lists a linear entry whose effective keys include the branch's team key uppercased — otherwise the branch is invalid, so a typo like isue-42-fix is not silently accepted as Linear
team is the Linear team key lowercased (letters and digits, starting with a letter); number is the ticket number (digits only)
- Checked AFTER the
issue- keyword, the special prefixes, and release- — those literal prefixes always win over the generic Linear shape
short-description is required, lowercase, hyphens only (no underscores)
- Aim for under 60 characters; must be under 100
Special prefix format:
<prefix>-<short-description>
prefix must be one of: hotfix, trivial, maintenance, proposal, security (all lowercase)
short-description is required, lowercase, hyphens only (no underscores)
- Aim for under 60 characters; must be under 100
Release branch format:
release-<version>
version is a SemVer number (e.g., 1.2.0, 22.0.0) — no v prefix
- No short description required
- Created automatically by release workflows
Valid branch examples:
issue-123-add-password-reset
issue-123-update-dto
issue-45-fix-editor-crash
issue-789-update-proto
eng-123-add-auth — when a linear tracker with key ENG is configured
hotfix-memory-leak-editor
trivial-fix-typo-readme
maintenance-upgrade-node-22
proposal-add-vim-keybindings
security-tainted-format-string
release-1.2.0
Invalid branch examples:
add-user-auth — missing issue-<number> prefix or special prefix
issue_123_add_auth — underscores not allowed
eng_123_add_auth — underscores not allowed in Linear branches either
repo-123-add-auth — Linear shape without a matching linear tracker key REPO in agents.trackers
wip — no issue ref, no description
ISSUE-123-add-auth — must be lowercase
release-v1.2.0 — no v prefix
If BRANCH_NAME is empty, skip branch name validation entirely (Dependabot and similar bots cannot follow branch naming conventions).
Avoid
- Implementation details (those belong in PR body)
- Technical jargon without context
- Vague descriptions
- Including the GitHub issue number in the title (link via magic words in the body instead; the Linear ticket-id prefix on Linear-tracked repositories is the one sanctioned exception)
Valid PR Title Examples
Allow editor theme selection per workspace
Add annotation events for playback duration reporting
Refactor annotation codec for streaming support
Remove legacy plan-import endpoints
ENG-123: Allow theme selection — Linear-tracked repo, branch eng-123-…
HOTFIX: Memory leak in editor
TRIVIAL: Fix typo in README
MAINTENANCE: Upgrade Node to 22 LTS
PROPOSAL: Add Vim keybindings
SECURITY: Sanitize tainted format string in runClaude
Release 1.2.0
Release Symbiot Editor 1.2.0
Invalid PR Title Examples
feat(editor): add theme routing — Conventional Commits format, not PR title format
#123: Add feature — GitHub issue numbers must NOT appear in the title
123: Add feature — Same; link the issue via Closes #123 in the body
ENG-123: Add feature — on an issue-…/special-prefix branch, or when no linear tracker matches key ENG
Add PR quality gate on branch frtns-28-pr-gate — a gated Linear branch requires the FRTNS-28: title prefix
Added theme options — Vague, past tense, missing business value
Allow editor theme selection per workspace. — Trailing period not allowed
chore: bump deps — Conventional Commits format
release 1.2.0 — Release must be capitalized
Release v1.2.0 — no v prefix in version
Intentional divergence from the CI checks
The contributing-check CI action validates the title and the branch independently, so it accepts some combinations these rules reject. The title↔branch consistency rule, the mandatory colon + space after the Linear id (the CI header pattern makes the colon optional), and the agents.trackers gating are deliberately stricter and have no CI counterpart — a PR that passes CI but fails here is working as designed, not drift.
GitHub Issue Verification
For issue-<number>- branch names (skip for HOTFIX/TRIVIAL/MAINTENANCE/PROPOSAL/SECURITY prefixes, Linear branches, and Release branches), perform these additional checks:
-
Extract the issue number from the branch name (e.g., 123 from issue-123-add-password-reset).
-
Check issue existence:
gh issue view <NUMBER> --json title,body,state
If not found, mark invalid with reason: "GitHub issue # referenced by branch does not exist"
-
Validate relevance: Compare the PR title against the issue title and body. The title must be meaningfully related to the issue — it should capture the essence of what's being done. Non-meaningful or generic titles that don't relate to the issue content are invalid.
If the gh call fails (auth/network), skip this section and validate format only.
For Linear branches, skip ticket-existence and relevance verification entirely — in CI only gh is available, with no Linear read path. The format rules plus the title↔branch consistency rule are the full check.
Comment Generation
When the PR is invalid, generate a full GitHub PR comment in the comment field. Be sarcastic and use emojis generously. Address the PR author by @-mentioning PR_AUTHOR. Explain what went wrong, show how to fix it, and link to the [contributing guidelines](/CONTRIBUTING.md). When the PR is valid, set comment to an empty string.
Output
Return structured JSON output with exactly these fields:
titleValid (boolean): Whether the PR title passes all title rules
branchValid (boolean): Whether the branch name passes all branch rules (always true when BRANCH_NAME is empty)
reason (string): If any check failed, a brief technical summary of what failed. If all valid, an empty string.
comment (string): If any check failed, the full GitHub PR comment body (markdown) as described in Comment Generation. If all valid, an empty string.
Reference formatting & readability
These rules govern references — when you point the reader at a real file, standard, section, commit, or issue. (A token named only as an example, with no real target, is a code specimen in backticks, like any code identifier.) Every reference must resolve: render it as a real link whose target exists, and prefer the most stable link form so it does not rot. Render the same kind of reference the same way everywhere:
- Code specimens — backticks, e.g.
buildReviewComments, reviewOutput.ts. A backticked token names a thing as an example; it is not a reference and carries no link.
- Files, docs, skills, agents, and actions you point the reader at — link them, e.g.
[release field spec](<repo-blob-url>/docs/06-release-field.md). Use a repo-relative path in repository files and the absolute <repo-blob-url> form in generated output posted outside the repo (PR/issue bodies, review comments, release notes), where relative paths do not resolve. Any prose mention of a file or path that exists in the repo is such a reference — link it so it resolves on the default branch at writing time; a path that does not exist yet (a file the text proposes to create) or one shown inside a command or fenced block is a code specimen, not a reference.
- Standards and conventions — ALWAYS link the versioned RFC by its stable ID, e.g.
[RFC-0001](<repo-blob-url>/rfc/0001-reference-formatting.md); an Accepted RFC is immutable except through an explicit version bump, so the link never rots.
- External resources — articles, posts, vendor docs, and web standards or specs you cite — link them inline as
[title](url) to the canonical source, taking the title from the source (or the site name). Use only a URL present in your input or context — never produce one from memory; a source with no known URL stays plain prose. When several sources back one document, they may be gathered into a short references list.
- Sections — link the heading by its anchor. Same document: a bare
#anchor, e.g. [Phase 6](#phase-6-reply-to-review-threads). Another document: path#anchor — a repo-relative path in repository files, the absolute <repo-blob-url>/path#anchor form in generated output. A GitHub anchor is the heading lower-cased, spaces turned to hyphens, punctuation dropped.
- Commit SHAs — ALWAYS a link, e.g.
[0328a61](<repo-commit-url>/0328a61); a commit is immutable. If you cannot build the URL, leave the bare SHA un-backticked.
- Issue / PR references — leave the bare number (GitHub auto-links it) or write a full link. A tracker ID GitHub does not auto-link (e.g. Linear
ENG-123) is dead text when bare: in prose, ALWAYS render it as a markdown link, e.g. [ENG-123](https://linear.app/<workspace>/issue/ENG-123) — a slug-less issue URL resolves. On a magic-word line (Closes/Fixes/Related to in a PR body's **Issues:** section) use plain forms only: bare #N for GitHub, the plain issue URL for other trackers — never a markdown-bracket link, which breaks the close-parsers.
Backticks suppress GitHub autolinking: a commit SHA or issue/PR number inside a code span renders as dead text — that is why a backticked SHA was un-clickable in a prior review. Never wrap a SHA or issue/PR number in backticks; link it, or leave it bare so GitHub auto-links it.
Write the most helpful, readable output you can: plain, direct prose; every reference resolvable; explain the "why", not the obvious "what".