Commit 0374bcb
ci(partof-guard): the closing-keyword gate reads every commit message on the PR (#16371)
The gate judged one of the two surfaces GitHub's reference parser acts on.
It read the PR body; it never read the commit messages, and this repo
squash-merges, so the message that lands on the default branch is assembled
at merge time by concatenating them. That text is written by nobody and
reviewed by nobody: it carries every trailer its inputs carried, and it can
contradict itself where none of its parts did.
The specimen is the squash of PR 16247, commit fc3fb7c, an ancestor of
the default branch: three bullets, a closing trailer for a card inside the
first bullet's body, and a third bullet retracting a claim the first still
makes. No body-side rule could have seen it -- the contradictory text
existed in no body -- and the sweep's H23, which patrols this surface after
merge, binds narrower and is silent on it: H23 reports only the Part-of plus
closing-keyword contradiction, and this specimen declares no Part-of at all.
So the gate now enforces a second, strictly wider rule: no commit on the PR
may carry a card-relation trailer at all. Width is what makes it
enforceable -- "trailers that would contradict each other once concatenated"
is a property of an assembly that does not exist until the merge button,
while "no trailer in any commit" is a property of one commit. An assembly
cannot manufacture what none of its inputs contain.
The relation extractors are the sweep's, imported at `markdown: false` and
not re-spelled: a fourth spelling of the closing-keyword grammar would be a
fourth thing for the parity gate to hold in step, and the commit-message
reading is a measured contract of that sweep rather than this gate's call.
The workflow gathers the list and hands it over as a file path, so the
judging path stays HTTP-free by construction. The endpoint is read rather
than `git log base..head` walked: it returns exactly the set GitHub will
squash, while the walk needs a merge base this depth-1 checkout does not
have and would report another author's landed trailers as this PR's.
An absent, malformed or empty commit list exits 2 and says which rule judged
nothing. A wiring that forgot the commits has not seen a clean commit
history, and the two must never print the same line.
Self-test batteries 9 -> 15, cases 28 -> 66, with the real squash message as
the regression fixture.
Claude-Session: https://claude.ai/code/session_01Vbw3RPgdtqesx4azk9SbW8
Co-authored-by: Claude <noreply@anthropic.com>1 parent 65ec530 commit 0374bcb
2 files changed
Lines changed: 549 additions & 28 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
36 | 41 | | |
37 | 42 | | |
| 43 | + | |
38 | 44 | | |
39 | 45 | | |
40 | 46 | | |
| |||
87 | 93 | | |
88 | 94 | | |
89 | 95 | | |
90 | | - | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
91 | 135 | | |
92 | 136 | | |
93 | 137 | | |
| 138 | + | |
94 | 139 | | |
0 commit comments