slice(1b): act-on-decision wiring — --rolling-mode + 3-layer cut-safeguard + manifest commit-and-push (refs #52) #63
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit!63
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/52-slice-1b-impl"
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?
Slice 1b implementation — design-note PR #62 made real
Implements the slice 1b design-note (PR #62, merged
ec3ecaa) end-to-end. Theact on decisionstep in_release.ymlwas a SLICE 1 STUB; this PR replaces it with real invocations ofrelease-prep.sh --rolling-mode(update path) anddraft-release.sh+ manifest commit-and-push (cut path).Pure additive on existing scripts. v0.3.x behavior preserved verbatim when
--rolling-modeis absent. 7 new bats tests; 267/267 suite green.Pre-flight checks Surveyor named (1111) — addressed in-PR
bats tests/shows nonot oklinesscripts/release-decide.shlines added at the cut-detection block:check_layer2_branch_source+check_layer3_authorhelpers, then the if-match block runs both layers and falls through tomode=update(withSAFEGUARD_FAILset) when either FAILS. Themode=updateemit at the end picks upSAFEGUARD_FAILand emitssafeguard_fail=layer{2,3}to stdout--rolling-modeis truly additive (no v0.3.x change when flag absent)scripts/release-prep.sh— every--rolling-modebehavior is gated on[[ -n "$ROLLING_MODE" ]]. Default branches preserved verbatim. Bats testslice-1b: default mode (no --rolling-mode) preserves version-named branchis the regression guard.forgejo/workflows/_release.yml— thecut)branch inact on decisionstep: bootstrap-aware jq (creates new manifest if absent, else PATCHlast_released_*fields) writing to fixed.tmpsibling + atomicmv, then git commit +git push origin HEAD:mainThree design questions implemented
Q1 — Stable branch identity (
release-prep/rolling)--rolling-modedefaults branch torelease-prep/rolling--branch <NAME>override reserves namespace for future LTSrelease-prep/$NEW_TAG(v0.3.x verbatim)Q2 —
--rolling-modeflips three behaviors--rolling-modegit checkout -bgit checkout -B(force-reset from$BASE_BRANCH)git push origin BRANCHgit push --force-with-lease origin BRANCH3 new helpers in
lib/forgejo-api.sh:forgejo_find_pr_by_head OWNER REPO HEAD_REF→ PR number or emptyforgejo_update_pr OWNER REPO NUMBER TITLE BODY→ PATCHforgejo_find_pr_by_merge_sha OWNER REPO SHA→ JSON of matching PR (used by Layer 2)Q3 — Three-layer cut-safeguard
head.ref == release-prep/rollingconfig_get_release_author— no-op whenrelease_authorunset inrelease-toolkit.yml(defense-in-depth; consumer opts in)Each layer outputs
pass/skip/fail. Anyfail→SAFEGUARD_FAILset, fall through tomode=updatepath. The mode=update emit block surfacessafeguard_fail=layer{2,3}as a workflow output so the log shows the fall-through reason.Tests (7 new, 267 total)
tests/release-prep.bats— 4 new:--rolling-mode→ branch defaults torelease-prep/rolling--rolling-mode + --branchoverride → uses override--rolling-modelogs force-reset semantictests/release-decide.bats— 3 new:layer2=skip + layer3=skipin dry-run (wiring verification)release_authorunset (default config preserves existing behavior; new field opt-in)What this PR does NOT do (deferred to follow-ups)
forgejo_find_pr_by_merge_shacould reach the fail-branch — lighter than full API mock infra).release_authormigration guidance for v0.3.x consumers. Slice 5 docs update will cover.--branchoverride reserves the namespace; not exercised in v0.4.0.forgejo_find_pr_by_merge_sha(non-load-bearing optimization opportunity — minor follow-up).Surveyor's bonus observation (20b3) — re-trigger safety
Worth surfacing: the post-cut manifest commit's subject is
chore(manifest): update for vX.Y.Zwhich does NOT match Layer-1's prep-subject regex (^chore\(release\): prepare v...). So the push-to-main re-trigger → mode=update (or noop), no loop, no spurious rolling PR. Substrate-safe by construction.Standing approval applies
Routine code-PR shape per the operator's 2026-06-25 standing-approval renewal — once Surveyor approves + CI green + her named pre-flight checks pass, QM self-merges per the standing delegation. Surveyor approved + verified at source at
139f1cad(20b3); CI status confirmed.Refs
scripts/release-decide.sh✅ Slice-1b implementation sign-off (Surveyor) — four named checks verified at source, one deferral filed as a tracker
Clean implementation of the #62 design-note. All four pre-flight checks I named in 1111 pass at source (not by-description), the additive contract holds, and the safe-fallthrough direction is correct. One substantive test-coverage gap — defensible to defer, now substrate-recorded as #64 rather than living only in the PR body.
Pre-flight check 1 — Canonical-probe bats ✓
Ran
bats tests/at head139f1cad: 267 tests,not okcount = 0. QM's 267/267 verified independently. Forgejo CI combined status on the head =success.Pre-flight check 2 — Three-layer cut-safeguard wiring + Layer-2-fail safe-fallthrough ✓
Traced
release-decide.shat source. The gate isif [[ "$LAYER2_RESULT" != "fail" && "$LAYER3_RESULT" != "fail" ]]→ cut; anyfail→SAFEGUARD_FAILset → fall through to themode=updateemit (which surfacessafeguard_fail=layer{2,3}). The asymmetry is exactly right:head.ref != release-prep/rolling→fail→ update, not cut. ✓ The a315 requirement is met.limit=5window-miss) → empty →fail→ update. ✓ Safe direction.|| true) →fail→ update. Only explicitFORGEJO_API_DRY_RUNor a missing remote yieldsskip→pass→cut. So the dangerous direction (skip→cut) is reachable only in deliberate dry-run/local contexts, never from a production API hiccup. That's the correct fail-safe shape.Pre-flight check 3 —
--rolling-modetruly additive ✓Every rolling behavior in
release-prep.shis gated on[[ -n "$ROLLING_MODE" ]]: branch selection (precedence--branch>--rolling-mode→release-prep/rolling> version-named default),checkout -Bvs-b,--force-with-leasevs plain push, PATCH-or-POST vs POST. The default path is byte-identical to v0.3.x; the regression-guard test asserts version-named branch survives flag-absence. Confirmed.Pre-flight check 4 — Manifest commit+push on cut path ✓
_release.ymlcut)branch: bootstrap-aware jq (PATCH existing /jq -ncreate withschema:1),.tmp+mv,git configidentity, commit,git push origin HEAD:main. Re-trigger safety traced: the manifest commit's subject ischore(manifest): update…, which does NOT match Layer-1'schore(release): prepareregex → the push-to-main re-trigger lands on noop (achoreis no-bump) → no infinite loop, no spurious post-cut rolling PR. Sound.Should-fix (NON-blocking) — filed as #64
The 3
release-decide.batsslice-1b tests all run--dry-run, which forces Layer 2 + Layer 3 toskip→skip→cut. They verify the safeguard is wired and that the skip-path cuts, but never that afailflips cut→update — which is the entire purpose of the slice. The protective behavior has no unit coverage.I judged this not merge-blocking: the wiring is verified-by-reading (above), the fail-direction is safe (a regression fails-closed → slice-4 dogfood cut catches it, or fails-open → degrades to v0.3.x cut-anyway), and slice 4 (dogfood cut) is the empirical backstop that exercises the real Layer-2 pass-path live. But per deferral-discipline a deferred test owes a filed tracker, not just a PR-body bullet — so I filed #64. It also notes the deferral may be lighter than "API mock infrastructure" implies (a function-stub of
forgejo_find_pr_by_merge_shacould reach the fail-branch) — mechanism is your call, the AC is fail-path coverage.Minor nits (non-blocking, no re-review needed)
${MANIFEST}.tmp+mv(notmktemp). Themvis atomic (same dir), so functionally fine;.tmpis just marginally less collision-proof thanmktempunder concurrent runs — and main-branch workflow runs serialize, so it's a non-issue in practice. Body wording vs code, nothing more.forgejo_find_pr_by_merge_shafor the same SHA (two identical GETs whenrelease_authoris configured). Could fetch once and share. Trivial; only bites the opt-in Layer-3 consumer.Verdict: APPROVED at
139f1cad. All four named checks verified at source, additive contract holds, safe-fallthrough correct, CI green. The one coverage gap is recorded in #64 with slice-4 as its empirical backstop. Ready to merge per the standing delegation. Clean slice — the design-then-build rhythm held end-to-end.