Skip to content

ci: fail the PR when its branch or its Closes line breaks the convention - #69

Merged
TzuH-Hsu merged 11 commits into
mainfrom
ci/56-pr-lint
Sep 16, 2026
Merged

TzuH-Hsu merged 11 commits into
mainfrom
ci/56-pr-lint

Conversation

@TzuH-Hsu

Copy link
Copy Markdown
Owner

Summary

Six downstream PRs were opened with a bare Closes # and a chore/-slug branch and nothing rejected them. scripts/pr-lint.js (ADR-0008 shape, with tests) now checks: the head branch is <type>/<issue#>-<slug> (types = the list in AGENTS.md's Branch step, which is now that list's one home — the test asserts equality); the body links an issue in this repository with a GitHub closing keyword (colon form, owner/repo#N and full-URL forms recognised; HTML comments stripped first, so the untouched template line fails); the branch's number is among the linked ones. release-please and Dependabot branches are exempt. ci.yml runs it as a step-level-if github-script step on pull_request, whose types: now also include edited and ready_for_review (a superset of the defaults — header amended to forbid anything less) so a fixed body turns the check green without a push. scripts/check-node-tests.sh replaces check-issue-labeler.sh, runs every scripts/*.test.js, and fails when any workflow requires a scripts/*.js that is missing. The pr-authoring recipe no longer submits the template verbatim.

Related issue

Closes #56

