feat(rt-changelog-body-check): port changelog-body-check.sh → rt changelog-body-check #577
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!577
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/569-rt-changelog-body-check"
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?
What
Phase 6b validator port (ADR-0009 §3.3):
scripts/changelog-body-check.sh→rt changelog-body-check [path]. The mechanical Cold-Read linter for a release PR body (release-toolkit#282): target the newest## [vX.Y.Z]section (or## [Unreleased]) and run nine checks — 1/2/3/6 fail-loud, 4/5 warn-only, 7/8/9 density (fail + warn bands). NOT a Cutter consumer (pure changelog read). 3-state exit: 0 pass-or-warn-only, 1 a fail-loud violation, 2 file-missing / no-section.Refs #569 · Refs ADR-0009 §3.3 · the milestone-#79 changelog-body gate
Sequencing — first of the wire-existing-gates trio (dependency-over-dispatch-order)
The trio was dispatched #567→#568→#569, but the oracle-empirical read reordered it to #569 → #567, #568 independent (ratified).
compose-verify(#567) shells out tochangelog-body-checkun-redirected, so this gate'sPASS: check Nchrome flows through to compose-verify's compared stdout (measured: 678 bytes, not the "empty" the old comment claimed). #567 goes light by reusing this port's renderer.Framing-verify — a small-from-scratch port (scope-expansion, ratified)
The pure
gates.ChangelogBodyCheckexisted but returned only per-check verdicts ([9]Verdict) and discarded the message detail (version=X date=Y, char-count, section list, the≤-bearing thresholds) that the compared stdout chrome needs. So the port is a result refinement + a message renderer + the cmd + the byte-oracle harness — comparable to a small from-scratch port, not thin wire-existing.Shape
internal/gatesBodyCheckResultrefinement (R2) —Checks [9]Verdict→[9]CheckOutcome{Verdict, Message, Hits}. Each check builds its byte-faithful bash_pass/_warn/_failmessage (+ density-check per-hit snippets). Presentation lives with the logic (theFragmentLengthWarnLine-return shape), directly unit-testable. Substrate-refinement-per-port (manifest.Store #558, fragments #572), now atgates.cmd/rtrenderBodyCheck— the shared cut-time renderer: PASS chrome → stdout, WARN/FAIL + hits → stderr, blank + summary.rt changelog-body-checkcalls it directly;rt compose-verify(#567) will call it for Gate 2's passthrough. Second command-layer utility this arc (after #572'sexitError).cmd/rt changelog-body-check [path]— file-missing / no-section → exit 2 (exitError); FAIL → exit 1; pass/warn-only → exit 0."stdout empty both sides"comment (internal/gatescompose_verifytest + oracleshim) that the #567 measurement disproved.Ratified forks (#569)
[9]CheckOutcomerefactor over additive parallel arrays — the honest model;Exit()+ the existing gates test updated trivially.→ hitsnippets) is emitted for operator faithfulness but NOT harness-byte-compared; the FAIL/WARN message strings are unit-pinned; hit snippets are rune-truncated to dodge the #572-F4cut -c-under-LC_ALL=Cbytes-vs-runes trap.CHANGELOG_BODY_CHECK_*thresholds out of scope — a pre-existing disclosed boundary (gates const block); the renderer uses the built-in defaults, matching bash's default path (harness tests the default path).≤(U+2264) on the COMPARED stdout surface; FAIL/WARN lines carry em-dash / en-dash /§/→on stderr. Byte-faithful over the §5 ASCII aspiration — same shape as #572's em-dash.Verification
internal/gatesTestCheckOutcomeMessages/TestCheckOutcomeFailMessages— byte-exact PASS chrome (incl. the≤) + two FAIL messages (em-dash).cmd/rtTestChangelogBodyCheckEquivalence— the prebuilt binary vs the real bash script over 15 scenarios: every check's PASS line, each WARN band (4/5/7/9), each FAIL band (1/2/3/6/7/8/9), the Unreleased skip, and both exit-2 paths (no-section, file-missing — both empty stdout).cmd/rtTestChangelogBodyCheckExitCodes/TestChangelogBodyCheckRenderRouting— the 3-state exit (incl. theexitErrorexit-2 paths) + the PASS→stdout / FAIL,WARN→stderr stream split (the contract #567's Gate-2 passthrough relies on).Mutation experiment (non-vacuity — three axes)
All reverted by re-edit; 0 residue.
Full gate green on host +
forgejo-ci-go:latest(go1.26.2):golangci-lint0 issues,go build,go vet, wholego test ./...,gofmt -lclean,shellcheckon the oracle wrapper clean.What this PR does NOT do
internal/verdictwiring — the gate is a bespoke 3-state exit; the exit-2 ridesexitError, exit-1 is a plain red.→ hitsnippets are emitted for faithfulness but unit-pinned, not byte-compared (S-a, batch precedent). The hit snippets carry acut -c1-70truncation that counts bytes underLC_ALL=Cbut runes in the port (the #572-F4 class), so they stay off the locale-pinned harness.CHANGELOG_BODY_CHECK_*threshold env-override — a pre-existing disclosed boundary; the renderer uses the defaults that match bash's default path.rt compose-verify(#567) itself — this PR lands the shared renderer #567 consumes; #567 is the next in the trio.Review — PR#577, rt changelog-body-check (Go port + BodyCheckResult refactor, #569)
Independent deep-verify at head
571421fd(basemain@ab1ec3e, incl #572). Read the 400-line bash oracle + the[9]CheckOutcomerefactor + the renderer, ran the full gate (go1.26.2,build/vet/golangci-lint0 issues/go test ./...), reproduced all 3 mutations, and ran my own bash-vs-rtdifferential on all 14 fixtures + both exit-2 paths, independent of the shipped harness.No must-fix. One small (doc-only) should-consider. The heavy refactor lands byte-faithful.
Verified faithful
build/vet/golangci-lint(0 issues)/go test ./...green; CI 8/8 on571421fd.≤U+2264 byte-faithfulnesse2 89 a4, Go source (428/444/466) =e2 89 a4, andrt's rendered check-7 stdout =e2 89 a4. Byte-compared in the harness (clean + checks 7/8/9) AND mutation M2 (≤→<=) reds exactly check_7/8/9 + clean. Load-bearing compared byte, confirmed.PASS:→stdout (compared),WARN:/FAIL:+→ hit→stderr, blank→stdout, summary (PASS/passed with warnings→stdout,FAILED→stderr). Within stdout the order is check-order 1..9 + blank + summary == bash. Mutation M1 (PASS→stderr) reds all 14 PASS-chrome scenarios.[9]Verdict→[9]CheckOutcomerefactorinternal/gatestests pass; the only external consumer (oracleshim) is adapted behavior-neutrally (v→c.Verdict, prints the same verdict). No backward-compat break becauseBodyCheckResultis internal-only — the honest-shape refactor is safe.truncateRunes(…, 70)truncates by rune where bashcut -c1-70truncates by BYTE under LC_ALL=C — the correct choice, and since hits ride stderr (uncompared) the divergence never reaches the byte-oracle. Unit-pinned. Honest split.errChangelogBodyFail) / 2 (file-missing + no-section, via reusedexitError{2}from #572). Mutation M3 (no-section exit-2→1) reds exactly no-section, not file-missing (distinct sentinels) — narrowest. A directory arg trips exit-2 on both sides (verified).res.Checksloop change is the required type adaptation, not a behavior change.should-consider (small, doc-only) — the dropped env-overrides have no in-code breadcrumb
bash reads
CHANGELOG_BODY_CHECK_SENTENCE_WARN/..._PARAGRAPH_WARN(oracle lines 66-69) to let a consumer retune the density thresholds; the Go port hardcodessentenceWarn = 25/paragraphWarn = 75consts. Dropping the override is ratified out-of-scope (framing-verify) and disclosed in the PR — the concern is only that the shipped code carries no hint of it. A future maintainer (or a consumer who sets the env expecting bash's behavior and silently gets the hardcoded threshold — a band-flip on the compared surface) has to dig into PR#577 to learn it was deliberate. Per the house scope-at-point-of-use discipline (state a mechanism's scope at the point of use, not just in the PR conversation), a one-line comment near the const block — "bash'sCHANGELOG_BODY_CHECK_*threshold overrides are intentionally out-of-scope for this port (#569); land as a follow-up if a consumer needs them" — closes it. Non-blocking; the decision itself is sound and already ratified.Verdict
APPROVED, head-pinned at
571421fd. The refactor + renderer are byte-faithful (my independent differential agrees with the 15-scenario harness across all fixtures + both exit-2 paths), the≤is byte-faithful end-to-end (source→source→rendered,e2 89 a4, byte-compared + mutation-confirmed), the[9]CheckOutcomerefactor preserves behavior with its one consumer adapted cleanly, the stderr-unit-pinned / stdout-byte-compared split is honest, and all 3 mutations reproduce narrowest-correct.renderBodyCheckis a clean shared substrate for #567's light path. The one should-consider is a doc breadcrumb, yours to weigh. Heavy validator, lands clean. Yours to land.— Surveyor