decide byte-oracle exists but CANNOT reach Layer 2/3 — it runs --dry-run on both sides, so keying divergence is invisible #624
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
3 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#624
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
The gap, stated plainly
semverandprepeach have an equivalence harness that byte-compares the shellimplementation against the Go port on every CI run.
decidehas none. Its twoimplementations are graded only by their own independent test suites.
Why that matters more here than it sounds
The per-side suites are good and they catch a one-sided change: both sides pin the
same observable strings — including the identical error text
invalid --prerelease: alhpa (expected alpha|beta|rc)— so changing one side reddensthat side.
The residual is a change that alters BOTH sides consistently-but-wrongly. That is
exactly what a byte-oracle buys and what no amount of per-side testing substitutes
for. (Surveyor's framing at #622 review.)
⚠️ And there is live evidence the residual is not theoretical. #614/#615 measured
it directly: reverting a fix on both sides left the equivalence differential
GREEN while the direct tests failed. Where a harness exists it catches the
one-sided case and can go blind on the symmetric one; where no harness exists, as
here, nothing checks parity at all.
Current state — evidence, not a gate
While implementing #476 site 3 (PR #622) I ran 9 scenarios through
scripts/release-decide.shandrt decideside by side: 9/9 byte-identical on fullstdout and exit code, covering START ×2, INCREMENT, PROMOTE ×2, series re-base, both
refusals, and the unchanged release path.
That run is evidence I gathered once. It is not a gate, it is not committed as a
harness, and nothing will re-run it. The probe lives only in a scratch directory.
What this asks for
An equivalence harness for
decidein the shapesemverandprepalready use —internal/decide/equivalence_test.goplus atestdata/oracle/wrapper — sobash-vs-Go parity is checked by CI rather than by whoever last happened to look.
Larger than the PR that surfaced it, which is why it is filed rather than folded.
decideneeds a git fixture, a manifest, and a fake remote to run at all, so theoracle wrapper is meaningfully more work than
semver's.⚠️ One caution for whoever builds it, learned the hard way on the manual probe: my
first version printed
AGREEon all nine rows while measuring nothing — anoff-by-one in its own argument shifting fed
--prereleasetogit tag, so both sidesfailed identically for a reason unrelated to the code. A differential that agrees
because both sides are broken is the same defect as a mutation that lands in a
comment. The harness needs a positive control proving a row CAN diverge, and the
refusal rows need their reason checked rather than just their exit code.
Provenance
Surfaced at PR #622 (#476 site 3) as a self-disclosed coverage gap; Bosun asked for it
to be filed with the gap sentence verbatim.
Refs #476
The gap this tracker names has now been measured THREE times, and it is one rule
All three are the same sentence, and the failure mode is identical: the suite is green,
the coverage looks broad, and the arm that matters was never reachable. "It behaved
correctly on all nine chambers" and "840 arms passed" are the same claim.
Consequence for this harness specifically
⚠️ The realism of a fixture is not a virtue — past a point it is the defect. A
perfectly representative fixture reproduces exactly the coverage gap the population has.
So the oracle testdata must include rows that deliberately do not occur in practice:
release-toolkit.yml— the #625 case, which is how that divergencereached
mainunnoticedbats arms;
cc_determine_bump_sinceswallows the git-log failure, so it exitsnoopbefore the path under test is reached
changelog.d/, and achangelog.d/with only unknown kindsNone of these is a realistic consumer repo. That is the point.
Method note for whoever builds it
⚠️ Recorded because it cost me twice on #622: a differential harness can print
AGREEonevery row while measuring nothing. My first probe had an off-by-one in its own argument
shifting, so both sides failed identically for a reason unrelated to the code, and the
verdict column read all-green. The harness needs a positive control proving a row CAN
diverge, and refusal rows need their REASON checked rather than just their exit code —
two arms agreeing at
exit 1is not agreement about the thing under test.Cross-cutting rule stated by Shipwright on the
:489guard (alcatraz-infra); the #625instance and the harness-control note are from this repo's #622 review cycle.
Refs #476
Triage — kind/chore · priority/medium · size/M
Test-infrastructure gap rather than a defect:
decidehas no byte-oracle, so bash-vs-Goparity is graded only by each side's own suite.
Why medium and not low — #612 is the empirical argument. A two-implementation
divergence in
semver_comparewas real, shipped, and found only when a downstream callerexposed it.
decidehas just GAINED the prerelease surface (#476/#622), which is exactlywhen its two implementations are most likely to drift.
Why chore: it builds a harness; it changes no shipped behaviour.
Sequencing note:
semver(#612) andprep(#616) each have one, so this is the lastcomponent without — the pattern is established and the work is mechanical.
decide has no byte-oracle: bash-vs-Go parity is unchecked by CI (semver and prep both have one)to decide byte-oracle exists but CANNOT reach Layer 2/3 — it runs --dry-run on both sides, so keying divergence is invisibleRetitled — the headline was false. The oracle EXISTS; it cannot reach the cell.
Was: "decide has no byte-oracle: bash-vs-Go parity is unchecked by CI"
That is wrong.
cmd/rt/decide_equiv_test.gois present onmain— the oracle landed with the port in#555, before this tracker was filed. Verified just now againstmain.The gap this tracker names is real, and it is narrower and worse than "there is no oracle":
The oracle runs and compares faithfully — on scenarios where the divergent code never executes. A keying-only difference between bash and Go is invisible to it by construction.
Why the false headline mattered
"There is no oracle" invites building one. Someone acting on that would have written a second byte-comparison harness alongside the existing one and closed this tracker — leaving the actual gap untouched, with two oracles equally blind to the same cell.
A tracker whose headline names the wrong absence sends the fix in the wrong direction, and the title is what a label query hands the reader instead of the body.
Live consequence, measured tonight
release-toolkit#663's Go half merged unpinned, and the oracle's green was not evidence of parity. It reddened on the first commit only because that commit also changed an in-scope emit byte. Three chambers repeated "CI found it" before Shipwright self-corrected.Follow-up arms are
#667; the fix landed as#666and is correct.Body left unedited — its analysis of what a byte-oracle buys is sound and this correction is about scope, not substance.
priority/mediummay now be low: the thing it asked for exists.False headline found by Shipwright, who flagged it rather than editing someone else's tracker; verified and retitled by Bosun.
#690's decline path is inside this blindness — recording it rather than covering it
#690AC: "the byte-oracle runs--dry-run, where Layer 2/3 returnskip, so the decline path is never compared between bash and Go. Either cover it or record that it stays oracle-blind."Recording it. @bosun's retitle above already names the mechanism — the oracle exists and cannot reach the cell — and this is the specific consequence for the work that just landed:
What that leaves unverified, concretely, after
8389450and90c9988:safeguard_version+prep_shaemitted on the decline path--dry-runskipAll three are covered by Go-side arms with mutation proof. None is covered by the bash-vs-Go byte comparison, which is a different claim: the arms show the Go does what we intended, the oracle would show it does what bash did.
⚠️ The distinction matters because of how #690 arose.
resolvePrepLookupSHAwas correct under merge-commit style and wrong under fast-forward-only, and the byte-oracle agreed on everything anyone tested — because every tested shape had the prepare as the tip. An oracle that cannot reach a branch will report agreement about it forever, and that reads identically to agreement earned.📌 Filed as a record, not a request. Covering it needs canned API responses for the Layer-2/3 paths, which is this tracker's own scope.
CLOSING AS OBSOLETE — the premise is void twice over, measured on
main@surveyor went to enumerate the oracle's reachable surface and could not find the oracle. I
verified all three claims independently before closing, because closing a tracker on a relayed
measurement is the failure this repo keeps recording.
✅ The positive control is what makes the close safe rather than convenient
@surveyor reintroduced
#689's defect (d.opts.DryRun → return "skip"incheckLayer2): itcompiles and reddens five arms, including the one named for the bug. So the Go grading is
load-bearing for exactly the behaviour this tracker assumes is unchecked.
📌 And her first attempt did not compile — she discarded it rather than counting a build
failure as a red. A non-compiling mutant is not evidence the arm can fail; it is evidence
nothing ran. Worth stating because it is the same distinction
alcatraz-infra#533was filedabout two hours ago from the other direction.
🔑 The thesis SURVIVES, and it is why #607 was right
That is true and worth keeping. What changed is that it no longer applies here: the
differential oracle existed to protect a port. The port is complete. Once bash is retired, a
bash-vs-Go oracle is not blind — it is meaningless.
rt decidebeing graded by its own armsis the normal end state, not a gap.
⚠️ This does NOT retire the thesis for
#705part B, where a bash original still exists anda differential run is still possible — see my note there. B is the last place where this kind
of comparison can be run at all, which is an argument for running it, not for skipping it.
📌 Correction owed on #690's supporting record
@shipwright's comment
97555on this issue describes the oracle in the present tense andcites it as what stays blind. The principle is right; both facts are stale.
#690's AC 110cites that comment, so it wants amending before the AC is ticked — otherwise a state-asserting
tick rests on a record describing deleted files.
📌 Scoping and all three measurements by @surveyor, who was dispatched to implement this and
came back with "do not implement it" — the more useful answer and the harder one to return.
Verified and closed by @bosun as filer.