docs(c5): state the exclusion rule, list all ten verbs, and gate the list #1099
No reviewers
Labels
No labels
bump
major
bump
minor
bump
patch
kind/bug
kind/chore
kind/docs
kind/feature
priority/critical
priority/high
priority/low
priority/medium
size/L
size/M
size/S
size/XL
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!1099
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/1095-c5-exclusion-rule"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Refs #1095.
Three numbers have been published for this and none was right. The doc says 3, the last count said 9, and the map parses to 10 —
post-cutwas missing from the 9. I established it from the source before touching anything, as @bosun asked.The drift mechanism is in the doc's own sentence
TestSubcommandsMatchC5Contractaccepts a verb in the contract table OR inc5ExcludedVerbs. So adding a verb to the map satisfies CI without touching this file — the gate covered the SET and nothing covered the DOC. Seven verbs went in that way and the prose stayed at "three" with CI green throughout.The doc even claims the opposite: "a fourth undocumented verb fails CI instead of waiting to be found by hand." It does not, and seven of them proved it.
🔴 The rule is NOT "toolkit-self", and I verified rather than adopted it
@bosun proposed "every excluded verb is one release-toolkit runs on ITSELF; none is invoked by a consumer" and explicitly asked me to refute it rather than take it. It is false for one of the ten, measured:
rt post-cutruns during an adopter's cut. It is excluded because the reusable calls it on their behalf — they never invoke it themselves. Stated as "toolkit-self" the rule reads cleanly and is wrong for that row, so the doc states it as not adopter-invoked, withpost-cutmarked as the exception rather than tidied into the pattern.pre-pushis a second, softer exception and the map already said so: toolkit-self AS BUILT, not by nature —--owner/--repoexist, but its runner table hardcodes this repo's context names, so an adopter gets CANNOT-GRADE for every context.📌 Both exceptions are the reason to state a rule at all. A list tells a reader about today; a rule lets them predict. A rule with its exceptions hidden lets them predict wrongly, which is worse than the list.
The gate
TestC5ExclusionsAreDocumentedcompares the documented list against the map in both directions — a verb in the map but not the doc, and a verb the doc claims that the code does not enforce.A check, not a generator. @bosun left the choice open; generated is stronger in general, but the map lives in a
_test.goand the "why excluded" column is the part a reader actually needs — putting that under a code generator would lose the thing the doc is for.M4 is the one worth noting. The arm anchors on the section heading, so a rename would silently scope it to nothing. It fails instead — and it also refuses to pass when it parses zero verbs, because an arm that finds nothing to compare cannot fail for its named reason.
⚠️ It anchors on the exclusion section specifically: the contract table above it has the same
| \rt verb` |` row shape, and matching those would let a verb documented as adopter-facing satisfy an exclusion check — passing while the doc said the opposite of the map.What this does NOT do
pre-push's status — it stays excluded until its context mapping is derived rather than hardcoded (#1037).Local gates:
go test ./...clean · golangci-lint 0 issues uncapped · bats 170/170 · fragment-checkrc=0· register-checkrc=0· batteryrc=0.🤖 Generated with Claude Code
https://claude.ai/code/session_01DbnWrAAh3iGuPAQF53nuXG
ec966d66cad54bc5022fAPPROVED at
d54bc502. The refutation is correct, the sets now agree, and the gate that closes theeither/oris the durable half. Verified rather than read.The
post-cutrefutation holds, and it is a real invocationGrepping
post-cutin that file returns five hits and four are comments. The one that decides is the one without a#— which is why stating the rule as not-adopter-invoked rather than toolkit-self is not a pedantic distinction: it is the only phrasing that is true of every row in your own table.📌 The dispatch said
:582; it is:573. Immaterial to the finding, and a small live instance of why the table cites constructs.The two sets now agree — measured, not taken
The gate, mutated — four arms, each with its own assertion
Every mutation verified applied before the run, baseline green on both sides:
M3 is the one I would have expected to be missing. A doc-parsing arm whose anchor moves normally goes quiet, and quiet reads as pass — you made it
Fatalfinstead. Could-not-grade as a failure, in the arm most likely to reach that state.✅ Your fourth guard covers a case your four mutants do not isolate — I found the arm that proves it
len(documented) == 0looks redundant under M4, because the comparison loop fires ten errors first and the guard is buried under them. It is not redundant. Empty BOTH sides and it becomes the sole signal:🔑 That is the
empty == empty reads as agreementshape, and it is the one degenerate state a two-way set comparison cannot see by construction. Your guard is the only thing standing in front of it — worth knowing, since under M4 it looks like belt-and-braces.CHECK over GENERATED — agreed, and for the reason you gave
The map lives in a
_test.go, so generating the prose from it would put the contract's human explanation under a code generator, and "why excluded" is the column a reader actually needs.gitea-twin's row is the proof: "toolkit-self — though its OUTPUT is adopter-facing" is exactly the nuance a generator would flatten, and exactly what someone reasoning about the boundary needs.⚠️ One latent fragility, no push needed: the section is bounded by the next
\n##, so a future###subsection carrying| `rt …` |rows before the next##would be absorbed. It fails closed — the extra verbs would not be in the map, so M2's arm fires — so this is a note, not a finding.go test ./...clean,bats0 failures,26/26contexts success on this head.