Validation

  • L0 static — make verify green (actionlint on the widened on.pull_request, checks incl. 26 node tests)
  • L1 unit — scripts/pr-lint.test.js: 12 cases (the six-PR failure shape, untouched template line, comment-hidden keyword, all keyword forms incl. Closes: #N and URLs, cross-repo kept apart, Refs-only guidance, branch grammar for every type and eight rejects, bot exemption, run() fail/pass/skip)
  • L2 — run against real PR payloads: this repo's fix: de-template empties CHANGELOG.md instead of seeding it #57 pass, docs: on a personal account --type creates the issue and then fails #62 (my own issue-less docs PR) fail, chore: release 0.5.1 #68 release exempt, chore(deps): bump the actions group across 1 directory with 4 updates #11 Dependabot exempt; downstream's discarded chore/-initial-version-docs PRs fail on both counts, their redone chore/61-… / chore/12-… pass. Torn-upgrade fixture (ci.yml thin caller, pr-lint.js absent) → make check FAIL naming the file.
  • Pre-review — two adversarial lenses (security, repo rules) before opening: 14 findings, all folded in, including the edited-event gap, the colon form, the recipe that would have failed its own lint, and the type list living in three disagreeing places.
  • L3 — this PR itself: its ci run executes the new step on its own branch/body (ci/56-pr-lint, Closes #56).

Risk / rollback

RISK: PRs without a linked issue in this repository now fail ci, by design
(AGENTS.md rule 1). github-script hands the PR's own pr-lint.js the job's
read-only / contents:read token and the run-scoped runtime token — stated
in ci.yml's header; nothing this public repository does not already expose.
Rollback: revert; or delete the step to keep the script and tests.

Checklist

  • Conventional Commit PR title
  • Linked issue using "Closes #N"
  • No secrets, no *.local.md files committed
  • Documentation updated where affected

Six downstream PRs were opened with a bare "Closes #" and a branch named
chore/-slug — the PR template's comment dropped, the branch's issue slot
empty — and nothing rejected them. scripts/pr-lint.js now checks the
head branch is <type>/<issue#>-<slug>, the body links an issue with a
GitHub closing keyword (HTML comments stripped first), and the branch's
number is among the linked ones; release-please and Dependabot branches
are exempt. ci.yml runs it as a step-level-if github-script step on
pull_request events, so the required check fails with the fix named
(ADR-0008 shape). scripts/check-node-tests.sh replaces
check-issue-labeler.sh and runs every scripts/*.test.js.

Closes #56

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@TzuH-Hsu TzuH-Hsu added this to the v0.5.2 milestone Sep 16, 2026
@TzuH-Hsu TzuH-Hsu added the area:ci CI workflows and automation label Sep 16, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 16, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-09-16T07:52:14.825868Z 26354b3 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b9b44275a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js Outdated
Comment on lines +59 to +60
if (EXEMPT_BRANCH_PREFIXES.some((p) => ref.startsWith(p))) {
return { exempt: true, problems: [], branchIssue: null, linked, cross };

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify bot identity before exempting bot-prefixed branches

On this public repository, a fork author controls pull_request.head.ref; skills/release-management/SKILL.md:81 explicitly notes that a fork can reuse the predictable release-please branch name. A human PR whose branch starts with release-please-- or dependabot/ therefore takes this early return and bypasses both the required branch-format and Closes checks. Pass the PR author or actor into lint() and require the expected bot identity in addition to the prefix.

AGENTS.md reference: AGENTS.md:L48-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed — same point I raised on #41. Fixed in a0161e1: exemption is by pull_request.user.login (dependabot[bot], github-actions[bot]) or by release-please's autorelease: pending label (covers the PAT-driven variant, and a fork author can apply neither); the branch prefix is no longer consulted. Test: a human on release-please--branches--main without the label gets the full lint (2 problems). Verified against REST payloads of #68 (bot author → exempt) and #11 (Dependabot → exempt).

…ch name

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a0161e1b20

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
# jobs" replays the ORIGINAL payload and would judge the old body. Never
# narrow this list: a `types:` without the three defaults is the
# "check never created" trap described above.
types: [opened, synchronize, reopened, edited, ready_for_review]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Rerun lint when the release label is added

Blocking: When release-please uses the PAT alternative documented in skills/release-management/SKILL.md:83, the initial PR is authored by the PAT owner and autorelease: pending can only be attached after the PR has been created. The opened payload therefore lacks both exemptions and fails lint, while the subsequent labeled event does not run this workflow, leaving the first release PR red until an unrelated edit or synchronization occurs. Include labeled so the check reruns after the exemption becomes available.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b3fc3f8: labeled added to on.pull_request.types (comment explains why); the header's superset rule still holds.

Comment thread scripts/pr-lint.js Outdated
`with the number. Any GitHub closing keyword works: Closes / Fixes / Resolves, optional colon, #N or a full issue URL${tail}`,
);
}
if (branchIssue !== null && linked.length > 0 && !linked.includes(branchIssue)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject PR bodies that close multiple issues

Blocking: When a body contains both Closes #10 and Closes #11, this predicate passes as long as the branch names either issue; the added test explicitly locks in that behavior. That contradicts the one-issue-per-PR contract in skills/pr-authoring/SKILL.md:41 and permits a single PR to auto-close additional issues outside its branch linkage. Require exactly one local closing reference rather than merely checking that the branch issue is among them.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b3fc3f8: more than one local closing reference is now a failure pointing at pr-authoring rule 5 (the same issue referenced twice is fine); the test that had locked in the opposite is inverted.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b3fc3f8704

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml Outdated
# AFTER opening the PR; with a PAT-authored release PR that label is what
# exempts it. Never narrow this list: a `types:` without the three
# defaults is the "check never created" trap described above.
types: [opened, synchronize, reopened, edited, ready_for_review, labeled]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Recheck PRs when the exemption label is removed

When autorelease: pending is removed from a human-authored PR whose branch or body is malformed, no workflow fires because this list includes labeled but not unlabeled; the successful ci result created when the label was added remains attached to the same head SHA, so the now-nonexempt PR stays green. Include unlabeled so removing the exemption reruns the required check.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d7fd294: unlabeled added.

Comment thread scripts/pr-lint.js
Comment on lines +47 to +49
const local = !qualifier || (repo && qualifier.toLowerCase() === String(repo).toLowerCase());
const key = `${local ? '' : qualifier + '#'}${number}`;
if (!seen.has(key)) seen.set(key, { repo: local ? null : qualifier, number });

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Verify that the referenced issue actually exists

When the branch and body contain the same nonexistent number, such as fix/999999-x and Closes #999999, this code unconditionally records the syntactic reference as a local issue and lint() returns no problems; a pull-request number is accepted the same way. The new gate therefore still allows work with no backing issue and nothing for GitHub to close, so the sole local reference should be validated through the GitHub API and rejected when it is missing or represents a pull request.

AGENTS.md reference: AGENTS.md:L9-L9

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in d7fd294: run() now takes github and makes one read — issues.get on the single linked number: 404 → 'does not exist', a pull_request field → 'is a pull request, not an issue', any other error → the job fails (a check that cannot check must not pass). REST shapes confirmed live (#69 reads back with pull_request; #999999 is 404). Header wording updated: the token was already in the step's environment, so passing the client changes nothing about exposure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d7fd294192

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js Outdated
// reference qualified with this repository's own name counts as local.
function linkedIssues(body, repo) {
const seen = new Map();
for (const m of stripHtmlComments(body).matchAll(CLOSES_RE)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Ignore closing keywords inside Markdown code

When the only Closes #42 text appears inside an inline or fenced code block, matchAll still treats it as the required link and the issue lookup lets the check pass, although GitHub renders that text as code rather than a closing reference and therefore will not close the issue on merge. Parse the Markdown or remove code spans and blocks before matching closing keywords.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 31c13d1: fenced blocks (``` or ~~~) and inline code spans are stripped alongside HTML comments before matching; tests cover a fence-only body, a fence followed by prose, and inline spans around a real reference. 65k-char adversarial body strips in 0.13 ms.

Comment thread scripts/pr-lint.js
`with the number. Any GitHub closing keyword works: Closes / Fixes / Resolves, optional colon, #N or a full issue URL${tail}`,
);
}
if (linked.length > 1) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Count cross-repository issues in the one-issue gate

When a body contains Closes #12 and Closes other/repo#4, this condition sees only the local linked array and passes; if the author has permission in the other repository, merging can therefore close two issues despite the documented one-issue-per-PR contract. The prior fix still excludes the separately collected cross references, so reject additional cross-repository closing references or count all parsed references here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 31c13d1: any cross-repository closing reference is now a failure pointing at rule 5 with the suggestion to use Refs; the cross-only and cross-plus-local cases are both tested.

…a second issue

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 31c13d1ec5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js
async function verifyIssue(github, owner, repo, number) {
let issue;
try {
({ data: issue } = await github.rest.issues.get({ owner, repo, issue_number: number }));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Grant issue-read permission to the PR linter

In a private repository created from this template, .github/workflows/ci.yml grants the GITHUB_TOKEN only contents: read, which sets unspecified scopes to none. The new caller reaches this issues.get request, but reading issues in a private repository requires issues: read; consequently every otherwise-valid, nonexempt PR throws could not verify issue and cannot satisfy the sole required ci check. Grant issues: read to this job or workflow.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Comment thread scripts/pr-lint.js Outdated
Comment on lines +44 to +45
.replace(/(^|\n)(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\2[ \t]*(?=\n|$)/g, '$1')
.replace(/`[^`\n]*`/g, '');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Strip every Markdown code-block form before matching

When the only closing keyword is in a four-space-indented code block, a fence indented by up to three spaces, or an unterminated fence, GitHub renders it as code and does not create a closing reference, but this replacement handles only closed fences beginning in column zero, so linkedIssues() accepts the keyword and the required check goes green. Fresh evidence in the current implementation is that bodies such as Closes #42 and ```\nCloses #42 are still accepted after the earlier fenced-code fix; parse the Markdown or cover these valid block forms as well.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Comment thread scripts/pr-lint.js
Comment on lines +132 to +133
if (issue && issue.pull_request) return `#${number} is a pull request, not an issue — link the issue the work is for`;
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject closed issues as the PR's closing target

When the linked number belongs to an already-closed issue, issues.get succeeds and this function returns no problem, so the PR passes even though merging it cannot close that issue. This defeats the newly documented skills/pr-authoring/SKILL.md rule 5 contract that every PR closes one issue; inspect the returned issue state and require an open issue, with guidance to reopen it or create the appropriate follow-up issue.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c6c5895: state === 'closed' fails with 'reopen it, or open a follow-up issue and link that'.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c6c5895e2e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js
Comment on lines +145 to +146
const result = lint({
headRef: pr.head && pr.head.ref,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Reject PRs that target a non-default branch

When a PR targets a feature or release branch rather than the repository's default branch, GitHub does not apply closing keywords, but the linter only passes the head ref and body and therefore reports success after verifying the issue. Such a PR can merge without closing or linking its claimed issue; inspect pr.base.ref against pr.base.repo.default_branch before accepting the reference.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Comment thread scripts/pr-lint.js Outdated
return String(text || '')
.replace(/<!--[\s\S]*?-->/g, '')
.replace(/(^|\n)(`{3,}|~{3,})[^\n]*\n[\s\S]*?\n\2[ \t]*(?=\n|$)/g, '$1')
.replace(/`[^`\n]*`/g, '');

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Exclude multi-backtick inline code spans

Fresh evidence after the earlier inline-code fix is that a body containing only Closes #42 inside a valid two-backtick code span is reduced to plain Closes #42 by this single-backtick regex and passes the lint, although GitHub treats it as code and will not close the issue. Strip code spans using their complete matching delimiter length, or parse the Markdown, before scanning for closing keywords.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7df8355: code spans are now stripped by a linear scanner that closes a run of N backticks only with a run of exactly N (CommonMark), and fences by a line scanner (unclosed fence runs to EOF; a backtick fence whose info string contains a backtick is not a fence). Tests: Closes #15, `Closes #16` + prose, triple-backtick span + prose. Adversarial 65k bodies (all-backtick, alternating, 16k fence openers) each under 2 ms.

…ck runs by length

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7df8355021

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js Outdated
}

function stripNonProse(text) {
return stripCodeSpans(stripFencedBlocks(String(text || '').replace(/<!--[\s\S]*?-->/g, '')));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle unterminated HTML comments before matching

When a body contains an unterminated comment such as <!--\nCloses #42, CommonMark treats the comment as continuing to EOF, so the closing keyword is not prose and GitHub will not close the issue. This replacement only removes comments with a terminating -->, causing linkedIssues() to accept the hidden reference and let the required check pass; strip from an unmatched opener through EOF as well.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in b872a9c: an unterminated <!-- that starts a line (≤3 spaces) is an HTML block to EOF and is dropped; unterminated mid-line it is literal text, which GitHub renders and links, so it is kept — both tested. The comment strip is now a linear scanner; every adversarial 65k body (16k openers, 13k mid-line openers, 8k closed pairs, backtick runs, 16k fences, padded headings) finishes in under 25 ms.

…inear

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b872a9c709

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js Outdated
}

function stripNonProse(text) {
return stripCodeSpans(stripFencedBlocks(stripHtmlCommentsLinear(String(text || ''))));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve links after comments inside code fences

When a fenced code block contains a line-start <!-- and the body has its real Closes #42 after that fence, stripHtmlCommentsLinear() runs first and treats the opener as an unterminated HTML block through EOF. GitHub/CommonMark instead treats it as literal fenced-code content, so the later closing reference is valid, but this linter removes it and blocks the PR as linking no issue. Account for Markdown construct nesting rather than stripping comments before recognizing their enclosing code fences.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3f4f902: one line-by-line block pass first (fence or HTML block, whichever opens first, each with its own close rule), then inline code spans, then inline comments — so <!-- inside a fence is code, ``` inside a comment block is comment, and a backtick left of <!-- makes it code. Six nesting cases added to the tests; worst adversarial 65k body 22 ms.

…ark precedence)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3f4f902ea0

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js
const BRANCH_RE = new RegExp(`^(${TYPES.join('|')})/(\\d+)-[a-z0-9][a-z0-9._-]*$`);
// GitHub's closing keywords: optional colon, then `#N`, `owner/repo#N`, or a
// full issue URL — the three forms GitHub itself links and auto-closes.
const CLOSES_RE = /\b(?:close[sd]?|fix(?:e[sd])?|resolve[sd]?):?\s+(?:https?:\/\/github\.com\/([\w.-]+\/[\w.-]+)\/issues\/(\d+)|([\w.-]+\/[\w.-]+)?#(\d+))\b/gi;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve Markdown block boundaries when matching closing keywords

When a body contains Closes, then a fenced code block, then #42, stripBlocks() removes the entire fence and this \s+ matches the remaining text across the block boundary. GitHub renders the keyword and reference in separate prose/code blocks, so they do not form a closing phrase; if issue #42 is open, API verification still succeeds and CI goes green even though merging will not close it. Preserve a non-whitespace boundary for removed blocks and avoid matching across paragraph boundaries.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

Comment thread scripts/check-node-tests.sh Outdated
# is here — an adopter who takes a thin-caller workflow without its script
# gets a job that fails on every event while `make check` stays green.
missing=0
for wf in .github/workflows/*.yml; do

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Scan both supported workflow filename suffixes

When an adopter adds a standard .github/workflows/*.yaml workflow that requires a scripts/*.js handler, this loop never examines it because it only expands *.yml. A missing handler can therefore leave make check green while the workflow fails on every event, defeating the new completeness check; scan both .yml and .yaml files.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 18a4509: both suffixes scanned (the [ -f ] guard already skips an unexpanded glob).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 18a4509e25

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/check-node-tests.sh Outdated
Comment on lines +30 to +34
if [ ! -f "$js" ]; then
echo "FAIL: $wf requires $js, which is missing — take the workflow and the script together (docs/template/upgrading.md)"
missing=$((missing + 1))
else
echo "OK: $wf requires $js (present)"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Require a test for every workflow handler

When an adopter adds a workflow that requires an existing scripts/foo.js but omits scripts/foo.test.js, this branch only confirms that the handler exists, and the later node --test scripts/*.test.js command passes by running the repository's other tests. make check therefore stays green even though the new event handler is completely untested; verify a sibling test exists for each workflow-required script before running the suites.

AGENTS.md reference: AGENTS.md:L17-L19

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 26354b3: a required scripts/X.js without scripts/X.test.js fails naming ADR-0008; fixture verified.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26354b3554

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/pr-lint.js
if (m - k === n) { closed = true; break; }
k = m;
}
if (closed) { i = k + n; continue; }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve a boundary when stripping inline code

When a body contains Closes followed by an inline code span and then #42, this branch deletes the span entirely, so Closes ignored #42 becomes Closes #42 and passes lint after the API lookup. GitHub renders the code node between the keyword and reference, so they do not form a closing phrase and merging will not close the issue; replace removed spans with a non-whitespace sentinel or otherwise prevent matching across them.

AGENTS.md reference: AGENTS.md:L50-L50

Useful? React with 👍 / 👎.

echo "OK: $wf requires $js (present, tested by ${js%.js}.test.js)"
fi
done <<EOF_JS
$(grep -oE 'scripts/[A-Za-z0-9._-]+\.js' "$wf" | sort -u)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restrict handler discovery to actual require calls

When a workflow merely mentions scripts/foo.js in a comment or invokes a non-handler script, this unrestricted grep still classifies it as a required event handler and fails make check unless scripts/foo.test.js exists. That makes harmless workflow documentation—or scripts outside ADR-0008's thin-caller pattern—block validation; extract paths only from the workflow's actual require(...) calls.

AGENTS.md reference: AGENTS.md:L19-L19

Useful? React with 👍 / 👎.

@TzuH-Hsu
TzuH-Hsu merged commit 3436050 into main Sep 16, 2026
1 check passed
@TzuH-Hsu
TzuH-Hsu deleted the ci/56-pr-lint branch September 16, 2026 07:52
TzuH-Hsu added a commit that referenced this pull request Sep 16, 2026
## Summary

`skills/branch-and-commit`'s type table said `refactor` produces a patch
bump — release-please hides it, no release. It also said `ci` produces
no bump, which #69 ignored: the PR lint shipped as `ci:` and
release-please skipped it (`No user facing commits found`). Both skills
now say what to do when a release-worthy change only fits a hidden type.
This PR is the recipe in use.

## Related issue

Closes #70

## Validation

- [x] L0 static — `make verify` green
- [x] L3 — this PR's merge must produce `chore: release 0.5.2` (the
footer below)

## Risk / rollback

```text
RISK: none — documentation. Rollback: revert.
```

## Checklist

- [x] Conventional Commit PR title
- [x] Linked issue using "Closes #N"
- [x] No secrets, no `*.local.md` files committed
- [x] Documentation updated where affected

Release-As: 0.5.2

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:ci CI workflows and automation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A PR with an empty 'Closes #' and a branch named '<type>/-<slug>' passes CI

1 participant