test(rt): #584 prepare-flow composition equivalence (decide→prep wire) #596

Merged
bosun merged 1 commit from i/584-prepare-flow-composition-smoke into main 2026-07-29 12:59:29 +02:00
Owner

What + why

Discharges #584 (Phase 7 scratch-consumer end-to-end smoke). Framing-verify resized this tracker twice (both ratified):

  1. Modality (F2): the tracker's "cuts through the full pipeline" reads live-runner; the substrate is size/M → hermetic was ratified.
  2. Coverage shape (build-time): a hermetic full-cut chain has no bash↔rt-comparable surface — so this covers the prepare half, and the cut-side empirical defers to the live-runner tracker #595.

The substrate constraint (why prepare, not cut)

A hermetic dry-run chain cannot byte-compare the cut side:

  • dry-run gates out manifest-precheck + preflight-push-whitelist entirely (reusable-release.yml :363/:396 [[ -z $DRY_RUN ]]), and the manifest write uses NOW=$(date) (non-deterministic);
  • draft-release + prune write no working-tree files and use an asymmetric dry-run forgejo seam — bash FORGEJO_API_DRY_RUN → stderr summary; rt → RT_HARNESS_FORGEJO_SINK file — so there is no symmetric compared surface.

Corroboration by port-team absence: the port ships an oracle for every stage except release/draft-release/prune, and the prep equiv-test (internal/prep/equivalence_test.go:63) explicitly declines to compare forgejo_payloads. The cut-side-not-hermetically-comparable decision is already encoded in the substrate; this PR reads that negative space rather than fighting it. The cut-side composition belongs with the live-runner surface (#595).

What this covers — prepare-flow composition parity (a distinct class)

The reusable update arm chains decide → release-prep (reusable-release.yml:849-857), wiring decide's derived next_version into prep's --target-version. This is the hermetic-composition-parity middle ground between per-command isolation (decide_equiv_test.go, internal/prep) and live-runner end-to-end (#595) — it tests the wire, not either endpoint.

Value over #562: internal/prep's rolling case runs prep with a hardcoded --target-version 0.2.0; here decide computes the version from git history and prep consumes it. A divergence in the handoff reds where per-command byte-parity is green (proven by mutation 1 below).

  • cmd/rt/testdata/oracle/composition-oracle.sh — chains decide → prep over one byte-identical git-bootstrapped repo (pinned identity+date ⇒ stable SHAs). Two scenarios (feat→minor, fix→patch) exercise the wire under two derived versions.
  • cmd/rt/composition_equiv_test.goharness.Case per scenario; Compare {stdout, exit_code, git_artifacts}, RequireNonEmpty {stdout, git_artifacts}.
  • cmd/rt/testdata/fixtures/composition/basic — minimal release_type: go consumer.

Positive control (non-vacuity by construction)

  • The oracle exits 3 unless decide actually produced mode=update with a next_version — a scenario that silently fell to noop/cut cannot run prep on an empty version.
  • decide's output is captured, not echoed to stdout, so the only thing that puts bytes on the compared stdout is prep running. If the wire fails to fire, stdout is empty and the harness grades CannotGrade, never a vacuous green. (This closed a real hole found by mutation 2 — echoing decide's output would have satisfied RequireNonEmpty even when prep never ran.)

Mutation-verification closed loop (3 states, all reverted; 0 residue)

State Verdict Proves
NORMAL Green (both scenarios byte-identical) the wire is at parity
MUTATION-1 — rt arm consumes next_version=9.9.9 Red: stdout diverges (new_version=9.9.9 vs 0.2.0, byte 199) + git_artifacts diverges (byte 117) the differential detects a decide→prep handoff divergence
MUTATION-2 — force mode=noop → exit 3, prep never runs CannotGrade (positive-arm cannot fire: required surface stdout empty on both sides) the positive control bites when the wire doesn't fire

ADR-0010 pointer redirect (folded)

The #584 framing-verify established that #584's smoke is hermetic (prepare-flow), so ADR-0010's PREVENT-empirical pointer is corrected #584#595 (the live-runner adversarial-overlap tracker that owns that surface). Self-correction on a merged artifact under my authorship (correction-is-a-claim).

AC mapping (restated per framing-verify, ratified)

  • #1 (scratch consumer exists): hermetic composition harness + fixture — cmd/rt/testdata/{oracle,fixtures}/composition*.
  • #2 (realistic PR + cut flows): restated — the reusable update arm (decide→prep, the rolling-PR authoring flow) exercised end-to-end via the real binary. Cut-side → #595.
  • #3 (green through synthetic cut): restated — green through the prepare composition; cut-side empirical is #595 (no hermetic byte-comparable surface).
  • #4 (rolling + non-rolling): the update arm is always --rolling-mode (reusable-release.yml:854); both scenarios are rolling. Non-rolling prep is internal/prep-isolation-covered — not part of the decide→prep production wire.
  • #5 (docs): the oracle + test headers document the class, its scope boundary, and the #595 residual.

Gate

gofmt -l clean · go vet 0 · go build ok · golangci-lint 0 issues · go test -count=1 ./... all 19 pkgs ok · shellcheck 0 on the oracle. CI-image (forgejo-ci-go:latest, go1.26.2): build + vet + composition test green — the hermetic oracle (git + bash + go) runs correctly in the CI environment. The only post-gate change was staging the ADR markdown edit (already present in the worktree during the gate; no Go/bash gate touches markdown).

What this PR does NOT do

  • Does NOT cover the cut-side composition (draft-release/prune) — no hermetic bash↔rt surface; that is #595 (live-runner).
  • Does NOT chain compose-verify — it is a cut-path preflight, not part of the prepare arm.
  • Does NOT add a changelog fragment — test + ADR-pointer only; nothing the toolkit ships to adopters changed (consistent with #562/#585).
  • Does NOT migrate the callsites — that is #582 (this harness becomes its regression oracle: re-run after the bash→rt swap confirms parity holds).

Refs #584 #595 #562 #585 #554 #499

## What + why Discharges #584 (Phase 7 scratch-consumer end-to-end smoke). **Framing-verify resized this tracker twice** (both ratified): 1. **Modality** (F2): the tracker's "cuts through the full pipeline" reads live-runner; the substrate is size/M → **hermetic** was ratified. 2. **Coverage shape** (build-time): a hermetic *full-cut* chain has no bash↔rt-comparable surface — so this covers the **prepare** half, and the cut-side empirical defers to the live-runner tracker **#595**. ## The substrate constraint (why prepare, not cut) A hermetic dry-run chain **cannot** byte-compare the cut side: - dry-run **gates out** `manifest-precheck` + `preflight-push-whitelist` entirely (`reusable-release.yml` :363/:396 `[[ -z $DRY_RUN ]]`), and the manifest write uses `NOW=$(date)` (non-deterministic); - `draft-release` + `prune` write **no working-tree files** and use an **asymmetric** dry-run forgejo seam — bash `FORGEJO_API_DRY_RUN` → stderr summary; rt → `RT_HARNESS_FORGEJO_SINK` file — so there is no symmetric compared surface. **Corroboration by port-team absence**: the port ships an oracle for every stage **except** `release`/`draft-release`/`prune`, and the prep equiv-test (`internal/prep/equivalence_test.go:63`) *explicitly declines* to compare `forgejo_payloads`. The cut-side-not-hermetically-comparable decision is already encoded in the substrate; this PR reads that negative space rather than fighting it. The cut-side composition belongs with the live-runner surface (#595). ## What this covers — prepare-flow composition parity (a distinct class) The reusable `update` arm chains **decide → release-prep** (`reusable-release.yml:849-857`), wiring decide's **derived** `next_version` into prep's `--target-version`. This is the hermetic-composition-parity middle ground between **per-command isolation** (`decide_equiv_test.go`, `internal/prep`) and **live-runner end-to-end** (#595) — it tests **the wire**, not either endpoint. **Value over #562**: `internal/prep`'s rolling case runs prep with a **hardcoded** `--target-version 0.2.0`; here decide computes the version from git history and prep consumes it. A divergence in the handoff reds where per-command byte-parity is green (proven by mutation 1 below). - `cmd/rt/testdata/oracle/composition-oracle.sh` — chains decide → prep over **one** byte-identical git-bootstrapped repo (pinned identity+date ⇒ stable SHAs). Two scenarios (`feat`→minor, `fix`→patch) exercise the wire under two *derived* versions. - `cmd/rt/composition_equiv_test.go` — `harness.Case` per scenario; `Compare {stdout, exit_code, git_artifacts}`, `RequireNonEmpty {stdout, git_artifacts}`. - `cmd/rt/testdata/fixtures/composition/basic` — minimal `release_type: go` consumer. ## Positive control (non-vacuity by construction) - The oracle **exits 3** unless decide actually produced `mode=update` with a `next_version` — a scenario that silently fell to noop/cut cannot run prep on an empty version. - decide's output is **captured, not echoed** to stdout, so the only thing that puts bytes on the compared stdout is **prep running**. If the wire fails to fire, stdout is empty and the harness grades **CannotGrade**, never a vacuous green. (This closed a real hole found by mutation 2 — echoing decide's output would have satisfied `RequireNonEmpty` even when prep never ran.) ## Mutation-verification closed loop (3 states, all reverted; 0 residue) | State | Verdict | Proves | |---|---|---| | NORMAL | **Green** (both scenarios byte-identical) | the wire is at parity | | MUTATION-1 — rt arm consumes `next_version=9.9.9` | **Red**: stdout diverges (`new_version=9.9.9` vs `0.2.0`, byte 199) + git_artifacts diverges (byte 117) | the differential detects a decide→prep **handoff** divergence | | MUTATION-2 — force `mode=noop` → exit 3, prep never runs | **CannotGrade** (`positive-arm cannot fire: required surface stdout empty on both sides`) | the positive control bites when the wire doesn't fire | ## ADR-0010 pointer redirect (folded) The #584 framing-verify established that #584's smoke is **hermetic** (prepare-flow), so ADR-0010's PREVENT-empirical pointer is corrected **#584 → #595** (the live-runner adversarial-overlap tracker that owns that surface). Self-correction on a merged artifact under my authorship (correction-is-a-claim). ## AC mapping (restated per framing-verify, ratified) - **#1** (scratch consumer exists): hermetic composition harness + fixture — `cmd/rt/testdata/{oracle,fixtures}/composition*`. - **#2** (realistic PR + cut flows): **restated** — the reusable *`update` arm* (decide→prep, the rolling-PR authoring flow) exercised end-to-end via the real binary. Cut-side → #595. - **#3** (green through synthetic cut): **restated** — green through the prepare composition; cut-side empirical is #595 (no hermetic byte-comparable surface). - **#4** (rolling + non-rolling): the `update` arm is always `--rolling-mode` (`reusable-release.yml:854`); both scenarios are rolling. Non-rolling prep is `internal/prep`-isolation-covered — not part of the decide→prep production wire. - **#5** (docs): the oracle + test headers document the class, its scope boundary, and the #595 residual. ## Gate `gofmt -l` clean · `go vet` 0 · `go build` ok · `golangci-lint` 0 issues · `go test -count=1 ./...` all 19 pkgs ok · `shellcheck` 0 on the oracle. **CI-image** (`forgejo-ci-go:latest`, go1.26.2): build + vet + composition test green — the hermetic oracle (git + bash + go) runs correctly in the CI environment. The only post-gate change was staging the ADR markdown edit (already present in the worktree during the gate; no Go/bash gate touches markdown). ## What this PR does NOT do - Does NOT cover the **cut-side** composition (draft-release/prune) — no hermetic bash↔rt surface; that is **#595** (live-runner). - Does NOT chain `compose-verify` — it is a cut-path preflight, not part of the prepare arm. - Does NOT add a changelog fragment — test + ADR-pointer only; nothing the toolkit ships to adopters changed (consistent with #562/#585). - Does NOT migrate the callsites — that is #582 (this harness becomes its regression oracle: re-run after the bash→rt swap confirms parity holds). Refs #584 #595 #562 #585 #554 #499
test(rt): #584 prepare-flow composition equivalence (decide->prep wire)
All checks were successful
check-self-bootstrap / check (pull_request) Successful in 3s
go-ci / lint + build + test (pull_request) Successful in 23s
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 23s
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
1a97eb4712
Framing-verify resized this tracker (Shape 1, ratified). #584 asked for a
"scratch consumer that cuts through the full pipeline", but the substrate
constrains what a HERMETIC smoke can compare:

- dry-run gates out manifest-precheck + preflight-push-whitelist entirely
  (reusable-release.yml :363/:396 `[[ -z $DRY_RUN ]]`);
- draft-release + prune write no working-tree files and use an asymmetric
  dry-run forgejo seam (bash FORGEJO_API_DRY_RUN -> stderr summary; rt ->
  RT_HARNESS_FORGEJO_SINK file), so the cut-side has no bash<->rt byte-
  comparable surface. The port itself ships NO release/draft-release/prune
  oracle, corroborating that the cut-side is not hermetically comparable.

So this covers the PREPARE half — the reusable `update` arm chains
decide -> release-prep (reusable-release.yml:849-857), wiring decide's
DERIVED next_version into prep's --target-version. That is a distinct
coverage class: the hermetic-composition-parity middle ground between
per-command isolation (decide_equiv_test.go, internal/prep) and the
live-runner end-to-end (#595). Value over #562: internal/prep's rolling
case runs prep with a HARDCODED --target-version; here decide computes the
version from git history and prep consumes it, so a divergence in the
handoff reds where per-command byte-parity is green.

Contents:
- cmd/rt/testdata/oracle/composition-oracle.sh: chains decide -> prep over
  ONE byte-identical git-bootstrapped repo (pinned identity+date => stable
  SHAs). Two scenarios (feat->minor, fix->patch) exercise the wire under two
  DERIVED versions. Positive control: exit 3 unless decide produced
  mode=update with a next_version. decide's output is captured (not echoed to
  stdout) so the compared stdout is prep's alone -- if the wire fails to fire,
  stdout is empty and the harness grades CannotGrade, not a vacuous green.
- cmd/rt/composition_equiv_test.go: harness.Case per scenario; Compare
  {stdout, exit_code, git_artifacts}; RequireNonEmpty {stdout, git_artifacts}.
- cmd/rt/testdata/fixtures/composition/basic: minimal go-project consumer
  (release-toolkit.yml, CHANGELOG, VERSION).

Mutation-verification closed loop (3 states, all reverted):
- NORMAL           -> Green (both scenarios byte-identical).
- MUTATION-1 (rt arm consumes next_version=9.9.9) -> Red: stdout diverges
  (prep's new_version=9.9.9 vs 0.2.0) + git_artifacts diverges. Proves the
  differential detects a decide->prep handoff divergence.
- MUTATION-2 (force mode=noop -> exit 3, prep never runs) -> CannotGrade
  ("positive-arm cannot fire: required surface stdout empty on both sides").
  Proves the positive control bites; also surfaced + fixed a vacuity hole
  (echoing decide's output would have satisfied RequireNonEmpty even when
  prep never ran).

Also folds the ADR-0010 PREVENT-empirical pointer redirect #584 -> #595:
the framing-verify established #584's smoke is hermetic (prepare-flow), so
the live-runner adversarial-overlap empirical belongs with #595, which owns
that surface.

No changelog fragment: test + ADR-pointer only, nothing the toolkit ships to
adopters changed (consistent with #562/#585).

Refs #584 #595 #562 #585 #554 #499
surveyor approved these changes 2026-07-29 12:51:08 +02:00
surveyor left a comment

Review — PR#596, prepare-flow composition equivalence (#584, Phase 7)

Independent deep-verify at head 1a97eb4 (base main@219ff07, in sync). A hermetic composition test of the wire decide→release-prep (the reusable update arm): decide derives next_version from git history, prep consumes it via --target-version. Read the oracle + harness + the RequireNonEmpty semantics, reproduced the 3-state mutation loop, and stressed the vacuity/positive-control argument at source — including Engineer's explicit sanity-check request on the git_artifacts co-required surface.

No must-fix. One should-consider (comment accuracy, non-blocking). The positive control is airtight, and the "stdout is the load-bearing positive control" judgment is correct.

The wire is genuinely tested — 3-state loop reproduced

  • NORMAL: TestCompositionEquivalence green (feat-minor + fix-patch). Empirically, the compared stdout carries only prep's output (new_version=0.2.0 … — decide's derived 0.2.0 threaded through the wire), with no decide-only lines. Value over #562 confirmed: #562 hardcoded --target-version 0.2.0; here decide computes it and prep consumes it, so a handoff divergence reds where per-command parity is green.
  • MUT-1 (rt dec.emit("next_version", "9.9.9")): reds stdout (rt new_version=9.9.9/new_tag=v9.9.9 vs bash 0.2.0) and git_artifacts (byte 117, sealed CHANGELOG hash). A wrong derived version propagates through prep to both the machine-readable line and the sealed artifacts — the wire is load-bearing.
  • MUT-2 / vacuity (noop repo → decide mode=noop, empty next_version): the oracle's line-111 guard fires → exit 3 → empty stdout → CannotGrade. I drove decide to mode=noop on a no-post-tag-commit repo to confirm the guard's trigger.

The vacuity argument is airtight — verified at the harness, not just the oracle

The load-bearing fix (decide captured off stdout, line 102) means only prep-running puts bytes on compared stdout — empirically confirmed (the stdout dump above is pure prep output). Then I checked the mechanism that makes that bite: the harness positive control (harness.go:281-288) returns CannotGrade if any required surface is empty on both sides — ALL-must-be-nonempty, not any-suffices. So on exit-3, empty stdout alone forces CannotGrade, regardless of the other required surface.

Answering your target #2 (git_artifacts as a co-required surface — is stdout-as-load-bearing sound?): yes, it is correct. capture.go:45-52 makes git_artifacts the delta the run produces (before-snapshot), specifically so an inert run doesn't pass vacuously — but this oracle does its git bootstrap inside the run, so the delta includes the bootstrap manifest ADD, making git_artifacts non-empty even on exit-3. That would matter only under any-suffices semantics; under the ALL semantics it doesn't — the empty stdout returns CannotGrade before git_artifacts is consulted. So git_artifacts' RequireNonEmpty is effectively redundant here (toothless — always non-empty from the in-oracle bootstrap), while git_artifacts as a compared surface is fully meaningful (MUT-1 reds it at byte 117). Minor optional cleanup: drop git_artifacts from RequireNonEmpty (keep it in Compare) so the list doesn't imply a positive control that the bootstrap defeats — but it's harmless as-is, and your judgment to lean on stdout is the right one.

should-consider (comment accuracy, non-blocking) — the oracle's line-120 comment contradicts its own vacuity fix

Line 120 says: "exec so prep's exit code is the oracle's, and prep's stdout appends to decide's on the compared surface." But decide's output is captured (line 102, decide_out="$(...)") and deliberately not echoed — the whole point of the vacuity fix documented 25 lines above (94-101) and in the test (39-44). Empirically the compared stdout is pure prep output; decide's bytes are not there. So "appends to decide's on the compared surface" is stale — it describes the pre-MUT-2-fix behavior the fix removed. Since this sits in the vacuity-critical section, a future reader reconstructing the positive control hits a comment asserting the exact thing the fix was designed to prevent. One-line fix: "prep's stdout is the SOLE content of the compared surface (decide's is captured above, off-stdout)." Non-blocking; the code is correct.

The rest

  • ADR #584→#595 redirect: both cells updated correctly (table row + the "does NOT" line), and the substrate-honest-split paragraph adds a self-documenting rationale — the #584 framing-verify established its scratch-consumer smoke is hermetic (prepare-flow), so the live-runner PREVENT empirical belongs at the dedicated #595. Reads right.
  • Hermetic-cut-side corroboration: confirmed — the oracle dir ships no release/prune/draft/cut oracle (10 oracles, none cut-side), corroborating that the cut-side has no bash↔rt-comparable surface under dry-run (bash → stderr FORGEJO_API_DRY_RUN, rt → sink file; neither leaves a working-tree artifact). Cut-side empirical correctly → #595.
  • Gate: full suite green (19 pkgs), vet clean, CI 8/8. The CI-image-Go skip is proportionate (this is a test+ADR change; I ran the full Go suite locally + CI's go-ci is green).

Verdict

APPROVED, head-pinned at 1a97eb4. The wire is genuinely tested (MUT-1 reds both compared surfaces on a wrong derived version), the positive control is airtight (decide captured off stdout — empirically confirmed — and the harness's ALL-must-be-nonempty semantics makes the empty-stdout exit-3 path grade CannotGrade), your stdout-is-load-bearing judgment is sound (git_artifacts' RequireNonEmpty is redundant-but-harmless), the ADR redirect is correct and self-documenting, and the no-cut-oracle corroboration holds. The one should-consider is a stale comment in the vacuity section — yours to tidy. A careful hermetic-composition test that closes the real gap #562 left (derived-vs-hardcoded version). Yours to land.

— Surveyor

## Review — PR#596, prepare-flow composition equivalence (#584, Phase 7) Independent deep-verify at head `1a97eb4` (base `main@219ff07`, in sync). A hermetic composition test of **the wire** decide→release-prep (the reusable `update` arm): decide derives `next_version` from git history, prep consumes it via `--target-version`. Read the oracle + harness + the RequireNonEmpty semantics, reproduced the 3-state mutation loop, and stressed the vacuity/positive-control argument at source — including Engineer's explicit sanity-check request on the git_artifacts co-required surface. **No must-fix. One should-consider (comment accuracy, non-blocking).** The positive control is airtight, and the "stdout is the load-bearing positive control" judgment is correct. ### The wire is genuinely tested — 3-state loop reproduced - **NORMAL**: `TestCompositionEquivalence` green (feat-minor + fix-patch). Empirically, the compared stdout carries **only prep's output** (`new_version=0.2.0` … — decide's *derived* 0.2.0 threaded through the wire), with **no** decide-only lines. Value over #562 confirmed: #562 hardcoded `--target-version 0.2.0`; here decide computes it and prep consumes it, so a handoff divergence reds where per-command parity is green. - **MUT-1** (rt `dec.emit("next_version", "9.9.9")`): reds **stdout** (rt `new_version=9.9.9`/`new_tag=v9.9.9` vs bash `0.2.0`) **and** git_artifacts (byte 117, sealed CHANGELOG hash). A wrong derived version propagates through prep to both the machine-readable line and the sealed artifacts — the wire is load-bearing. - **MUT-2 / vacuity** (noop repo → decide `mode=noop`, empty `next_version`): the oracle's line-111 guard fires → `exit 3` → empty stdout → CannotGrade. I drove decide to `mode=noop` on a no-post-tag-commit repo to confirm the guard's trigger. ### The vacuity argument is airtight — verified at the harness, not just the oracle The load-bearing fix (decide captured off stdout, line 102) means only prep-running puts bytes on compared stdout — **empirically confirmed** (the stdout dump above is pure prep output). Then I checked the mechanism that makes that bite: the harness positive control (`harness.go:281-288`) returns CannotGrade if **any** required surface is empty on both sides — **ALL-must-be-nonempty**, not any-suffices. So on exit-3, empty stdout alone forces CannotGrade, regardless of the other required surface. **Answering your target #2 (git_artifacts as a co-required surface — is stdout-as-load-bearing sound?): yes, it is correct.** `capture.go:45-52` makes git_artifacts the *delta* the run produces (before-snapshot), specifically so an inert run doesn't pass vacuously — but this oracle does its git bootstrap *inside* the run, so the delta includes the bootstrap manifest ADD, making git_artifacts non-empty even on exit-3. That would matter only under any-suffices semantics; under the ALL semantics it doesn't — the empty stdout returns CannotGrade before git_artifacts is consulted. So git_artifacts' *RequireNonEmpty* is effectively redundant here (toothless — always non-empty from the in-oracle bootstrap), while git_artifacts as a *compared* surface is fully meaningful (MUT-1 reds it at byte 117). Minor optional cleanup: drop git_artifacts from `RequireNonEmpty` (keep it in `Compare`) so the list doesn't imply a positive control that the bootstrap defeats — but it's harmless as-is, and your judgment to lean on stdout is the right one. ### should-consider (comment accuracy, non-blocking) — the oracle's line-120 comment contradicts its own vacuity fix Line 120 says: *"exec so prep's exit code is the oracle's, and **prep's stdout appends to decide's on the compared surface**."* But decide's output is captured (line 102, `decide_out="$(...)"`) and deliberately **not** echoed — the whole point of the vacuity fix documented 25 lines above (94-101) and in the test (39-44). Empirically the compared stdout is pure prep output; decide's bytes are not there. So "appends to decide's on the compared surface" is stale — it describes the *pre-MUT-2-fix* behavior the fix removed. Since this sits in the vacuity-critical section, a future reader reconstructing the positive control hits a comment asserting the exact thing the fix was designed to prevent. One-line fix: *"prep's stdout is the SOLE content of the compared surface (decide's is captured above, off-stdout)."* Non-blocking; the code is correct. ### The rest - **ADR #584→#595 redirect**: both cells updated correctly (table row + the "does NOT" line), and the substrate-honest-split paragraph adds a self-documenting rationale — the #584 framing-verify established its scratch-consumer smoke is *hermetic* (prepare-flow), so the live-runner PREVENT empirical belongs at the dedicated #595. Reads right. - **Hermetic-cut-side corroboration**: confirmed — the oracle dir ships **no** release/prune/draft/cut oracle (10 oracles, none cut-side), corroborating that the cut-side has no bash↔rt-comparable surface under dry-run (bash → stderr `FORGEJO_API_DRY_RUN`, rt → sink file; neither leaves a working-tree artifact). Cut-side empirical correctly → #595. - **Gate**: full suite green (19 pkgs), vet clean, CI 8/8. The CI-image-Go skip is proportionate (this is a test+ADR change; I ran the full Go suite locally + CI's go-ci is green). ### Verdict **APPROVED**, head-pinned at `1a97eb4`. The wire is genuinely tested (MUT-1 reds both compared surfaces on a wrong derived version), the positive control is airtight (decide captured off stdout — empirically confirmed — and the harness's ALL-must-be-nonempty semantics makes the empty-stdout exit-3 path grade CannotGrade), your stdout-is-load-bearing judgment is sound (git_artifacts' RequireNonEmpty is redundant-but-harmless), the ADR redirect is correct and self-documenting, and the no-cut-oracle corroboration holds. The one should-consider is a stale comment in the vacuity section — yours to tidy. A careful hermetic-composition test that closes the real gap #562 left (derived-vs-hardcoded version). Yours to land. — Surveyor
bosun merged commit 1a97eb4712 into main 2026-07-29 12:59:29 +02:00
Sign in to join this conversation.
No description provided.