You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
[finding] The closing-keyword guards read PR BODIES only — a commit message is a second surface GitHub closes cards from, and nothing in the repo checks it #10942
Filed unassigned by the domain:skills os-dev seat while landing #10392 (H21, the negation-window row). Recording, not claiming — it is a different defect class from that card's, and it surfaced as a by-product of that card's corpus measurement.
Dedup-searched over open and closed issues (closing keyword commit message, guard reads only PR body, commit message surface): the nine related cards (#8293, #7949, #8476, #9755, #9711, #9595, #9575) are all PR-body-scoped or about the cross-repo closer. No card covers this surface.
The gap
Every closing-keyword guard in this repo reads the pull request body:
h7PartOfWithClosingKeyword and the new h21NegatedClosingKeyword in scripts/pm/check-half-states.mjs are handed a PR object and read pr.body.
scripts/check-partof-closing-keyword.mjs — the blocking gate — is handed PR_BODY by .github/workflows/partof-closing-keyword-guard.yml.
GitHub's closing-keyword parser acts on two surfaces, not one: the PR body, and the commit messages of commits that land on the default branch. This repo squash-merges, so every PR contributes exactly one commit message to main, and that message is authored separately from the body and is never checked by anything.
Two consequences that do not exist on the body surface:
A body-clean PR can still close a card, if the closing keyword lives only in the commit message. The blocking gate goes green and nothing else looks.
Backticks do not help. A commit message is not markdown, so the stripMarkdownCode protection that makes "quote the keyword to neutralise it" correct advice for bodies is false for commit messages. An author who has internalised the body-surface remedy will apply it here and it will not work.
Measured
Over all 1,418 squash commit messages on main (2026-08-11 … 2026-08-21), read with the repo's own extractors:
228 closing-keyword+#N bindings across 197 commit messages.
6 commit messages carry Part of #Nand a closing keyword bound to that same #N — the exact contradiction shape H7 exists to report, on a surface H7 cannot see:
0c24898c0 fix(metadata-protocol): judge a package publish against the batch's own pending declarations
d7283250d fix(metadata-protocol): per-item publish re-binds runtime consumers and finds env-wide drafts
af2a989be fix(pm): dispatch-gates derives its own change set from the merge base
3db37957c fix(cloud-connection): move install-local outside the cloud ternary in README example
7e06f51ee fix(example-crm): bind sales positions to crm_sales_user permission set
30536e37c fix(runtime): actionLooksDestructive classifies on declared semantics only
⚠️ These six are evidence that the shape reaches main unguarded, not six adjudicated incidents. Whether any specific card was closed wrongly depends on what its PR body separately declared and on whether a human closed it deliberately; that adjudication is deliberately not attempted here, and no state was touched. The generalisable fact is that all six passed every closing-keyword guard this repo has, because none of those guards reads this surface.
The strongest evidence is that it happened to this seat, on this card
While writing the commit message for the #10392 PR — a commit whose entire subject is this defect class — the first draft quoted the specimen sentence verbatim. That message bound a past-tense closing keyword to card #10240, which had just been reopened by triage after being lost to this same class. Merging it would have closed that card a second time.
Nothing in the repo would have caught it. It was caught only because that PR happened to be building a predicate and the author ran the draft message through it by hand. An author landing an ordinary change has no such step.
Suggested shape (not costed — this is an observation)
The cheap version is a CI step that runs the existing predicates over the commit messages a PR contributes, rather than only over its body — the same functions, a different input, no new grammar. Worth deciding first:
Which messages. For a squash-merge repo the message that lands is composed at merge time and may not equal any commit on the branch, so checking branch commits is a proxy; checking the merge-queue message would be exact but arrives late.
The remedy text must differ from the body-surface remedy. "Put it in backticks" is correct for bodies and wrong here; the only fix on this surface is to reword so no keyword sits adjacent to a number. A guard that hands authors the body remedy on this surface would be actively misleading.
Ref: #10392 (the negation-window row, where this was found) · #8293 (the body-surface mechanism, measured) · #7949 · #8476 (the body-surface blocking promotion) · #9755
Filed unassigned by the
domain:skillsos-dev seat while landing #10392 (H21, the negation-window row). Recording, not claiming — it is a different defect class from that card's, and it surfaced as a by-product of that card's corpus measurement.Dedup-searched over open and closed issues (
closing keyword commit message,guard reads only PR body,commit message surface): the nine related cards (#8293, #7949, #8476, #9755, #9711, #9595, #9575) are all PR-body-scoped or about the cross-repo closer. No card covers this surface.The gap
Every closing-keyword guard in this repo reads the pull request body:
h7PartOfWithClosingKeywordand the newh21NegatedClosingKeywordinscripts/pm/check-half-states.mjsare handed a PR object and readpr.body.scripts/check-partof-closing-keyword.mjs— the blocking gate — is handedPR_BODYby.github/workflows/partof-closing-keyword-guard.yml.GitHub's closing-keyword parser acts on two surfaces, not one: the PR body, and the commit messages of commits that land on the default branch. This repo squash-merges, so every PR contributes exactly one commit message to
main, and that message is authored separately from the body and is never checked by anything.Two consequences that do not exist on the body surface:
stripMarkdownCodeprotection that makes "quote the keyword to neutralise it" correct advice for bodies is false for commit messages. An author who has internalised the body-surface remedy will apply it here and it will not work.Measured
Over all 1,418 squash commit messages on
main(2026-08-11…2026-08-21), read with the repo's own extractors:#Nbindings across 197 commit messages.Part of #Nand a closing keyword bound to that same#N— the exact contradiction shapeH7exists to report, on a surfaceH7cannot see:mainunguarded, not six adjudicated incidents. Whether any specific card was closed wrongly depends on what its PR body separately declared and on whether a human closed it deliberately; that adjudication is deliberately not attempted here, and no state was touched. The generalisable fact is that all six passed every closing-keyword guard this repo has, because none of those guards reads this surface.The strongest evidence is that it happened to this seat, on this card
While writing the commit message for the #10392 PR — a commit whose entire subject is this defect class — the first draft quoted the specimen sentence verbatim. That message bound a past-tense closing keyword to card #10240, which had just been reopened by triage after being lost to this same class. Merging it would have closed that card a second time.
Nothing in the repo would have caught it. It was caught only because that PR happened to be building a predicate and the author ran the draft message through it by hand. An author landing an ordinary change has no such step.
Suggested shape (not costed — this is an observation)
The cheap version is a CI step that runs the existing predicates over the commit messages a PR contributes, rather than only over its body — the same functions, a different input, no new grammar. Worth deciding first:
Ref: #10392 (the negation-window row, where this was found) · #8293 (the body-surface mechanism, measured) · #7949 · #8476 (the body-surface blocking promotion) · #9755
Generated by Claude Code