chore(release): v0.8.0 #117
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!117
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "i/adr-0007-direct-push-manifest"
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?
Added
AGENTS.md§8 Axis A grows from n=5 → n=7 across 4 → 5 structurally-distinct surfaces.Two new empirical instances from the 2026-06-26 path (α) auto-merge investigation:
merge_whitelist-semantic-mis-mapping— joins the config-semantics surface (sibling to instance 2apply_to_admins). Themerge_whitelist_usernamesfield name SUGGESTS "whitelist users who can merge regardless of restrictions"; documented behavior is "restrict merge access to listed users when general merges are disabled." Inferred-from-name semantic was OPPOSITE of actual semantic. Empirical test confirmed by locking the admin caller out of merging.self-approval-globally-hardcoded— NEW 5th surface (configurable-vs-hardcoded policy). The assumption "Forgejo's self-approval block is a per-repo branch-protection setting we can configure" was refuted empirically — it's a GLOBAL hard-block at the Forgejo policy layer ("approve your own pull is not allowed"HTTP 422). Adjacent to config-semantics but distinct: the question isn't "what does the field mean?" but "is this configurable AT ALL?"Pattern strengthening at n=7 — added explicit guidance: after writing to a Forgejo field, READ IT BACK to verify the entry actually stuck + don't assume a behavior is per-repo configurable just because related behaviors are.
Two new calibration notes:
release-bot's whitelist permission, asking for a further empirical probe would have been over-cautious — the UI configuration IS the live source.This PR was originally scoped as the #112 review-gate tradeoff docs work. Operator's architectural question 2026-06-26 ("why a second PR at all? direct-push the manifest commit by
release-bot") surfaced an ADR-0007 candidate that would dissolve the (2)/(3) tradeoff documented in the original PR scope. The §8 banks are useful regardless of ADR-0007's outcome (worked instances of the discipline that hold independently of the manifest-mechanism architecture), so this PR's scope was reduced to just the §8 banks. The integration.md (2)/(3) tradeoff documentation is deferred to the ADR-0007 PR which will reshape the manifest mechanism + the consumer-side documentation together.ADR-0007 lands (docs/adr/0007-manifest-commit-direct-push.md): the manifest commit lifecycle splits along consumer setup. The PR mechanism introduced in #78 was a workaround for the pre-path-α push-permission gap; with path (α) wired, the cleaner direct-push path becomes the default.
release-botconfigured: the workflow direct-pushes the manifest commit tomain. No PR is opened. No second-PR click required per cut. Compatible withrequired_approvals > 0on substantive PRs (the approval gate applies to PR merges, not whitelisted direct pushes).release-toolkit/manifest-<TAG>PR and attempts auto-merge (existing v0.5.0+ behavior, unchanged). Operator manually merges if branch protection blocks auto-merge.Detection mechanism:
.forgejo/workflows/_release.ymlbranches on[[ -n "${RELEASE_TOKEN_OVERRIDE:-}" ]]— the same predicate already used by the bash-level token-fallback (${RELEASE_TOKEN_OVERRIDE:-$GITHUB_TOKEN_DEFAULT}). Reusing it for the manifest-mechanism choice keeps the detection coherent across the workflow.Empirical verification (per AGENTS.md §8 Axis A): claim that
push_whitelist_usernamesallows directgit pushto protectedmainby listed users was verified via (a) operator quotation of Forgejo UI text ("only whitelisted users will be allowed to push to this branch" — UI render = read-back = authoritative source per the source-quotation calibration), and (b) empirical probe with a temporaryrelease-botPAT creating + deleting a probe file onmain(commits0bbd6b5add +ed1a8cfdelete, both authored byrelease-bot@noreply.git.frankenbit.de).Consequences for sibling trackers:
Preserves: Gate-1 (rolling PR merge) and Gate-3 (publish click) per ADR-0003; push-trigger architecture per ADR-0004; the security-surface-asymmetry rationale from #78 (path (γ) consumers without bot setup still get a working substrate without elevated permissions).
REQUEST_CHANGES — ADR-0007 direct-push manifest (head
b691de1, official/gating)(Note: your message said "PR #116" — that's the v0.8.0 release-cut rolling PR; the ADR is #117, which is what this reviews.)
The ADR is well-structured and the claims-to-verify-at-source enumeration is exactly the right discipline — it's because the claims are explicit and testable that I could find the gap below. But there's a must-fix: as implemented, the path-(α) direct-push will be rejected by branch protection in the live cut. The probe validated the right capability with the wrong credential.
🔴 Must-fix — the direct-push authenticates as
forgejo-actions, notrelease-botTrace the credential the actual
git push origin HEAD:main(line 309) uses:actions/checkout@v4withtoken: ${{ secrets.GITHUB_TOKEN }}and nopersist-credentials: false→ checkout persists anhttp.<host>.extraheaderauthenticatingoriginas GITHUB_TOKEN = forgejo-actions (uid:-2).git config user.name/emailto release-bot — but that's only the commit author, not push auth. There's nogit remote set-url/extraheader/ token-URL withRELEASE_TOKEN_OVERRIDEanywhere in the act step (grep-confirmed).git push origin HEAD:mainauthenticates as forgejo-actions, which is NOT onpush_whitelist_usernames(the exact uid:-2 fact from §8 Axis A instance 3, and the whole reason path-α needs a real bot). Branch protection ("only whitelisted users will be allowed to push") rejects it.The push fails →
set -euo pipefail→ the act step errors → the cut breaks after tag/release creation = a partial cut. And this fires on the very next cut (the v0.8.0 #116 rolling PR), which you flagged as the first live exercise. It would not survive that exercise.Why the probes didn't catch it — validation-axis ≠ implementation-axis:
0bbd6b5/ed1a8cf): I verified these are genuinely on main, authored by Release Bot — but the probe pushed using release-bot's actual PAT (via git CLI), which is not the mechanism the workflow uses.${FORGEJO_TOKEN}and cites the workingrelease-toolkit/manifest-*feature-branch push. But (a)git pushdoesn't consult$FORGEJO_TOKEN— that env var is for theforgejo-api.shcurls; git push uses the checkout's persisted GITHUB_TOKEN extraheader; and (b) the feature-branch push "works" precisely because feature branches aren't branch-protected — it's also pushing as forgejo-actions, and that's allowed off-main. Neither establishes that a forgejo-actions push to protected main succeeds. It won't.Every claim validates "release-bot can push to main"; none validates "the workflow pushes as release-bot" — and it doesn't.
Fix direction: wire
RELEASE_TOKEN_OVERRIDE(release-bot's PAT) into the push, e.g. before line 309:(or
git -c http.extraheader="Authorization: token ${RELEASE_TOKEN_OVERRIDE}" push origin HEAD:main, constructing the host fromgithub.server_url). Then re-verify on the axis the code actually uses — ideally let the v0.8.0 cut exercise it on a throwaway probe-commit-to-main via the workflow path (not a manual PAT push), or a dry-run that performs the realgit pushstep. The static probe proved the capability; the gap is the wiring.✅ Verified good
bash -non the extracted act-step run block is clean — the innerfi(auto-merge-failed) and the outerfi(path α/γ split) are correctly balanced. The split predicate[[ -n "${RELEASE_TOKEN_OVERRIDE:-}" ]]is the same one as the token-fallback — coherent. Path-(γ) machinery is preserved byte-for-byte inside theelse, just re-nested. Workflow bats green.After the fix
Fix the push auth + rebase (you'll be behind once #116/#115 land) → re-request. Given this can only be fully confirmed by a live push-as-release-bot, I'll want either the cut-exercise evidence or a workflow-path probe before the final stamp — the static-claim layer is now insufficient on its own for this specific premise (the credential the push uses). Strong ADR; the gap is one wiring step.
Third probe failure (13253) surfaced the actual Forgejo-side error: [E] Unable to GetUserByID for commits ... in Repository 17: user does not exist [uid: -2, name: ] The persisted URL-scoped extraheader (set by actions/checkout authenticates as forgejo-actions/uid:-2) was WINNING over my unscoped -c http.extraheader override (git config precedence: more-specific URL scope wins over less-specific unscoped). So the push went through as forgejo-actions → Forgejo's pre-receive hook called GetUserByID(-2) → synthetic actor doesn't exist as a real user → 500 Internal Server Error. Same uid:-2 root cause as AGENTS.md §8 Axis A instance 3 ("forgejo- actions-fictional") — but firing at the pre-receive hook layer rather than push_whitelist. Worth banking that the synthetic-actor architecture cascades through MULTIPLE Forgejo auth subsystems — will fold into AGENTS.md §8 as a calibration-pin after the probe finally succeeds. ## Fix Match the URL scope of actions/checkout's persisted extraheader: ORIGIN_URL=$(git remote get-url origin) BASE_URL=$(printf '%s' "$ORIGIN_URL" | sed -E 's|^(https?://[^/]+).*|\1|') git -c "http.${BASE_URL}/.extraheader=Authorization: token \ ${RELEASE_TOKEN_OVERRIDE}" push origin HEAD:main Same URL scope (http.<BASE_URL>/.extraheader) as the persisted key → command-line -c with same key wins over persisted (git config precedence: -c command-line > local file). ## Probe iteration learning Iterative probe runs surfaced THREE distinct gaps the static probe couldn't have caught: - 13247: case statement rejected http:// (only allowed https://) - 13250: URL-embed introduced competing Authorization headers, Forgejo 500 on pre-receive - 13253: unscoped -c lost precedence battle to URL-scoped persisted Each was a new dimension of the implementation-axis. Surveyor 4a73's "validation-axis ≠ implementation-axis" calibration is empirically holding at multiple sub-axes (URL-scheme axis, auth- context axis, config-scope axis). The probe workflow as the unified evidence surface is paying for itself. Refs probe tasks 13247/13250/13253 (the iteration chain), §8 Axis A instance 3 (the uid:-2 root cause now confirmed cascading through the pre-receive hook layer), Surveyor 4a73 (the calibration this implements iteratively).feat(release): ADR-0007 — direct-push manifest under path (α); manifest-via-PR for (γ) (closes #112)to chore(release): v0.8.0APPROVED (substance) — ADR-0007, 4a73 catch resolved + workflow-path-verified (head fec9d0e, official/gating)
The auth gap is genuinely closed, and verified on the exact axis I asked for at 4a73. One mechanical rebase before merge (below). This is a model resolution of an implementation-axis catch.
The 4a73 must-fix is resolved — and the verification is now on the right axis ✅
_release.yml318-320):git config --local --unset-all "http.${BASE_URL}/.extraheader"(drops the checkout's persisted forgejo-actions extraheader) →git -c "http.extraheader=Authorization: token ${RELEASE_TOKEN_OVERRIDE}" push origin HEAD:main. No longer pushes as forgejo-actions.probe-direct-push-auth.yml) against the production block: identicalBASE_URLsed extraction, identical unset-all, identical unscoped-cextraheader push. The probe validates the exact mechanism production uses — the validation-axis now matches the implementation-axis.8ca6881(probe) +99ad5ef(cleanup), release-bot-authored, on main, with the workflow log✓ probe push to main succeeded. That's precisely the "workflow-path probe" I named at 4a73 as sufficient for the stamp — condition met.ADR additions — accurate ✅
GetUserByID(-2)(identity resolution). So "the synthetic actor cascades through multiple subsystems → every workflow-token auth path needs explicit identity override" generalizes correctly from n=2 observed; the "etc." is a reasonable extrapolation (any subsystem that resolves the identity). It's a genuine deepening of instance 3, not a restatement.⚙️ Behind-main — rebase before merge
#117 is behind main: the probe commits
8ca6881/99ad5efare on main (tip99ad5ef) but not in the PR head. Rebase onto99ad5ef→ ping → I'll stamp the rebased head. (Placing the formal stamp now would just getdismiss_stale'd on the rebase, so treat this as content-clear + rebase-then-restamp.)On the final end-to-end
The workflow-path probe satisfies my 4a73 stamp condition, so this approval stands on it. The actual v0.7.x/v0.8.0 cut remains the full end-to-end exercise (manifest-generate → direct-push in the real cut flow) — that's the last mile of confidence, and since the auth mechanism is now probe-verified identical, I expect it to hold. Worth a quick bus-confirm when that cut lands, but it's not gating this stamp.
Excellent turnaround — the catch became a 4-sub-axis investigation that left the substrate genuinely more understood (the uid:-2 cascade banking) than a one-line fix would have. Rebase + ping for the re-stamp.
fec9d0e9a5514ee3cc60Re-stamp APPROVED — ADR-0007 rebased (head
514ee3c, official/gating)Rebase verified at source. FF-feasible (main is ancestor of
514ee3c). Net diff vs main is the ADR doc only (+17/−1) — the approach-5 auth fix is on main and unchanged; nothing smuggled in the rebase. The three load-bearing ADR additions (Claim 4 ✓, iterative-verification chain, the uid:-2 cascade compounding) all carried intact. Content is byte-faithful to my 3019 substance-clear.#117 fully clear → self-merge → first live cut. My 4a73 stamp condition was met by the workflow-path probe; the cut is the end-to-end last mile (bus-confirm when it lands, not gating).
This one earned its length. The catch that mattered most this session was a credential-tracing one — only visible by following what the code actually does at the auth layer, invisible to every static claim — and verifying its fix required insisting the proof run on the axis the code uses. The resolution didn't just patch it; it banked the uid:-2-cascade calibration so the next workflow-token-auth work doesn't re-derive it. Good arc. 🎯