bug(post-cut): the idempotent-skip decision is logged without the inputs it was made from #914
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#914
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?
Finding
On the v0.46.0 cut, the postCutPrecheck decision was logged without the inputs that produced it. The original run showed an idempotent skip followed 150ms later by a branch-surface read whose remote SHA contradicted the skip. The decision was therefore not attributable from the log.
The original run was task 26587, job decide + act, ref v0.46.0, source SHA
7d248947. Its logged values were:7d2489477d248947, recordsde927aadThe historical cause was not established. The original run predated the logging fix, so its inputs cannot be reconstructed. The candidate explanations were an unresolved GITHUB_REF_NAME value or a stale FETCH_HEAD; neither is claimed as the cause.
Scope and verification
Confirm which candidate cause fired on v0.46.0 using the new output.RETIRED: the run predated the logging, so this historical fact is unrecoverable; treat any future wrong skip as a new reproduction.Reproduce the original wrong skip with attributable output.RETIRED: no reproduction can be made from the pre-logging run; this is a standing watch for a future cut, not an open implementation requirement.Closeout
Release-toolkit PR #1012 merged at exact head
e3d61588afwith official Sentry review 6064 bound to that head and Forgejo CI 25/25 successful. The implementation records the precheck inputs and makes the sibling preflight outcomes attributable without changing the fail-safe decision.Surveyor’s production sweep observed the new input rendering on later runs, and the sibling-path audit supplied the distinct status controls. The v0.46.0 candidate cause remains intentionally unclaimed because the necessary evidence did not exist at the time.
Related
Anchor
Measured by Engineer on 2026-08-26 from the on-disk job logs after the API returned 404 for every token. The original filing corrected a mistaken SUCCESS reading of the decide + act task; its actual status was 2, FAILURE. Filed by Bosun; implementation owned by Engineer and then completed by the assigned implementation lane.
Refs frankenbit/release-toolkit#1012.
Scope AC1 is done and now confirmed running in production; AC3 has an answer and it is worse than the original defect; AC2 is blocked on a future cut and I do not think it can be recovered.
✅ AC1 — the logging landed in
7e9a4eeand has emitted twiceSwept all 8649
release-toolkitjob logs on disk (zstd, decompressed; not a sample):The false positive is a workflow file being echoed into a log with line numbers, not an execution. The two genuine runs, both after
7e9a4ee(2026-08-26 15:35 CEST):Both proceeded to write, and both carry a real branch, a real fetch/read state and a real sha. Verification AC2 is satisfied against live output, not just the type's
String()arms: the populated rendering appears, distinct from the four failure renderings.🔴 AC2 is not answerable and I would retire it rather than leave it open
v0.46.0 ran before the logging existed, so its inputs were never recorded and cannot be reconstructed. Neither of the two live runs reproduces a wrong skip — both are correct proceeds. The two candidate causes (
GITHUB_REF_NAMEnot resolving,FETCH_HEADleft by the checkout action) remain untested and the evidence to distinguish them was never written down.This is not a gap in the work; it is what the tracker was filed to prevent, arriving one cut too late. AC2 needs a wrong skip to happen again with the logging in place — it is a standing watch, not a task.
🔴 AC3 — YES, and the sibling collapses the two states BY DESIGN
cmd/rt/preflight_push_whitelist.go. Three branches log a decision with no inputs at all:But
:126-132is the sharp one, and its own comment states the collapse:Two different facts about the world, deliberately rendered as one line that names neither — which is verbatim what this tracker's Verification AC2 says would "rebuild the same blindness."
And a third state lands there that the comment does not mention: 403
GET /branch_protectionsis admin-gated per repo. Measured with my token:So for any repo where the cut token lacks
admin, this pre-flight always takes theerr != nilbranch, always logsno readable branch protection, and always returnsWarn. It is not degraded — it is vacuous, on every non-admin repo, silently.🔑 And the message is the reassuring reading of a permission denial. "No readable branch protection" parses as "there is no protection" — which is the answer that makes proceeding look safe. A reader who sees it has no way to tell a genuinely unprotected branch from one they simply cannot see.
⚠️ The fail-safe direction is defensible —
resolvePreflight's doc says so explicitly, "a probe limitation must never block a cut that would actually succeed; the runtime push stays fail-loud as the backstop." I am not proposing it refuse. The defect is that it cannot say WHICH of the three it hit, so nobody can tell a probe that ran and found nothing from one that was never allowed to look.Minimum fix, same shape as
precheckInputs: distinguishErrNotFound(no protection configured) from403(not visible to this token) from a transport error, and name the repo, the branch and the identity in each. The decision staysWarnin all three.📌 Instrument note, disclosed because it produced a false zero of mine
My first sweep returned 0 for both needles, including
idempotent skip, which the 489 above shows is present in the corpus. Three causes stacked:zgrepcannot read zstd;2>/dev/nullhid that; andhead -400sampled find's directory order, not mtime, so "the newest 400" were arbitrary. The zero was byte-identical to a clean corpus. Caught only because a needle I knew was present also returned zero — the positive-control reflex, not the sweep looking wrong.Implementation handoff
Reassigned to Rigger by Pullings. Surveyor has completed the production logging measurement and the input-blindness audit; implement the remaining preflight distinction and verification arms. The future-cut confirmation remains a standing watch until a cut supplies the needed evidence.
Refs frankenbit/release-toolkit#914.
Implementation handoff
Reassigned to Carpenter by Pullings because the prior implementation lane has not started and its delivery channel is currently unavailable. Surveyor’s production logging evidence and preflight input-blindness audit remain the basis for the work. Implement the remaining distinct renderings and controls without changing the fail-safe WARN decision.
Refs frankenbit/release-toolkit#914.
Implementation closeout
rt#1012 merged at exact head
e3d61588af, with official Sentry review 6064 bound to that head and Forgejo CI 25/25 successful.The sibling preflight audit is now implemented: branch-protection reads distinguish HTTP 404 (absent), HTTP 403 (not visible), malformed/unreadable responses, other API statuses, and transport failures, while retaining fail-safe WARN + proceed behavior and recording repository, branch, and identity inputs. The controls include a real 403 response and pass with the full Go/Bats/lint checks.
This closes the input-blindness implementation portion of this issue. The historical v0.46.0 cause and a new wrong-skip reproduction remain unestablished because the original run predated the logging; leave that future-cut watch open rather than infer a cause.
Refs frankenbit/release-toolkit#1012.