Skip to content

feat: pr-lint asks GitHub which issues a PR closes instead of parsing Markdown - #113

Merged
TzuH-Hsu merged 4 commits into
mainfrom
feat/104-pr-lint-closing-references
Sep 29, 2026
Merged

TzuH-Hsu merged 4 commits into
mainfrom
feat/104-pr-lint-closing-references

Conversation

@TzuH-Hsu

@TzuH-Hsu TzuH-Hsu commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

Summary

scripts/pr-lint.js decided which issues a PR closes by re-implementing CommonMark block parsing. Nine review rounds on #96 still left it disagreeing with GitHub whenever containers nest, and three more simple cases turned up after v0.5.7. GitHub already exposes the ground truth: the pull request's closingIssuesReferences field, read with excludeUserLinked: true so it lists exactly the issues the body closes, with keywords in prose as GitHub renders them.

  • run() reads that field over GraphQL. lint() checks the result: exactly one issue, in this repository, the branch's, and still open. Branch grammar and the bot exemptions are unchanged.
  • The Markdown scanner and its tests are gone (−260 lines net).
  • The field lags a body edit. I measured this by editing a PR body and polling: 1–9 s. The lint therefore reads until two reads 5 s apart agree, at most five times. Typical cost is one extra read and a 5 s wait.
  • Because the lint reads live state, "Re-run jobs" now re-checks the current body. The ci.yml comment that said it replays the old payload is corrected.
  • Messages follow GitHub's semantics. A number that is a pull request, or a deleted issue, now shows up as "closes no issue", because GitHub closes nothing for it.

Checking GitHub against CommonMark. I put the three cases deferred to this issue into an open PR's body and read the field:

Body GitHub CommonMark v0.5.7 scanner
Heading\n=======\n Closes #104 not closed not closed closed
`- ```\n Closes #104\n ```` not closed not closed closed
- item\n > quote\n # heading\n\n Closes #107 closed closed not closed

Sidebar links do not count. Adding or removing an issue link in the PR's sidebar fires no pull_request event, so accepting it would let a green check go stale; the lint reads only what the body closes, and AGENTS.md step 4, skills/pr-authoring rule 5 and the architecture ledger say so. Before the first read, the lint also waits until the triggering edit is 15 s old, so two agreeing reads cannot both be stale.

Downstream: the kit's pr-lint.js and pr-lint.test.js change shape completely; adopters replace both files whole. The workflow step is unchanged, and so are the job permissions: issues: read and pull-requests: read were already there.

Related issue

Closes #104

Validation

Pick validation depth by blast radius. A level that applies but could not run becomes a RISK: line below. See AGENTS.md for the validation ladder.

Risk / rollback

RISK: L2 not run on a private repository — GraphQL under the ci job's issues/pull-requests read scopes is expected to work as the REST read did, but is untested; the first private adopter run will show it, and a refusal fails the check with the scopes named
Rollback: revert this PR; the Markdown scanner returns

Checklist

Before merging:

  • Conventional Commit PR title (<type>: <description>)
  • Linked issue using "Closes #N"
  • No secrets, no *.local.md files committed
  • Documentation updated where affected

… Markdown

The lint re-implemented CommonMark block parsing to decide which `Closes`
lines count; nine review rounds on #96 left it still disagreeing with
GitHub on nested containers, and three more simple cases turned up after
v0.5.7 (#104). GitHub already exposes the answer: the pull request's
closingIssuesReferences — closing keywords in prose as GitHub renders them,
plus issues linked in the sidebar. run() now reads that field over GraphQL
and lint() judges the references: exactly one, in this repository, the
branch's, still open. The scanner and its tests are removed.

The field lags a body edit by a few seconds (measured 1-9 s), so the
lint reads until two reads 5 s apart agree, at most five times. Because
it reads live state, "Re-run jobs" now re-checks the current body. On
this repository, GitHub's own answer for the three deferred cases — a
Setext heading, a fence on a list item's first line, a quote ending on a
heading inside a list item — matched CommonMark in all three, where the
scanner got all three wrong.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TzuH-Hsu TzuH-Hsu added the area:ci CI workflows and automation label Sep 28, 2026
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 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-28T16:50:44.288127Z 35a3c82 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: 8003baa48c

ℹ️ 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 Resolved
…ng closingIssuesReferences

Two equal reads inside the field's lag window (measured up to 9 s) could
both be stale. run() now waits until the PR's updated_at from the event is
at least 15 s old before the first read, then keeps the two-read
agreement. Runs that start later than that do not wait.

Co-Authored-By: Claude Opus 5.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: f100be075e

ℹ️ 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 Resolved
A sidebar (Development) link can be added or removed without any
pull_request event, so a check that accepted it could stay green after
the link changed. The query now passes excludeUserLinked: true — GitHub's
own list of issues the body closes — and the docs and failure message say
a sidebar-only link does not count.

Co-Authored-By: Claude Opus 5.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: cf9077da68

ℹ️ 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 Resolved
… settle on a stale answer

A manual re-run replays the original event, whose updated_at predates any
later body edit, while the references are read live. The query now also
returns the pull request's updatedAt, and the lint waits until the later
of the two is 15 s old before settling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@TzuH-Hsu TzuH-Hsu added this to the v0.6.0 milestone Sep 29, 2026
@TzuH-Hsu
TzuH-Hsu merged commit 02b4138 into main Sep 29, 2026
1 check passed
@TzuH-Hsu
TzuH-Hsu deleted the feat/104-pr-lint-closing-references branch September 29, 2026 06:57
@github-actions github-actions Bot mentioned this pull request Sep 29, 2026
TzuH-Hsu pushed a commit that referenced this pull request Sep 29, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.6.0](v0.5.8...v0.6.0)
(2026-09-29)


### Features

* check-tool-versions reads adopter-owned pins from an optional
scripts/tool-pins.extra
([#112](#112))
([5e37f0b](5e37f0b))
* pr-lint asks GitHub which issues a PR closes instead of parsing
Markdown
([#113](#113))
([02b4138](02b4138))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.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.

Task: pr-lint should ask GitHub which issues a PR closes (closingIssuesReferences) instead of parsing Markdown

1 participant