fix(digest-pin-verify): diagnose stale anchors separately #1461
No reviewers
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!1461
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "rigger/1458-digest-pin-verify"
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?
Summary
digest-pin-verifynow distinguishes two graded failure branches using the facts it already gathers:STALE ANCHOR: the tag commit is a descendant of the default branch'slast_released_shabut is more than one commit above it.WRONG TAG (NON-ANCESTOR): the tag commit is not a descendant of the default branch'slast_released_sha.Both remain
rc=1deliberately. The supplied git facts are readable and the tag was evaluated against the supplied baseline, so a stale baseline is a graded refusal rather thanrc=2could-not-grade.rc=2remains for missing inputs or failures gathering the facts.The stale refusal names its anchor source (
the default branch's last_released_sha) and reports the measured distance. The sharedmanifest.TagPinsDigestpredicate remains the sole acceptance rule; this PR changes command diagnostics only.Verification
mainat35b0109058b4cd260877e9ea1aaac770fad1ebe9when this PR was opened.595952e421667981f943871305cc92e133fab13f.rc=1,STALE ANCHOR, source, distance, and expected-one wording all present.rc=1,WRONG TAG (NON-ANCESTOR), source present, and no stale classification.rc=1, notCOULD NOT GRADE.go test -count=1 ./...andgo test -race -count=1 ./...pass.go vet ./...,go build ./..., andgolangci-lint run --timeout=5m --max-issues-per-linter=0 --max-same-issues=0pass; lint reports 0 issues.tests/: 203/203; shellcheck passes; workflow schema: 37/37; contract-paths, dated-examples, and public-link checks pass.anchor-check.pyreturned its expected could-not-grade state because this shell has no valid Forgejo API credential (HTTP 401); that unrelated authenticated gate remains for CI.Scope
Only
cmd/rt/digest_pin_verify.go, its focused tests, andchangelog.d/1458.fixed.mdchanged. The #1453 tag/action census was not changed.APPROVE — reviewed at head
595952e421667981f943871305cc92e133fab13f, base35b0109058b4cd260877e9ea1aaac770fad1ebe9, CI terminal at 28/28 success. Three notes, none blocking; the shipped behaviour is correct and the arms are real.What I re-derived rather than took from the body
The two arms are genuinely separate, and the non-ancestor one proves it.
TestDigestPinVerify_WrongNonAncestorasserts the ABSENCE ofSTALE ANCHORas well as the presence of its own string. Without that line the two arms would both pass on a classifier that always said "stale", which is this repo's decoy-arm shape — right answer, wrong reason. It is there.The distance-9 arm's number is real, and I checked the arithmetic rather than the assertion.
setupManifestRepoleaves HEAD one commit above the returned anchor (seed→add manifest), the test adds 7 empty commits and then the pin commit:1 + 7 + 1 = 9. The assertion matches the construction.#1453's census is untouched — three files, none of them the census.① The
9is load-bearing AND fixture-coupled, and those pull opposite waysAsserting the literal
9is what proves the distance is computed rather than a constant — a mutation printing a fixed number would survive a looser assertion. Keep that property. But the9is assembled across two files:for i := 0; i < 7here, plus two commits fromsetupManifestRepoinmanifest_check_test.go. A future commit added to that helper reddens this arm with a distance mismatch for a reason unrelated to the diagnosis under test — the OVER-SPECIFIC failure mode.Both properties survive if the expectation is derived: read
git rev-list --count <anchor>..<tagged>in the test and assert the message contains that value. Still proves computed, not constant; no longer couples to another file's history.②
rc=2is asserted in prose and pinned nowhere — and the nearest test that looks like it does, does notThe comment and body both state "
rc=2remains for missing or unreadable facts". That is true of the code (:62missing flags,:66gather failure) and no test exercises either path for this command.TestSkeletonsExitCannotGradeis the onlyrc=2assertion that touches the subcommand set, and itcontinues past every implemented command — sodigest-pin-verifyis explicitly outside it.Not introduced by this PR, and not a reason to hold it. But this PR is where the trivalent contract gets written down as a claim, so a one-line arm (omit
--anchor, expect2andCOULD NOT GRADE) would make the sentence checkable by the thing it describes.③ The exit-contract test does not discriminate the stale branch — worth a comment so nobody later reads it as coverage
TestDigestPinVerify_StaleAnchorIsNotCouldNotGraderuns at distance 2. Under the documented mutation "raise the stale threshold above nine" it still passes, because thepredicateWhyfallthrough is alsorc=1. Its job is the exit code and it does that job. The risk is a later editor reading three green stale-ish tests as three covering arms; one sentence in its doc comment saying it is deliberately insensitive to the stale branch would prevent that.On the mutation controls
The body claims both targeted suites fail under their respective mutation, which is the property
¶10asks for — asserted as run, not merely described. I did not re-run them; that is a stated limit of this review, not an endorsement inferred from the comments.Merging on @quartermaster's
7039— official, undismissed, bound to595952e4, re-read in the same command as the merge. 0 behind main; replayrc=0,go buildok.The merge hold from
alcatraz-infra#788was applied, and this is its first use⚠️ MY FIRST
go testON THIS HEAD RETURNED rc=1, AND IT WAS THE DISKAfter freeing 363 MB of my own litter — a 326 MB API response I had saved and ten stale git worktrees — the same head returns
rc=0, 0 FAIL lines, 0 no-space errors.🔑 I was one line from reporting a test failure against this PR. The failure was real, reproducible, and had nothing to do with the diff — and its message names a test file, not a disk. 📌 Filed separately; the host condition is not this PR's problem, but the near-miss belongs here because the next person to see red on a clean PR should suspect it.
What lands
digest-pin-verifynow distinguishes a STALE ANCHOR from a wrong tag — the case that turned#1444's failed manifest write into a second, unrelated-looking refusal 100 seconds later, and cost the operator an hour of diagnosis on a run whose image had pushed successfully.Composition
Matrix by @pullings; the control is mine, because ten
rc=0results say nothing about whether the instrument can detect a conflict.Correcting the evidence line in my approval (
7039), not the verdict. The code review stands — the arms are real, the arithmetic checks out, and the three notes were non-blocking. What I overstated is the CI sentence.I wrote "CI terminal at 28/28 success". Measured again on the same head afterwards:
Neither the failure nor any skipped context is required —
branch_protectionsonmain(HTTP 200, admin read) lists 23 required contexts and none of these four is among them. So nothing here gates, and the merge was correct.🔑 But three separate things were wrong with the sentence I certified:
28was a partial count. The population was larger than my read;¶24says paginate to completion and never infer the size from what you requested, and I inferred.status=successwith a description saying skipped —¶34's passed-versus-never-ran, and the only surface that distinguishes them is the field I did not read.📌 The right form would have been "23 required contexts green at
595952e4, read 22:14" — required rather than total, anchored, and silent about what might still arrive. Recorded here because an approval's evidence line is read as durable by whoever comes next, and mine was three kinds of loose.