chore: add PR template and required CI checks (V2-719, V2-720) - #185
Conversation
Add the standard pull request template (V2-719) and the CI enforcement that makes a linked Linear issue and the template fields required on every PR (V2-720). Part of the release-process extensions; identical across the six crate repos. - .github/PULL_REQUEST_TEMPLATE.md: eight-field template with Risk tier and Semver impact checkboxes, stable headings as grep anchors for the train-manifest verification. - CLAUDE.md: instruct agents to fill the template on PR open. - .github/workflows/pr-checks.yml + .github/scripts/check_pr.py: the linear-link (main + rc-*) and pr-template (main) required status checks. V2-719 V2-720 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Adds repo-level PR hygiene enforcement for the ant-node project by introducing a standardized PR template and GitHub Actions checks that require a Linear reference and a fully completed template (with main-only enforcement for template completeness).
Changes:
- Added
.github/PULL_REQUEST_TEMPLATE.mdto standardize PR descriptions. - Added
pr-checksGitHub Actions workflow to enforce a Linear link and PR template completeness. - Added a Python checker script (
.github/scripts/check_pr.py) and documented the requirement inCLAUDE.md.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
CLAUDE.md |
Documents the new requirement to use the standard PR template and notes the CI enforcement checks. |
.github/workflows/pr-checks.yml |
Adds CI jobs intended to enforce “linear-link” and “pr-template” status checks on PRs targeting main / rc-*. |
.github/scripts/check_pr.py |
Implements the Linear reference and PR template validation logic executed by the workflow jobs. |
.github/PULL_REQUEST_TEMPLATE.md |
Introduces the standardized PR template with required sections and checkboxes. |
Comments suppressed due to low confidence (1)
.github/workflows/pr-checks.yml:36
- Same tamper-resistance concern here: ensure the checkout uses the base SHA so the template check script can’t be altered in the PR to force a pass.
steps:
- uses: actions/checkout@v4
- name: Require the PR template fields
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pull_request: | ||
| types: [opened, edited, synchronize, reopened] | ||
| branches: | ||
| - main | ||
| - 'rc-*' |
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - name: Require a linked Linear issue |
| def has_linear_ref(*parts): | ||
| haystack = "\n".join(parts) | ||
| m = LINEAR_URL.search(haystack) or LINEAR_KEY.search(haystack) | ||
| return m.group(0) if m else None |
| for heading in ( | ||
| "linear issue", | ||
| "risk tier", | ||
| "compatibility", | ||
| "semver impact", | ||
| "test evidence", | ||
| "new dependency", | ||
| "adr", | ||
| "mitigation / rollback", | ||
| ): | ||
| if heading not in secs: | ||
| errors.append(f"missing section: ## {heading.title()}") | ||
|
|
dirvine
left a comment
There was a problem hiding this comment.
I found two gaps in the enforcement, reproduced against the checker on this PR head:
-
The Linear gate does not reliably require a Linear issue.
LINEAR_KEYaccepts any<letters>-<digits>token anywhere in the title, body, or branch. For example, a PR containing onlyUTF-8as its supposed issue reference passes bothlinearandtemplate. Common technical strings such asSHA-256,HTTP-2, orRFC-123can therefore satisfy the required check accidentally. Please require a reallinear.app/.../issue/...URL, or constrain keys to the organisation's known Linear team prefixes. -
“Fill every field” is not enforced. A Compatibility section with only
Wire: nonepasses whileStorage:andAPI:are blank, and a T0/T1 PR also passes with an empty ADR section. I reproduced that combination locally and receivedPR template complete. Please require all three compatibility axes and require an explicit ADR value (n/afor T0/T1; a link for T2/T3).
The six PRs currently carry the same checker (SHA-256 89aa9a45c0444cb40726a3e5d3b8cc5e402d75ed4e50341fb2fff84053098649), so the same fix should be applied consistently across them. The template/workflow shape otherwise looks sound, and the new checks themselves are running successfully.
Address review feedback on the release-process PR checks: - Linear gate: require a real Linear reference — a linear.app URL or an issue key with a known team prefix (V2/AUTO/REL/INFRA/QA) — so tokens like UTF-8, SHA-256, RFC-123 no longer satisfy it. Strip HTML comments before matching so the template's own V2-123 example does not count. - Template completeness: require all three Compatibility axes (Wire/Storage/API) and an explicit ADR value (n/a for T0/T1, a link for T2/T3); previously one filled axis and an empty ADR passed. - Fix a regex that let an empty Compatibility axis match the next line. - Emit exact template headings in "missing section" errors. - Run the workflow on ready_for_review too. V2-719 V2-720 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (1)
.github/scripts/check_pr.py:97
- check_linear() says a linear.app URL must be in the PR body, but it currently searches for linear.app/ across title/body/branch via linear_ref(). This makes enforcement inconsistent with the error message (and the PR description). Either allow URLs anywhere and update the message, or enforce URL-in-body only; the patch below implements URL-in-body only while still allowing issue keys in title/body/branch.
def check_linear():
# Strip comments from the body so the template's own example does not count.
ref = linear_ref(env("PR_TITLE"), strip_comments(env("PR_BODY")), env("PR_BRANCH"))
if ref:
ok(f"✅ Linear reference found: {ref}")
| LINEAR_KEY = re.compile( | ||
| r"\b(?:" + "|".join(LINEAR_TEAM_PREFIXES) + r")-[0-9]+\b", re.IGNORECASE | ||
| ) | ||
| LINEAR_URL = re.compile(r"linear\.app/", re.IGNORECASE) |
| CI enforces the Linear link and the template fields (`linear-link` and | ||
| `pr-template` status checks); a PR that omits them fails its checks. |
|
Thanks — both gaps are fixed, and the same checker (identical across all six PRs) is updated consistently. 1. Linear gate no longer accepts arbitrary tokens. A reference now counts only if it is a 2. "Fill every field" is enforced. All three Compatibility axes (Wire/Storage/API) must each carry a value ( Also addressed from the Copilot review: exact template headings in the "missing section" messages ( One point I did not change yet: Copilot's tamper-resistance note (switch |
dirvine
left a comment
There was a problem hiding this comment.
The compatibility and ADR enforcement is fixed, and the constrained issue-key prefixes correctly reject UTF-8/SHA-256-style tokens. One residual gap remains in the Linear URL path:
LINEAR_URL = re.compile(r"linear\.app/", re.IGNORECASE) accepts any page on the domain. I reproduced both https://linear.app/changelog and https://linear.app/not-an-issue passing the linear and template checks as a linked issue.
Please constrain the URL pattern to an actual issue path, for example linear.app/<workspace>/issue/<key> (allowing the optional issue slug if needed), and include this negative case in the test matrix. Everything else from my previous review is addressed.
Follow-up review: the linear.app URL check accepted any page on the domain (linear.app/changelog, linear.app/not-an-issue passed). Constrain it to a real issue path (linear.app/<workspace>/issue/<key>). - Linear URL must now match linear.app/<workspace>/issue/<key>[/<slug>]. - Clarify the failure message (a reference is accepted in title, body, or branch, matching the actual behaviour). - Add .github/scripts/test_check_pr.py: a 17-case matrix exercising the real checker, including the linear.app/changelog and linear.app/not-an-issue negatives; run it in CI via a new self-test job. V2-719 V2-720 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
.github/scripts/check_pr.py:39
- The Linear issue URL regex can be bypassed by domains that merely contain
linear.app(e.g.https://notlinear.app/...), because the pattern is not anchored/bounded to the reallinear.apphost. This weakens the Linear-link requirement and can allow false positives.
LINEAR_URL = re.compile(
r"linear\.app/[^/\s]+/issue/[A-Za-z][A-Za-z0-9]*-[0-9]+", re.IGNORECASE
)
| ("linear: UTF-8 only", "linear", {"PR_TITLE": "fix UTF-8", "PR_BRANCH": "fix/utf8"}, 1), | ||
| ("linear: SHA-256 / RFC-123", "linear", {"PR_TITLE": "SHA-256 RFC-123", "PR_BRANCH": "x"}, 1), | ||
| ("linear: linear.app/changelog", "linear", {"PR_BODY": "see https://linear.app/changelog", "PR_BRANCH": "x"}, 1), | ||
| ("linear: linear.app/not-an-issue", "linear", {"PR_BODY": "https://linear.app/not-an-issue", "PR_BRANCH": "x"}, 1), | ||
| ("linear: unfilled template (example in comment)", "linear", {"PR_BODY": UNFILLED, "PR_BRANCH": "x"}, 1), |
|
Fixed — and thanks for catching the loose URL path. Linear URL is now constrained to a real issue path. Both negatives are in a committed test matrix. Added The same checker + tests are identical across all six PRs ( |
dirvine
left a comment
There was a problem hiding this comment.
Follow-up verified. The Linear URL matcher now requires an actual <workspace>/issue/<key> path, the two reported false-positive URLs are covered by regression tests, and the shared test matrix passes 17/17 locally. The required linear-link and pr-template checks are green. Approved.
Linear issue
Risk tier
Compatibility
Semver impact
Test evidence
check_pr.pywas exercised locally across its cases: missing Linear referencefails
linear; a key in the title/body/branch passes; a body without thetemplate fails
template; an rc-* base is a no-op pass; a fully-filled templatepasses; and the failure modes (no/duplicate tier or semver box, empty section,
Tier 2/3 without an ADR link) each fail with a specific message. This PR's own
description is validated by the new checks.
New dependency
none
ADR
n/a
Mitigation / rollback
Revert this PR; the template and checks are additive and carry no runtime effect.