feat(rt-changelog-body-check): port changelog-body-check.sh → rt changelog-body-check #577

Merged
bosun merged 1 commit from i/569-rt-changelog-body-check into main 2026-07-27 13:49:59 +02:00
Owner

What

Phase 6b validator port (ADR-0009 §3.3): scripts/changelog-body-check.shrt 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 to changelog-body-check un-redirected, so this gate's PASS: check N chrome 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.ChangelogBodyCheck existed 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/gates BodyCheckResult refinement (R2)Checks [9]Verdict[9]CheckOutcome{Verdict, Message, Hits}. Each check builds its byte-faithful bash _pass/_warn/_fail message (+ density-check per-hit snippets). Presentation lives with the logic (the FragmentLengthWarn Line-return shape), directly unit-testable. Substrate-refinement-per-port (manifest.Store #558, fragments #572), now at gates.
  • cmd/rt renderBodyCheck — the shared cut-time renderer: PASS chrome → stdout, WARN/FAIL + hits → stderr, blank + summary. rt changelog-body-check calls it directly; rt compose-verify (#567) will call it for Gate 2's passthrough. Second command-layer utility this arc (after #572's exitError).
  • cmd/rt changelog-body-check [path] — file-missing / no-section → exit 2 (exitError); FAIL → exit 1; pass/warn-only → exit 0.
  • Stale-comment fold — corrects the "stdout empty both sides" comment (internal/gates compose_verify test + oracleshim) that the #567 measurement disproved.

Ratified forks (#569)

Fork Decision
R2 cohesive [9]CheckOutcome refactor over additive parallel arrays — the honest model; Exit() + the existing gates test updated trivially.
S-a compared surface = STDOUT (PASS chrome + summary) + exit-code (batch precedent, all 5 prior ports). stderr (FAIL/WARN + → hit snippets) 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-F4 cut -c-under-LC_ALL=C bytes-vs-runes trap.
#491 WARN-5→FAIL out of scope — a separate future behavior change (open, not in milestone #79). Port current check-5 = WARN, byte-faithful to the live oracle.
env-override 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).
non-ASCII checks 7/8/9 PASS lines carry a literal (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/gates TestCheckOutcomeMessages / TestCheckOutcomeFailMessages — byte-exact PASS chrome (incl. the ) + two FAIL messages (em-dash).
  • cmd/rt TestChangelogBodyCheckEquivalence — 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/rt TestChangelogBodyCheckExitCodes / TestChangelogBodyCheckRenderRouting — the 3-state exit (incl. the exitError exit-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.

(1) render FAIL → stdout (mis-route):
    reds EXACTLY the FAIL scenarios' stdout (missing-sections diverges at byte 54);
    PASS/WARN/exit-2 scenarios stay GREEN.
(2) check-7 PASS  ≤ → <=:
    reds EXACTLY the ≤-bearing PASS chrome (clean diverges at byte 460) + the
    internal/gates message unit; every FAIL/exit-2 scenario GREEN.
(3) NoSection exit-2 → 0:
    reds EXACTLY no-section (exit_code) + the cmd exit-code unit.

Full gate green on host + forgejo-ci-go:latest (go1.26.2): golangci-lint 0 issues, go build, go vet, whole go test ./..., gofmt -l clean, shellcheck on the oracle wrapper clean.

What this PR does NOT do

  • No internal/verdict wiring — the gate is a bespoke 3-state exit; the exit-2 rides exitError, exit-1 is a plain red.
  • No harness-byte-compare of stderr — the FAIL/WARN diagnostics + → hit snippets are emitted for faithfulness but unit-pinned, not byte-compared (S-a, batch precedent). The hit snippets carry a cut -c1-70 truncation that counts bytes under LC_ALL=C but runes in the port (the #572-F4 class), so they stay off the locale-pinned harness.
  • No #491 WARN-5→FAIL promotion — a separate behavior change on its own tracker.
  • No 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.
## 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 to `changelog-body-check` **un-redirected**, so this gate's `PASS: check N` chrome 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.ChangelogBodyCheck` **existed** 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/gates` `BodyCheckResult` refinement (R2)** — `Checks [9]Verdict` → `[9]CheckOutcome{Verdict, Message, Hits}`. Each check builds its byte-faithful bash `_pass/_warn/_fail` message (+ density-check per-hit snippets). Presentation lives with the logic (the `FragmentLengthWarn` `Line`-return shape), directly unit-testable. **Substrate-refinement-per-port** (manifest.Store #558, fragments #572), now at `gates`. - **`cmd/rt` `renderBodyCheck`** — the **shared cut-time renderer**: PASS chrome → stdout, WARN/FAIL + hits → stderr, blank + summary. `rt changelog-body-check` calls it directly; `rt compose-verify` (#567) will call it for Gate 2's passthrough. Second command-layer utility this arc (after #572's `exitError`). - **`cmd/rt changelog-body-check [path]`** — file-missing / no-section → exit 2 (`exitError`); FAIL → exit 1; pass/warn-only → exit 0. - **Stale-comment fold** — corrects the `"stdout empty both sides"` comment (internal/gates `compose_verify` test + oracleshim) that the #567 measurement disproved. ## Ratified forks (#569) | Fork | Decision | |------|----------| | **R2** | **cohesive `[9]CheckOutcome`** refactor over additive parallel arrays — the honest model; `Exit()` + the existing gates test updated trivially. | | **S-a** | **compared surface = STDOUT (PASS chrome + summary) + exit-code** (batch precedent, all 5 prior ports). stderr (FAIL/WARN + `→ hit` snippets) 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-F4 `cut -c`-under-`LC_ALL=C` bytes-vs-runes trap. | | **#491** | WARN-5→FAIL **out of scope** — a separate future behavior change (open, not in milestone #79). Port current check-5 = **WARN**, byte-faithful to the live oracle. | | **env-override** | `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). | | **non-ASCII** | checks 7/8/9 PASS lines carry a literal **`≤` (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/gates` `TestCheckOutcomeMessages` / `TestCheckOutcomeFailMessages`** — byte-exact PASS chrome (incl. the `≤`) + two FAIL messages (em-dash). - **`cmd/rt` `TestChangelogBodyCheckEquivalence`** — 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/rt` `TestChangelogBodyCheckExitCodes` / `TestChangelogBodyCheckRenderRouting`** — the 3-state exit (incl. the `exitError` exit-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. ``` (1) render FAIL → stdout (mis-route): reds EXACTLY the FAIL scenarios' stdout (missing-sections diverges at byte 54); PASS/WARN/exit-2 scenarios stay GREEN. (2) check-7 PASS ≤ → <=: reds EXACTLY the ≤-bearing PASS chrome (clean diverges at byte 460) + the internal/gates message unit; every FAIL/exit-2 scenario GREEN. (3) NoSection exit-2 → 0: reds EXACTLY no-section (exit_code) + the cmd exit-code unit. ``` **Full gate green on host + `forgejo-ci-go:latest`** (go1.26.2): `golangci-lint` 0 issues, `go build`, `go vet`, whole `go test ./...`, `gofmt -l` clean, `shellcheck` on the oracle wrapper clean. ## What this PR does NOT do - **No `internal/verdict` wiring** — the gate is a bespoke 3-state exit; the exit-2 rides `exitError`, exit-1 is a plain red. - **No harness-byte-compare of stderr** — the FAIL/WARN diagnostics + `→ hit` snippets are emitted for faithfulness but unit-pinned, not byte-compared (S-a, batch precedent). The hit snippets carry a `cut -c1-70` truncation that counts bytes under `LC_ALL=C` but runes in the port (the #572-F4 class), so they stay off the locale-pinned harness. - **No #491 WARN-5→FAIL promotion** — a separate behavior change on its own tracker. - **No `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.
feat(rt-changelog-body-check): port changelog-body-check.sh → rt changelog-body-check
All checks were successful
check-self-bootstrap / check (pull_request) Successful in 3s
go-ci / lint + build + test (pull_request) Successful in 20s
manifest-check / manifest-vs-tag consistency (pull_request) Successful in 5s
manifest-check / check (pull_request) Successful in 0s
register-check / register-drift check (pull_request) Successful in 5s
register-check / check (pull_request) Successful in 0s
tests / bats (pull_request) Successful in 2m1s
tests / shellcheck (pull_request) Successful in 9s
check-self-bootstrap / check (push) Successful in 3s
go-ci / lint + build + test (push) Successful in 21s
release / decide + act (push) Successful in 12s
release / release (push) Successful in 0s
tests / bats (push) Successful in 1m58s
tests / shellcheck (push) Successful in 8s
571421fd22
Phase 6b validator/gate (ADR-0009 §3.3), 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. 3-state exit: 0 pass-or-warn-
only, 1 a fail-loud violation, 2 file-missing / no-section.

First of the wire-existing-gates trio (#569#567#568), sequenced by the
dependency the oracle revealed: compose-verify (#567) shells out to
changelog-body-check un-redirected, so its PASS chrome flows through to
compose-verify's compared stdout. #567 goes light by reusing this port's renderer.

Framing-verify (Refs #569): the pure gates.ChangelogBodyCheck EXISTED but returned
only per-check verdicts + discarded the message detail the compared stdout chrome
needs. Scope is a small-from-scratch port: a result refinement + a message renderer
+ the cmd + the byte-oracle harness.

Shape:
- internal/gates BodyCheckResult refinement (#569 R2): Checks [9]Verdict →
  [9]CheckOutcome{Verdict, Message, Hits}. Each check now builds its byte-faithful
  bash _pass/_warn/_fail message + (density checks) the per-hit snippets.
  Presentation lives with the logic — the gates.FragmentLengthWarn Line-return
  shape — so it is directly unit-testable. Substrate-refinement-per-port
  (manifest.Store #558, fragments #572), now at gates.
- cmd/rt renderBodyCheck — the SHARED cut-time renderer: PASS chrome → stdout,
  WARN/FAIL + hits → stderr, blank + summary. rt changelog-body-check calls it
  directly; rt compose-verify (#567) will call it for Gate 2's passthrough. Second
  command-layer utility this arc (after #572's exitError).
- cmd/rt changelog-body-check [path] — file-missing / no-section → exit 2
  (exitError); FAIL → exit 1; pass/warn-only → exit 0.
- Folds the stale "stdout empty both sides" comment fix (internal/gates
  compose_verify test + oracleshim) the #567 measurement corrected.

Ratified forks (Refs #569):
- R2 cohesive [9]CheckOutcome refactor over additive parallel arrays.
- S-a compared surface = STDOUT (PASS chrome + summary) + exit-code; stderr
  (FAIL/WARN + hits) emitted for faithfulness but NOT harness-byte-compared (batch
  precedent) — FAIL/WARN message strings unit-pinned; hit snippets rune-truncated
  to dodge the #572-F4 wc/cut-under-LC_ALL=C bytes-vs-runes trap.
- #491 WARN-5→FAIL out of scope (separate future behavior change): port check-5=WARN.
- CHANGELOG_BODY_CHECK_* threshold env-override out of scope (pre-existing disclosed
  boundary; renderer uses defaults = bash default path).

Non-ASCII byte-faithfulness: checks 7/8/9 PASS lines carry a literal ≤ (U+2264) on
the COMPARED stdout surface; the FAIL/WARN lines carry em-dash / en-dash / § / → on
stderr. Ported byte-faithful over the §5 ASCII aspiration (same shape as #572's
em-dash).

Verification:
- internal/gates TestCheckOutcomeMessages / TestCheckOutcomeFailMessages: byte-exact
  PASS chrome (incl the ≤) + two FAIL messages.
- cmd/rt TestChangelogBodyCheckEquivalence: byte-oracle 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).
- cmd/rt TestChangelogBodyCheckExitCodes + TestChangelogBodyCheckRenderRouting: the
  3-state exit + the PASS→stdout / FAIL,WARN→stderr stream split.
- Mutation-verified non-vacuous (all reverted by re-edit, 0 residue):
  (1) render FAIL→stdout → reds EXACTLY the FAIL scenarios' stdout (byte 54);
  (2) check-7 PASS ≤→<= → reds EXACTLY the ≤-bearing PASS (clean byte 460) + unit;
  (3) NoSection exit-2→0 → reds EXACTLY no-section (exit) + the exit-code unit.

Full gate green on host + forgejo-ci-go:latest (go1.26.2).

Refs #569
Refs ADR-0009 §3.3
surveyor approved these changes 2026-07-27 13:49:08 +02:00
surveyor left a comment

Review — PR#577, rt changelog-body-check (Go port + BodyCheckResult refactor, #569)

Independent deep-verify at head 571421fd (base main@ab1ec3e, incl #572). Read the 400-line bash oracle + the [9]CheckOutcome refactor + the renderer, ran the full gate (go1.26.2, build/vet/golangci-lint 0 issues/go test ./...), reproduced all 3 mutations, and ran my own bash-vs-rt differential 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

Area Result
Full gate build/vet/golangci-lint (0 issues)/go test ./... green; CI 8/8 on 571421fd.
My own differential (all 14 fixtures + exit-2 paths) every fixture byte-matches stdout+exit vs real bash — clean/bad-header/missing-sections/forward-ref/warn-length/style-mixed/dup-headers/long-sentence/warn-sentence/deep-parens/long-paragraph/unreleased/fenced-code/no-section, plus file-missing (exit 2) + no-section (exit 2). Not just the shipped harness — my independent run agrees.
U+2264 byte-faithfulness byte-faithful end-to-end. od'd all three: bash source (337/364/386) = e2 89 a4, Go source (428/444/466) = e2 89 a4, and rt'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.
Renderer stream routing faithful: 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]CheckOutcome refactor behavior-preserved: existing internal/gates tests pass; the only external consumer (oracleshim) is adapted behavior-neutrally (vc.Verdict, prints the same verdict). No backward-compat break because BodyCheckResult is internal-only — the honest-shape refactor is safe.
hits rune-truncation (S-a / #572-F4) truncateRunes(…, 70) truncates by rune where bash cut -c1-70 truncates 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.
exit mapping 0 (pass/warn) / 1 (fail-loud, via errChangelogBodyFail) / 2 (file-missing + no-section, via reused exitError{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).
Stale-comment fix (equivalence_test + oracleshim) correct + comment-only in effect: the old "stdout empty both sides" assertion was false once Gate-2's PASS chrome flows through un-redirected; the new comment scopes it correctly (this shim compares exit-only; the orchestrated stdout is #567's harness's job). The res.Checks loop 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 hardcodes sentenceWarn = 25 / paragraphWarn = 75 consts. 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's CHANGELOG_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]CheckOutcome refactor 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. renderBodyCheck is 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

## Review — PR#577, rt changelog-body-check (Go port + BodyCheckResult refactor, #569) Independent deep-verify at head `571421fd` (base `main@ab1ec3e`, incl #572). Read the 400-line bash oracle + the `[9]CheckOutcome` refactor + the renderer, ran the full gate (go1.26.2, `build`/`vet`/`golangci-lint` **0 issues**/`go test ./...`), reproduced all 3 mutations, and ran my **own** bash-vs-`rt` differential 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 | Area | Result | |---|---| | Full gate | ✅ `build`/`vet`/`golangci-lint` (**0 issues**)/`go test ./...` green; CI **8/8** on `571421fd`. | | **My own differential (all 14 fixtures + exit-2 paths)** | ✅ **every fixture byte-matches stdout+exit vs real bash** — clean/bad-header/missing-sections/forward-ref/warn-length/style-mixed/dup-headers/long-sentence/warn-sentence/deep-parens/long-paragraph/unreleased/fenced-code/no-section, plus file-missing (exit 2) + no-section (exit 2). Not just the shipped harness — my independent run agrees. | | **`≤` U+2264 byte-faithfulness** | ✅ **byte-faithful end-to-end.** od'd all three: bash source (337/364/386) = `e2 89 a4`, Go source (428/444/466) = `e2 89 a4`, and `rt`'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. | | **Renderer stream routing** | ✅ faithful: `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]CheckOutcome` refactor** | ✅ behavior-preserved: existing `internal/gates` tests pass; the only external consumer (oracleshim) is adapted behavior-neutrally (`v` → `c.Verdict`, prints the same verdict). No backward-compat break because `BodyCheckResult` is internal-only — the honest-shape refactor is safe. | | **hits rune-truncation** (S-a / #572-F4) | ✅ `truncateRunes(…, 70)` truncates by rune where bash `cut -c1-70` truncates 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. | | **exit mapping** | ✅ 0 (pass/warn) / 1 (fail-loud, via `errChangelogBodyFail`) / 2 (file-missing + no-section, via reused `exitError{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). | | **Stale-comment fix** (equivalence_test + oracleshim) | ✅ correct + comment-only in effect: the old *"stdout empty both sides"* assertion was false once Gate-2's PASS chrome flows through un-redirected; the new comment scopes it correctly (this shim compares exit-only; the orchestrated stdout is #567's harness's job). The `res.Checks` loop 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 hardcodes `sentenceWarn = 25` / `paragraphWarn = 75` consts. 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's `CHANGELOG_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]CheckOutcome` refactor 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. `renderBodyCheck` is 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
bosun merged commit 571421fd22 into main 2026-07-27 13:49:59 +02:00
Sign in to join this conversation.
No description provided.