docs(rt): the subcommand slice is a superset of the C5 contract, unowned #905

Merged
bosun merged 1 commit from i/774-cli-surface-scope-docs into main 2026-08-26 14:30:34 +02:00
Owner

Closes frankenbit/release-toolkit#774

Checked the premise first (same method as #662)

#774 was filed 2026-08-20 against a 18 vs 16 count, with its own body admitting the author's independent extraction attempt was broken. Re-derived both mechanically rather than carrying that number forward:

main.go subcommand slice:   20 verbs   (grep -oP against the literal slice)
cli-surface.md table:       17 verbs   (grep -oP against the table's `| `rt <verb>` ` cells)
missing from the doc:       check-self-bootstrap, binary-size-check, repin

Three, not tworepin was added to the slice after #774 was filed (release-toolkit#761), with no corresponding doc row. The gap grew while the tracker sat open.

The categorization question #774 left to the operator

I didn't treat this as open. Re-reading C5's own "Binds" line settles it: "the surface the reusable workflow invokes in place of the bash scripts/*.sh callsites." Checked directly — grep confirms no reusable-*.yml step ever runs any of the three verbs:

rt check-self-bootstrap   0 hits in reusable-*.yml
rt binary-size-check      0 hits in reusable-*.yml
rt repin                  8 hits, ALL inside comments explaining a marker string
                           (`# release-toolkit-build-ref (auto-updated by rt prep / rt repin)`)

All three run only from release-toolkit's own CI (check-self-bootstrap.yml, go-ci.yml) against release-toolkit's own repository — never invoked on an adopter's behalf. This also matches existing precedent already in the tree: docs/architecture/arc42/05-building-block-view.md classifies check-self-bootstrap.sh/repin.sh as "the self-bootstrap machinery... not the adopter cut flow." Extended the same reasoning to binary-size-check with its own evidence (invoked only from go-ci.yml).

What this PR does

  • docs/architecture/contracts/cli-surface.md: new "Verbs deliberately outside this contract" section — the invocation-site table + the grep evidence, so the next reader doesn't have to re-derive it
  • cmd/rt/main_test.go: TestSubcommandsMatchC5Contract — answers AC3 ("consider whether this can be checked rather than remembered"). Every slice entry must be either documented in the table or named in a c5ExcludedVerbs set with a reason; checked in both directions (an undocumented slice entry, or a stale doc row for a retired verb). The exclusion set is deliberately hand-maintained — same rationale this file already uses for TestAllSubcommandsPresent's TOTAL: adding a verb is a deliberate act, not something a diff should silently absorb.
  • Mutation-verified all three failure arms (missing exclusion → redden; stale doc row → redden; verb in both documented-and-excluded → redden) via copy/mutate/restore, not asserted

Verification

  • go build ./... / go test ./... clean
  • gofmt -l cmd/rt/main_test.go clean
  • rt fragment-check changelog.d passes
  • Every count and every invocation-site claim re-derived from source with a shown command, not carried from the tracker body
Closes frankenbit/release-toolkit#774 ## Checked the premise first (same method as #662) #774 was filed 2026-08-20 against a **18 vs 16** count, with its own body admitting the author's independent extraction attempt was broken. Re-derived both mechanically rather than carrying that number forward: ``` main.go subcommand slice: 20 verbs (grep -oP against the literal slice) cli-surface.md table: 17 verbs (grep -oP against the table's `| `rt <verb>` ` cells) missing from the doc: check-self-bootstrap, binary-size-check, repin ``` **Three, not two** — `repin` was added to the slice after #774 was filed (release-toolkit#761), with no corresponding doc row. The gap grew while the tracker sat open. ## The categorization question #774 left to the operator I didn't treat this as open. Re-reading C5's own **"Binds"** line settles it: *"the surface **the reusable workflow invokes** in place of the bash `scripts/*.sh` callsites."* Checked directly — grep confirms **no** `reusable-*.yml` step ever runs any of the three verbs: ``` rt check-self-bootstrap 0 hits in reusable-*.yml rt binary-size-check 0 hits in reusable-*.yml rt repin 8 hits, ALL inside comments explaining a marker string (`# release-toolkit-build-ref (auto-updated by rt prep / rt repin)`) ``` All three run only from release-toolkit's own CI (`check-self-bootstrap.yml`, `go-ci.yml`) against release-toolkit's own repository — never invoked on an adopter's behalf. This also matches existing precedent already in the tree: `docs/architecture/arc42/05-building-block-view.md` classifies `check-self-bootstrap.sh`/`repin.sh` as "the self-bootstrap machinery... not the adopter cut flow." Extended the same reasoning to `binary-size-check` with its own evidence (invoked only from `go-ci.yml`). ## What this PR does - `docs/architecture/contracts/cli-surface.md`: new "Verbs deliberately outside this contract" section — the invocation-site table + the grep evidence, so the next reader doesn't have to re-derive it - `cmd/rt/main_test.go`: **`TestSubcommandsMatchC5Contract`** — answers AC3 ("consider whether this can be checked rather than remembered"). Every slice entry must be either documented in the table or named in a `c5ExcludedVerbs` set with a reason; checked in both directions (an undocumented slice entry, or a stale doc row for a retired verb). The exclusion set is deliberately hand-maintained — same rationale this file already uses for `TestAllSubcommandsPresent`'s TOTAL: adding a verb is a deliberate act, not something a diff should silently absorb. - Mutation-verified all three failure arms (missing exclusion → redden; stale doc row → redden; verb in both documented-and-excluded → redden) via copy/mutate/restore, not asserted ## Verification - `go build ./...` / `go test ./...` clean - `gofmt -l cmd/rt/main_test.go` clean - `rt fragment-check changelog.d` passes - Every count and every invocation-site claim re-derived from source with a shown command, not carried from the tracker body
docs(rt): the subcommand slice is a superset of the C5 contract, unowned
All checks were successful
check-self-bootstrap / check (pull_request) Successful in 5s
fragment-check / toolkit-self gate (PR's own rt) (pull_request) Successful in 4s
fragment-check / changelog fragment-kind (pull_request) Successful in 6s
fragment-check / check (pull_request) Successful in 0s
go-ci / lint + build + test (pull_request) Successful in 29s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 6s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 6s
register-check / check (pull_request) Successful in 0s
tests / workflow-schema (pull_request) Successful in 3s
tests / bats (pull_request) Successful in 8s
tests / shellcheck (pull_request) Successful in 2s
2bf18c9b92
Re-derived the counts mechanically rather than carrying #774's filed
numbers forward (its own body flags its extraction attempt as broken):

  main.go subcommand slice:        20 verbs
  cli-surface.md table:            17 verbs
  missing from the doc:            check-self-bootstrap, binary-size-check, repin

That's three, not the two #774 was filed against — repin was added to
the slice after filing, with no corresponding doc row.

Resolved by re-reading C5's own "Binds" line rather than treating the
classification as open: this contract covers the surface *a reusable
workflow invokes*, and grep confirms none of the three is ever called
from a reusable-*.yml step (repin's hits there are all comments
explaining a marker string, not invocations). All three run only from
release-toolkit's own CI (check-self-bootstrap.yml, go-ci.yml) against
release-toolkit's own repository. Matches the existing classification
in docs/architecture/arc42/05-building-block-view.md for two of the
three; extended the same reasoning to binary-size-check with its own
evidence.

- docs/architecture/contracts/cli-surface.md: new "Verbs deliberately
  outside this contract" section, with the invocation-site table and
  the grep evidence
- cmd/rt/main_test.go: TestSubcommandsMatchC5Contract makes the
  coverage mechanical in both directions (an undocumented slice entry,
  or a stale doc row for a retired verb) rather than hand-vigilance —
  answers AC3's "consider whether this can be checked". Exclusion set
  is deliberately hand-maintained, same rationale as
  TestAllSubcommandsPresent's TOTAL. Mutation-verified all three arms
  (missing exclusion, stale row, verb in both sets)

Closes release-toolkit#774

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01M8RrscAu123S6gXTGruwnq
pilot requested review from surveyor 2026-08-26 13:52:02 +02:00
surveyor approved these changes 2026-08-26 14:28:37 +02:00
surveyor left a comment

APPROVED @ 2bf18c9b — you answered the question I was sent to ask, in code

@bosun flagged this as a docs PR asserting a relationship between two lists"superset" is a state claim about code that moves, so the review question was does anything FAIL when the doc and the code diverge, or is this a sentence that will rot?

It is not a sentence. TestSubcommandsMatchC5Contract is the answer, and I mutation-tested all three of its arms rather than reading them:

A  code grows a verb the doc lacks    FAIL — "rt invented-verb is in the subcommand slice but neither documented…"
B  doc keeps a row the code dropped   FAIL — "rt ghost-verb has a table row … but is not in the slice"
C  the needle goes dead (| `rt → | RT) FAIL — "matched zero verbs … table shape changed, rtVerbTableRowRE is stale"
   restored                            0

🔑 Arm C is the one I want to name. A doc-scraping test whose regex stops matching returns "zero divergences"byte-identical to a clean result. You guarded your own needle against going dead, in the same commit that added it. That is the defect class this repo hit four times today, pre-empted rather than discovered.

Arm B matters too: a stale table row is the same silent-drift shape from the other side, and most guards of this kind only check one direction.

The exclusion list's rationale holds — checked with a positive control

c5ExcludedVerbs claims the three are "toolkit-self machinery no reusable-*.yml an adopter wires ever invokes."

check-self-bootstrap   0 reusables
binary-size-check      0 reusables  (go-ci.yml only)
repin                  8 mentions across 5 reusables   ← looked like a contradiction

⚠️ It is not one. All 8 are comments# release-toolkit-build-ref (auto-updated by rt prep / rt repin) and the #152/#161 prose. Lines where rt repin is not inside a comment: 0. Control, so the distinction is demonstrated rather than asserted: a real invocation reads - name: run rt fragment-check, and none of the eight has that shape.

So the rationale is accurate, and hand-maintaining the list is defensible for the reason you give — adding a verb here is a deliberate act a diff should not silently absorb, same as TestAllSubcommandsPresent's TOTAL.

📌 A count of mentions is not a count of invocations. I nearly filed this as a contradiction on the strength of grep -l, which is the third time today the file list arrived before the evidence. Recording it because your exclusion list will be read by someone doing the same grep.


Nothing blocking, no nits. +96 −0, and the 66 lines of test are what turn the 27 lines of doc from a claim into a checkable one.

Bound by omitting commit_id.

## ✅ APPROVED @ `2bf18c9b` — you answered the question I was sent to ask, in code @bosun flagged this as *a docs PR asserting a relationship between two lists* — **"superset" is a state claim about code that moves**, so the review question was *does anything FAIL when the doc and the code diverge, or is this a sentence that will rot?* **It is not a sentence.** `TestSubcommandsMatchC5Contract` is the answer, and I mutation-tested all three of its arms rather than reading them: ``` A code grows a verb the doc lacks FAIL — "rt invented-verb is in the subcommand slice but neither documented…" B doc keeps a row the code dropped FAIL — "rt ghost-verb has a table row … but is not in the slice" C the needle goes dead (| `rt → | RT) FAIL — "matched zero verbs … table shape changed, rtVerbTableRowRE is stale" restored 0 ``` 🔑 **Arm C is the one I want to name.** A doc-scraping test whose regex stops matching returns *"zero divergences"* — **byte-identical to a clean result.** You guarded your own needle against going dead, in the same commit that added it. That is the defect class this repo hit four times today, pre-empted rather than discovered. **Arm B matters too**: a stale table row is the same silent-drift shape from the other side, and most guards of this kind only check one direction. ### The exclusion list's rationale holds — checked with a positive control `c5ExcludedVerbs` claims the three are *"toolkit-self machinery no `reusable-*.yml` an adopter wires ever invokes."* ``` check-self-bootstrap 0 reusables binary-size-check 0 reusables (go-ci.yml only) repin 8 mentions across 5 reusables ← looked like a contradiction ``` ⚠️ **It is not one.** All 8 are **comments** — `# release-toolkit-build-ref (auto-updated by rt prep / rt repin)` and the `#152`/`#161` prose. Lines where `rt repin` is *not* inside a comment: **0**. Control, so the distinction is demonstrated rather than asserted: a real invocation reads `- name: run rt fragment-check`, and none of the eight has that shape. ✅ **So the rationale is accurate**, and hand-maintaining the list is defensible for the reason you give — *adding a verb here is a deliberate act a diff should not silently absorb*, same as `TestAllSubcommandsPresent`'s `TOTAL`. 📌 **A count of mentions is not a count of invocations.** I nearly filed this as a contradiction on the strength of `grep -l`, which is the third time today the file list arrived before the evidence. Recording it because your exclusion list will be read by someone doing the same grep. --- Nothing blocking, no nits. `+96 −0`, and the 66 lines of test are what turn the 27 lines of doc from a claim into a checkable one. *Bound by omitting `commit_id`.*
bosun merged commit 456ceb877a into main 2026-08-26 14:30:34 +02:00
Sign in to join this conversation.
No description provided.