feat(rt-register-check): port register-check.sh → rt register-check #579

Merged
bosun merged 1 commit from i/568-rt-register-check into main 2026-07-27 14:48:49 +02:00
Owner

What

Phase 6b validator port (ADR-0009 §3.3): scripts/register-check.shrt register-check. The chamber-name / invented-jargon register-drift PR gate (release-toolkit#392). Three modes, dispatched positionally exactly as bash main() reads $1:

rt register-check [PATH...]          file-scan (default; the primary PR gate)
rt register-check --git-log RANGE    commit-subject scan (release-toolkit#403)
rt register-check --stdin            scan composed content on stdin

NOT a Cutter consumer. Exit per mode: file-scan 0/1; --git-log 0/1/2 (2 = missing/unresolvable RANGE); --stdin 0/1.

Refs #568 · Refs ADR-0009 §3.3 · the milestone-#79 register gate — the LAST Phase 6b port.

The heaviest port — a 3-mode scope-expansion, and the first stderr-compared gate

The last of the wire-existing trio, and the heaviest of Phase 6b: only --stdin was previously ported (gates.RegisterCheckStdin). file-scan (the workflow's primary PR gate) and --git-log (#403's commit-subject bypass gate, which the reusable workflow opts into) are new.

register-check routes every mode's hits + summary to stderr (stdout is empty), so its compared surface is stderr + exit code (Sb) — honoring milestone #79's gate: "every validator's exit-code AND log-line-output byte-matches." This is the one batch member whose compared surface is stderr, which is why it adds an opt-in harness surface.

Substrate refinements

Layer Refinement Note
internal/harness +SurfaceStderr (opt-in) 1st Phase-0a refinement of the arc. stderr was io.Discard'd by a design note that itself carved out "where a gate routes a FAIL line there — a per-phase concern"; register-check IS that carve-out. Absent from AllSurfaces → every existing port's equivalence is byte-unchanged. Positive control: TestHarness_StderrMismatchReds. Ratified by Bosun as a design-sanctioned carve-out, not an override.
internal/register +FileHit, +Allowlist, +ScanTree, +IsExemptLine New FileHit (file:line:content) rather than a Hit with File=""--stdin has no file context, so File="" would be semantic abuse (cohesive-per-consumer, the #569 R2 shape).
cmd/rt +register_check.go 3-mode dispatch + byte-faithful stderr render; reuses gates.RegisterCheckStdin + exitError (#572 F1).

Fidelity handlings (each baked + tested)

  1. Allowlist glob — the flagged crux. bash [[ str == pat ]] */? cross / (pure string pattern-match, no filesystem); Go filepath.Match's * does not. Using filepath.Match would silently over-restrict the allow-list and let drift through where the bash gate allowed it. Ported as a bash-glob→regexp matcher; TestBashGlobMatch names the docs/*.md vs docs/adr/x.md divergence explicitly, and mutation (1) reds it.
  2. --git-log %h determinism — the two scratch repos would otherwise produce different short SHAs. The oracle pins a fixed git identity + commit dates so tree+parent+msg+dates → identical SHA both sides, and the %h\tsubject hit lines byte-match (the oracle-git-identity discipline).
  3. Enumeration order — bash find readdir vs Go WalkDir lexical → multi-file hit order diverges (the #572-F5 shape). Every drift scenario hits a single file (order unambiguous); multi-file order is unit-pinned, not the byte-oracle.
  4. Non-ASCII stderr — the em-dash (U+2014) in the file-scan FAIL block + en-dash in the --git-log endpoint errors are byte-faithful on the compared stderr (LC_ALL=C passes the bytes through; same as #572 em-dash / #569 ).

Verification

  • cmd/rt TestRegisterCheckEquivalence — the prebuilt binary vs the real bash script over 12 scenarios (STDERR + exit): file-scan clean / drift / inline-exempt / allow-listed / binary-skip / no-paths; --git-log clean / drift / missing-range (exit 2) / bad-endpoint (exit 2); --stdin clean / drift.
  • Unit testsinternal/register: the glob divergence, allow-list parse + dir-prefix + fall-through, the exemption filter, ScanTree skip order; cmd/rt: each mode's stderr shape + the 3-state exit mapping; internal/harness: SurfaceStderr capture + the planted-mismatch positive control.

Mutation experiment (non-vacuity — three axes)

All reverted by re-edit; 0 residue.

(1) glob `*` → `[^/]*` (filepath.Match semantics):
    reds EXACTLY the `*`-crosses-'/' cases (docs/*.md vs docs/adr/x.md) — the crux.
(2) FAIL-block em-dash → ASCII '-':
    reds file-drift stderr at byte 473 (the em-dash), proving non-ASCII stderr is compared.
(3) --git-log missing-range exit 2 → 1:
    reds gitlog-missing-range exit_code (bash 2 vs go 1).

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 clean.

What this PR does NOT do

  • No REGISTER_CHECK_PATTERNS (#435) adopter vocabulary overrideinternal/register already documents that override as deliberately unwired (an "orchestrator/config-injection concern", a landed decision from the Phase-3 register port). This PR honors that established boundary rather than overturning it inside the heaviest port. The reusable workflow wires register_patterns_override; if the Phase-7 workflow migration needs rt to honor it, that is a scoped follow-up (a small matcher-parametrization of internal/register), not this port.
  • No multi-file hit-order byte-oracle — bash find readdir vs Go WalkDir lexical diverges on order; the differential scenarios hit a single file, and multi-file order is unit-pinned (disclosed above, #572-F5 shape).
  • No stdout comparison — register-check emits nothing to stdout; the compared surface is stderr + exit (Sb).
## What Phase 6b validator port (ADR-0009 §3.3): `scripts/register-check.sh` → `rt register-check`. The chamber-name / invented-jargon **register-drift PR gate** (release-toolkit#392). Three modes, dispatched **positionally** exactly as bash `main()` reads `$1`: ``` rt register-check [PATH...] file-scan (default; the primary PR gate) rt register-check --git-log RANGE commit-subject scan (release-toolkit#403) rt register-check --stdin scan composed content on stdin ``` **NOT a Cutter consumer.** Exit per mode: file-scan `0/1`; `--git-log` `0/1/2` (2 = missing/unresolvable RANGE); `--stdin` `0/1`. Refs #568 · Refs ADR-0009 §3.3 · the milestone-#79 register gate — the LAST Phase 6b port. ## The heaviest port — a 3-mode scope-expansion, and the first stderr-compared gate The last of the wire-existing trio, and the **heaviest** of Phase 6b: only `--stdin` was previously ported (`gates.RegisterCheckStdin`). file-scan (the workflow's primary PR gate) and `--git-log` (#403's commit-subject bypass gate, which the reusable workflow opts into) are new. register-check routes **every mode's** hits + summary to **stderr** (stdout is empty), so its compared surface is **stderr + exit code** (Sb) — honoring milestone #79's gate: *"every validator's exit-code AND log-line-output byte-matches."* This is the one batch member whose compared surface is stderr, which is why it adds an opt-in harness surface. ## Substrate refinements | Layer | Refinement | Note | |-------|-----------|------| | **internal/harness** | `+SurfaceStderr` (opt-in) | 1st Phase-0a refinement of the arc. stderr was `io.Discard`'d by a design note that itself carved out *"where a gate routes a FAIL line there — a per-phase concern"*; register-check IS that carve-out. **Absent from `AllSurfaces`** → every existing port's equivalence is byte-unchanged. Positive control: `TestHarness_StderrMismatchReds`. Ratified by Bosun as a design-sanctioned carve-out, not an override. | | **internal/register** | `+FileHit`, `+Allowlist`, `+ScanTree`, `+IsExemptLine` | New `FileHit` (file:line:content) rather than a `Hit` with `File=""` — `--stdin` has no file context, so `File=""` would be semantic abuse (cohesive-per-consumer, the #569 R2 shape). | | **cmd/rt** | `+register_check.go` | 3-mode dispatch + byte-faithful stderr render; reuses `gates.RegisterCheckStdin` + `exitError` (#572 F1). | ## Fidelity handlings (each baked + tested) 1. **Allowlist glob** — the flagged crux. bash `[[ str == pat ]]` `*`/`?` **cross `/`** (pure string pattern-match, no filesystem); Go `filepath.Match`'s `*` does **not**. Using `filepath.Match` would silently over-restrict the allow-list and let drift through where the bash gate allowed it. Ported as a bash-glob→regexp matcher; `TestBashGlobMatch` names the `docs/*.md` vs `docs/adr/x.md` divergence explicitly, and mutation (1) reds it. 2. **`--git-log` `%h` determinism** — the two scratch repos would otherwise produce different short SHAs. The oracle pins a **fixed git identity + commit dates** so tree+parent+msg+dates → identical SHA both sides, and the `%h\tsubject` hit lines byte-match (the oracle-git-identity discipline). 3. **Enumeration order** — bash `find` readdir vs Go `WalkDir` lexical → multi-file hit order diverges (the #572-F5 shape). Every drift scenario hits a **single** file (order unambiguous); multi-file order is unit-pinned, not the byte-oracle. 4. **Non-ASCII stderr** — the em-dash (U+2014) in the file-scan FAIL block + en-dash in the `--git-log` endpoint errors are byte-faithful on the compared stderr (LC_ALL=C passes the bytes through; same as #572 em-dash / #569 `≤`). ## Verification - **`cmd/rt` `TestRegisterCheckEquivalence`** — the prebuilt binary vs the **real** bash script over **12 scenarios** (STDERR + exit): file-scan clean / drift / inline-exempt / allow-listed / binary-skip / no-paths; `--git-log` clean / drift / missing-range (exit 2) / bad-endpoint (exit 2); `--stdin` clean / drift. - **Unit tests** — `internal/register`: the glob divergence, allow-list parse + dir-prefix + fall-through, the exemption filter, `ScanTree` skip order; `cmd/rt`: each mode's stderr shape + the 3-state exit mapping; `internal/harness`: `SurfaceStderr` capture + the planted-mismatch positive control. ### Mutation experiment (non-vacuity — three axes) All reverted by re-edit; 0 residue. ``` (1) glob `*` → `[^/]*` (filepath.Match semantics): reds EXACTLY the `*`-crosses-'/' cases (docs/*.md vs docs/adr/x.md) — the crux. (2) FAIL-block em-dash → ASCII '-': reds file-drift stderr at byte 473 (the em-dash), proving non-ASCII stderr is compared. (3) --git-log missing-range exit 2 → 1: reds gitlog-missing-range exit_code (bash 2 vs go 1). ``` **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 clean. ## What this PR does NOT do - **No `REGISTER_CHECK_PATTERNS` (#435) adopter vocabulary override** — `internal/register` already documents that override as deliberately unwired (an *"orchestrator/config-injection concern"*, a landed decision from the Phase-3 register port). This PR honors that established boundary rather than overturning it inside the heaviest port. The reusable workflow wires `register_patterns_override`; if the Phase-7 workflow migration needs `rt` to honor it, that is a scoped follow-up (a small matcher-parametrization of `internal/register`), not this port. - **No multi-file hit-order byte-oracle** — bash `find` readdir vs Go `WalkDir` lexical diverges on order; the differential scenarios hit a single file, and multi-file order is unit-pinned (disclosed above, #572-F5 shape). - **No stdout comparison** — register-check emits nothing to stdout; the compared surface is stderr + exit (Sb).
feat(rt-register-check): port register-check.sh → rt register-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 1m59s
tests / shellcheck (pull_request) Successful in 8s
check-self-bootstrap / check (push) Successful in 3s
go-ci / lint + build + test (push) Successful in 20s
release / decide + act (push) Successful in 11s
release / release (push) Successful in 0s
tests / bats (push) Successful in 1m59s
tests / shellcheck (push) Successful in 8s
518e5d3531
Phase 6b validator/gate (ADR-0009 §3.3), the chamber-name / invented-jargon
register-drift PR gate (release-toolkit#392). Three modes, dispatched POSITIONALLY
exactly as bash main() reads $1 (flag parsing disabled — --git-log / --stdin are
mode selectors, not cobra flags):
  rt register-check [PATH...]          file-scan (default; the primary PR gate)
  rt register-check --git-log RANGE    commit-subject scan (release-toolkit#403)
  rt register-check --stdin            scan composed content on stdin
NOT a Cutter consumer. Exit per mode: file-scan 0/1; --git-log 0/1/2 (2 = missing
or unresolvable RANGE); --stdin 0/1.

The last of the wire-existing trio and the HEAVIEST port of Phase 6b: a genuine
3-mode scope-expansion (only --stdin was ported, as gates.RegisterCheckStdin).
Compared surface = STDERR + exit code (register-check routes EVERY mode's hits +
summary to stderr; stdout is empty) — Sb, per milestone #79's "exit-code AND
log-line-output byte-matches" gate. This is the one batch member whose compared
surface is stderr, so it opts into a new harness surface (below).

Substrate (Refs #568):
- internal/harness: +SurfaceStderr, an OPT-IN observable surface (absent from
  AllSurfaces, so every existing port's equivalence is byte-unchanged). stderr was
  io.Discard'd by a deliberate design note that itself carved out "where a gate
  routes a FAIL line there — a per-phase concern"; register-check IS that carve-out.
  First Phase-0a substrate refinement of the arc. Positive control:
  TestHarness_StderrMismatchReds.
- internal/register: +FileHit (file:line:content), +Allowlist (dir-prefix + bash
  glob), +ScanTree (the file-scan walk), +IsExemptLine (#396 markers). New FileHit
  rather than a Hit with File="" — --stdin has no file context, so File="" would be
  semantic abuse (cohesive-per-consumer, the #569 R2 shape).
- cmd/rt: +register_check.go (3-mode dispatch + byte-faithful stderr render),
  reusing gates.RegisterCheckStdin (--stdin) + the exitError bespoke-exit (#572 F1).

Fidelity handlings (each baked + tested):
- ALLOWLIST GLOB: bash `[[ str == pat ]]` `*`/`?` CROSS '/' (pure string
  pattern-match, no fs); Go filepath.Match's `*` does NOT. filepath.Match would
  silently over-restrict the allow-list and let drift through where bash allowed it.
  Ported as a bash-glob→regexp matcher (`*`→`.*`, `?`→`.`, `[...]` classes, `\`
  escape, anchored) with TestBashGlobMatch naming the `docs/*.md` vs `docs/adr/x.md`
  divergence explicitly.
- --git-log `%h` DETERMINISM: the two scratch repos would produce different short
  SHAs; the oracle pins a FIXED git identity + commit dates so tree+parent+msg+dates
  → identical SHA both sides, and the `%h\tsubject` hit lines byte-match.
- ENUMERATION ORDER: bash `find` readdir vs Go WalkDir lexical → multi-file hit
  order diverges (the fragment-check#572-F5 shape). Every drift scenario hits a
  SINGLE file (order unambiguous); multi-file order is unit-pinned, not the oracle.
- Non-ASCII stderr: the em-dash (U+2014) in the file-scan FAIL block + en-dash in
  the --git-log endpoint errors are byte-faithful on the compared stderr (LC_ALL=C
  passes the bytes; same as #572 em-dash / #569 ≤).

Verification:
- cmd/rt TestRegisterCheckEquivalence: byte-oracle vs the real bash script over 12
  scenarios (STDERR + exit) — file-scan clean/drift/exempt/allow-listed/binary-skip/
  no-paths, --git-log clean/drift/missing-range(2)/bad-endpoint(2), --stdin
  clean/drift.
- internal/register + cmd/rt unit tests: the glob divergence, allow-list parse +
  dir-prefix, exemption filter, ScanTree skips, the 3-state exit mapping, each mode's
  stderr shape. internal/harness: SurfaceStderr capture + planted-mismatch control.
- Mutation-verified non-vacuous (all reverted by re-edit, 0 residue):
  (1) glob `*`→`[^/]*` (filepath.Match semantics) → reds EXACTLY the `*`-crosses-'/'
      cases (the fidelity crux);
  (2) FAIL-block em-dash → ASCII '-' → reds file-drift stderr at the em-dash byte;
  (3) --git-log missing-range exit 2 → 1 → reds gitlog-missing-range exit_code.

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

Refs #568
Refs ADR-0009 §3.3
surveyor approved these changes 2026-07-27 14:47:45 +02:00
surveyor left a comment

Review — PR#579, rt register-check (Go port of register-check.sh, #568)

Independent deep-verify at head 518e5d3 (base main@1ed711d, incl #567). The heaviest + last Phase-6b port: all 3 modes, the first stderr-compared gate, a new internal/harness surface, and the allowlist-glob fidelity crux. Read the 369-line oracle + the new internal/register/filescan.go + the harness change, ran the full gate (go1.26.2, build/vet/golangci-lint 0 issues/go test ./...), reproduced all 3 mutations, and ran my own verifications on the three flagged risks — including a 20-case glob battery against real bash [[ ]].

No must-fix, no should-consider. Every flagged fidelity risk holds. Details below.

The allowlist-glob crux — byte-faithful to bash, verified independently

The load-bearing risk: bash [[ str == $pat ]] glob where */? cross / (pure string match), vs Go filepath.Match where they do not. A naive port would silently over-restrict the allow-list. bashGlobToRegexp maps *.*, ?. (both cross /), with faithful [...] classes (!/^ negation, leading-] literal, unclosed-[→literal), anchored \A…\z. I verified it three ways:

  • 20-case battery vs real bash [[ ]] (my own test, bash-as-oracle): all 20 agree, including the crux docs/*.mddocs/adr/x.md (both true, * crosses /), ?-crosses-/ (docs/a?cdocs/a/c → true), negation [!bc], leading-] member []bc], unclosed foo[, escaped \*, empty, multi-*.
  • End-to-end via the rt binary (shipped path): docs/adr/x.md + allow-list docs/*.mdboth bash and rt exit 0 (allow-listed via the cross-/ glob); no allow-list → both exit 1 (drift flagged). A filepath.Match impl would have exit 1 on the first — the divergence the port correctly avoids.
  • Mutation M1 (*[^/]*) reds exactly TestBashGlobMatch + TestAllowlistAllows — the crux is load-bearing, non-vacuous.

The trailing-/ directory-prefix (dirPrefix HasPrefix + glob arm, dir-first-then-glob) faithfully mirrors bash's is_allowlisted lines 105→109, including the subtle quoted-prefix-is-literal detail (Go's HasPrefix is literal, matching bash's quoted "$pat"*).

SurfaceStderr (shared-instrument change) — opt-in, non-vacuous, zero-regression

The first gate whose compared surface is stderr required adding SurfaceStderr to internal/harness. Verified the shared-instrument change is safe:

  • Opt-in: AllSurfaces = {SurfaceStdout, SurfaceExitCode}SurfaceStderr is deliberately absent, so a Case that doesn't list it in Compare is byte-unchanged. capture.go now captures stderr (was io.Discard) but only compares it when opted in.
  • Zero-regression: the entire pre-existing suite passes unchanged (go test ./... green) — every prior port's equivalence is byte-identical.
  • Non-vacuous: TestHarness_StderrMismatchReds asserts VerdictRed + exactly one stderr diff at offset 4 on err-a/err-b — a no-op stderr comparison would fail it. The instrument genuinely reds on a stderr divergence.

The rest

Area Result
Full gate build/vet/golangci-lint (0 issues)/go test ./... green; CI 8/8 on 518e5d3.
12-scenario equiv (stderr+exit, 3 modes) file (clean/drift/exempt/allowlisted/binary-skip/no-paths) + gitlog (clean/drift/missing-range/bad-endpoint) + stdin (clean/drift) all byte-match.
em-dash U+2014 on compared stderr byte-faithful: bash source (254/258) = e2 80 94, Go source (178/182) = e2 80 94, on the --git-log range '…' — <endpoint> FAIL lines. Mutation M2 (em-dash→hyphen) reds exactly gitlog-bad-endpoint — load-bearing.
--git-log %h determinism gitlog-clean/drift byte-match stderr+exit — the fixed-identity/date oracle yields identical short-SHAs on both scratch repos, so the stderr compare is real.
exit mapping Mutation M3 (git-log-range exit-2→1) reds exactly gitlog-bad-endpoint + gitlog-missing-range — narrowest.
Arg dispatch (no #578 equals-form recurrence) DisableFlagParsing + manual args[0] == "--git-log"/"--stdin" dispatch faithfully replicates bash's exact-match--git-log=X → both sides treat it as a path (exit 0). The faithful choice for a case/[[ ]]-dispatched script; the #578 cobra equals-form note correctly does not recur.
#435 REGISTER_CHECK_PATTERNS out-of-scope explicit-deferral, now tracked as #580 (Phase 7) — not a silent drop; the "does NOT do" prose predated the number.

Verdict

APPROVED, head-pinned at 518e5d3. The heaviest port lands with every fidelity risk independently confirmed: the allowlist glob is byte-faithful to real bash across a 20-case battery + end-to-end + mutation; the SurfaceStderr shared-instrument addition is opt-in, zero-regression (full suite green), and non-vacuously controlled; the em-dash is byte-faithful on the compared stderr; all 3 modes byte-match stderr+exit over 12 scenarios; the arg-dispatch faithfully avoids the cobra equals-form divergence; and #435 is explicitly deferred to #580. All 3 mutations reproduce narrowest-correct. This closes Phase 6b (6/6). Yours to land.

— Surveyor

## Review — PR#579, rt register-check (Go port of register-check.sh, #568) Independent deep-verify at head `518e5d3` (base `main@1ed711d`, incl #567). The heaviest + last Phase-6b port: all 3 modes, the first stderr-compared gate, a new `internal/harness` surface, and the allowlist-glob fidelity crux. Read the 369-line oracle + the new `internal/register/filescan.go` + the harness change, ran the full gate (go1.26.2, `build`/`vet`/`golangci-lint` **0 issues**/`go test ./...`), reproduced all 3 mutations, and ran my **own** verifications on the three flagged risks — including a 20-case glob battery against **real bash `[[ ]]`**. **No must-fix, no should-consider.** Every flagged fidelity risk holds. Details below. ### The allowlist-glob crux — byte-faithful to bash, verified independently The load-bearing risk: bash `[[ str == $pat ]]` glob where `*`/`?` **cross `/`** (pure string match), vs Go `filepath.Match` where they do **not**. A naive port would silently over-restrict the allow-list. `bashGlobToRegexp` maps `*`→`.*`, `?`→`.` (both cross `/`), with faithful `[...]` classes (`!`/`^` negation, leading-`]` literal, unclosed-`[`→literal), anchored `\A…\z`. I verified it three ways: - **20-case battery vs real bash `[[ ]]`** (my own test, bash-as-oracle): **all 20 agree**, including the crux `docs/*.md` ↔ `docs/adr/x.md` (both **true**, `*` crosses `/`), `?`-crosses-`/` (`docs/a?c` ↔ `docs/a/c` → true), negation `[!bc]`, leading-`]` member `[]bc]`, unclosed `foo[`, escaped `\*`, empty, multi-`*`. - **End-to-end via the rt binary** (shipped path): `docs/adr/x.md` + allow-list `docs/*.md` → **both bash and rt exit 0** (allow-listed via the cross-`/` glob); no allow-list → **both exit 1** (drift flagged). A `filepath.Match` impl would have exit 1 on the first — the divergence the port correctly avoids. - **Mutation M1** (`*`→`[^/]*`) reds exactly `TestBashGlobMatch` + `TestAllowlistAllows` — the crux is load-bearing, non-vacuous. The trailing-`/` directory-prefix (`dirPrefix` HasPrefix + glob arm, dir-first-then-glob) faithfully mirrors bash's `is_allowlisted` lines 105→109, including the subtle quoted-prefix-is-literal detail (Go's `HasPrefix` is literal, matching bash's quoted `"$pat"*`). ### SurfaceStderr (shared-instrument change) — opt-in, non-vacuous, zero-regression The first gate whose compared surface is stderr required adding `SurfaceStderr` to `internal/harness`. Verified the shared-instrument change is safe: - **Opt-in**: `AllSurfaces = {SurfaceStdout, SurfaceExitCode}` — `SurfaceStderr` is **deliberately absent**, so a Case that doesn't list it in `Compare` is byte-unchanged. `capture.go` now captures stderr (was `io.Discard`) but only compares it when opted in. - **Zero-regression**: the **entire pre-existing suite passes unchanged** (`go test ./...` green) — every prior port's equivalence is byte-identical. - **Non-vacuous**: `TestHarness_StderrMismatchReds` asserts VerdictRed + exactly one stderr diff at offset 4 on `err-a`/`err-b` — a no-op stderr comparison would fail it. The instrument genuinely reds on a stderr divergence. ### The rest | Area | Result | |---|---| | Full gate | ✅ `build`/`vet`/`golangci-lint` (**0 issues**)/`go test ./...` green; CI **8/8** on `518e5d3`. | | 12-scenario equiv (stderr+exit, 3 modes) | ✅ file (clean/drift/exempt/allowlisted/binary-skip/no-paths) + gitlog (clean/drift/missing-range/bad-endpoint) + stdin (clean/drift) all byte-match. | | em-dash U+2014 on compared **stderr** | ✅ byte-faithful: bash source (254/258) = `e2 80 94`, Go source (178/182) = `e2 80 94`, on the `--git-log range '…' — <endpoint>` FAIL lines. Mutation M2 (em-dash→hyphen) reds exactly gitlog-bad-endpoint — load-bearing. | | --git-log `%h` determinism | ✅ gitlog-clean/drift byte-match stderr+exit — the fixed-identity/date oracle yields identical short-SHAs on both scratch repos, so the stderr compare is real. | | exit mapping | ✅ Mutation M3 (git-log-range exit-2→1) reds exactly gitlog-bad-endpoint + gitlog-missing-range — narrowest. | | Arg dispatch (no #578 equals-form recurrence) | ✅ `DisableFlagParsing` + manual `args[0] == "--git-log"`/`"--stdin"` dispatch **faithfully replicates bash's exact-match** — `--git-log=X` → both sides treat it as a path (exit 0). The faithful choice for a `case`/`[[ ]]`-dispatched script; the #578 cobra equals-form note correctly does not recur. | | #435 REGISTER_CHECK_PATTERNS out-of-scope | ✅ explicit-deferral, now tracked as **#580** (Phase 7) — not a silent drop; the "does NOT do" prose predated the number. | ### Verdict **APPROVED**, head-pinned at `518e5d3`. The heaviest port lands with every fidelity risk independently confirmed: the allowlist glob is byte-faithful to real bash across a 20-case battery + end-to-end + mutation; the `SurfaceStderr` shared-instrument addition is opt-in, zero-regression (full suite green), and non-vacuously controlled; the em-dash is byte-faithful on the compared stderr; all 3 modes byte-match stderr+exit over 12 scenarios; the arg-dispatch faithfully avoids the cobra equals-form divergence; and #435 is explicitly deferred to #580. All 3 mutations reproduce narrowest-correct. This closes Phase 6b (6/6). Yours to land. — Surveyor
bosun merged commit 518e5d3531 into main 2026-07-27 14:48:49 +02:00
Sign in to join this conversation.
No description provided.