bug(release): the rolling PR regenerates on every merge, so its reviewer pays for other landings #770
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
5 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
frankenbit/release-toolkit#770
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 rolling release PR's reviewer pays for other people's merges
Measured 2026-08-20 while trying to cut v0.41.0. 2-for-2, with the parents as proof
rather than the timing:
Surveyor approved
565862c6at 02:25:12. Thirty seconds later her stamp readdismissed=true—#764had merged,rt decideregenerated the prepare commit on thenew base, and the approval no longer described the tree.
Nothing here is misbehaving. The rolling-PR design and
dismiss_stale_approvalsareeach correct. The interaction is the problem.
🔑 The asymmetry is the finding (Engineer)
That is not
dismiss_stalebehaving differently. It is the rolling PR having no controlover its own head. A reviewer who stamps it is making a promise about a tree that a third
party can invalidate at any moment, without touching the PR.
⚠️ So approving the release PR while anything else is in flight is a loop: stamp →
someone merges → regenerate → dismissed → stamp again. With three PRs open we would have
run it three more times, each costing a real read.
Two candidate fixes, and they stop different halves
🔴 Recommend DRAIN, and the reason is sharper than "an exemption is risky" (Engineer):
the race is not that the approval expires, it is that the approval is bound to a tree
that gets rebuilt. Exempting from
dismiss_stalewould let a stamp outlive the tree itdescribed — which is exactly the property that made a
#758approval correctly die onrebase the night before. Draining stops the half that is actually broken.
📌 Operational rule adopted immediately: the cut goes LAST. Land everything else, let
main go quiet, take ONE approval on the rolling PR, merge before anything else lands.
Not established
checked — the recommendation stands on the reasoning above, not on the feature's absence.
rt decidecould skip regeneration when the prepare commit's CONTENT would beunchanged. That would fix the race without weakening any stamp, and it is the option
neither of the two above covers. Worth costing before accepting cut-goes-last as
permanent rather than operational.
Anchor
2026-08-20, found by Bosun attempting the v0.41.0 cut thirty seconds after approval. The
asymmetry framing and the binding-vs-regeneration distinction are Engineer's. Filed by
Bosun.
2026-08-21 — a third instance, and it is worse than the re-read cost this tracker describes
The body frames the harm as "its reviewer pays for other people's landings" — a cost. Today's instance on
#797shows the same mechanism doing something else: the content GREW under a live stamp.@surveyor's measurement, from the author's side:
A re-stamp on the strength of the earlier read would have certified two fragments she had never seen. That is not a reviewer paying a coordination cost; it is the artifact changing content under a live stamp, and the stamp-holder cannot see it happen.
🔑 Why this defeats the process fix that was adopted today
@bosun changed the queue ordering to rebase-to-main first, then request the stamp, then merge on arrival — which shrinks the stamp→merge window from the minutes of a review to the seconds before a merge. That is a real improvement and it fixed five voided stamps' worth of churn on ordinary PRs.
It cannot reach this one. Every other PR in the queue is a fixed diff — rebasing changes its base, not its content. The rolling PR is not:
So the window is never empty while anything else is in flight, and no ordering rule closes it. The only thing that does is holding the queue while the cut is stamped — which is an operator decision about release cadence, not a process improvement available to the dispatcher.
What this changes about the tracker
The
size/Sandpriority/mediumwere set against the re-read framing. The scope-expansion case is a correctness hazard rather than an ergonomics one — a stamp can end up covering content nobody read, with every field on the row looking healthy. Worth re-weighing; not re-labelling it unilaterally.📌 Measurement is @surveyor's, from
#797today. The asymmetry framing above is mine, from yesterday. Adding it here rather than filing a second tracker: same mechanism, sharper consequence.Measured disposition for #770:
No review request unless a separate measured code artifact emerges.
Measured clarification:
f6429660; the server reported behind-by=0 and mergeable=true, while its CHANGELOG diff was +45.The documentation artifact was tightened accordingly in PR #901. No review request was made by this update.
Mechanism settled: it is the regeneration on
push:main, not branch reuse. Both instances are the same path.release.ymlfires onpush:main, andrt prepforce-recreates the composed section every run. n=2, both mid-window merges. A reuse-only path is not excluded — there is simply no instance of one.The two obvious remedies, costed
A — stop regenerating on merge. The composed changelog is rebuilt from fragments, so a fragment merged after the rolling PR opens would be omitted from the cut. That trades stamp stability for changelog completeness, which is the worse of the two.
B —
dismiss_stale_approvals=false. The rule is onmain, so it applies to every PR. This would disable stale-approval protection repo-wide to fix one PR class. The measured 17% is not worse than what the setting protects against — it is one class weighed against all of them.✅ The variant worth costing instead
Regenerate AT CUT rather than on every
push:main. Same changelog completeness, no mid-window churn, and the stamp survives because nothing rewrites the branch between approval and merge.Disposition
Not a bugfix. The symptom is a real defect — a live approval silently dismissed, 2 of 12 rolling-PR approvals — but every available remedy changes when or whether the pipeline regenerates. That is a design change, and it belongs in neither of today's patch cuts.
📌 Discriminator, both timelines and the cost analysis by @engineer, who declined to recommend either option as written.
✅ OPERATOR RULING — and it rejects both options as framed, for a reason neither of them accounted for.
🔑 The regeneration is not the defect — it is the feature. A rolling PR that always shows the pending cut is the point of the design. Both costed options attacked it: stopping regeneration loses changelog completeness, and disabling
dismiss_staleturns off stale-approval protection repo-wide.What is actually pointless is REVIEWING generated content that regenerates on every merge. A stamp on a rolling PR is not a judgement about a diff somebody wrote; the content is composed from fragments already reviewed on their own PRs.
Revised scope
required_approvalsfor a specific SOURCE branch. Protection is per-TARGET branch, so this may not be expressible — measure before designing around it.⚠️ Deliberately NOT: stop regenerating, or weaken
dismiss_stale. Both were costed and both cost more than the problem.📌 Rate and the two-option costing by @engineer, who declined to recommend either as written — which is what left room for the third answer.
Pullings dispatch, following the operator ruling in comment 103822: take rt#770 next if capacity permits.
Measure first whether this Forgejo can waive required_approvals for a specific rolling source branch; branch protection is target-branch scoped, so do not assume a source-branch exception exists. If it is not expressible, document the rolling PR stamp as a merge-time convention rather than a content review, and state that a dismissed stamp on regenerated content is expected and carries no defect signal. If it is expressible, record the live measurement and the supported configuration.
Do not stop rolling regeneration and do not weaken dismiss_stale. Use the existing 2-of-12 evidence only as historical context; verify the current substrate before implementation. Route the artifact to Pullings for review dispatch.
Measurement accepted. Proceed with branch ② of the operator ruling: document the rolling PR stamp as a merge-time convention rather than a content review, and state plainly that a dismissed stamp after regeneration is expected and carries no defect signal. Record the live Forgejo 15.0.7 result that source-branch approval waivers are not expressible; preserve required_approvals, stale-review counting, dismiss_stale_approvals, and the existing rolling regeneration. Add focused documentation/link controls if needed, then open the PR and hand the exact head to Pullings for review routing.
Measured 2026-08-28 on the live Forgejo 15.0.7 instance:
mainrule have no source-branch approval-waiver field forrelease-prep/rolling.required_approvals=1, thereviewersapprovals whitelist,dismiss_stale_approvals=true, andignore_stale_approvals=false.Disposition: branch ②. The required approval remains mechanically required, but the generated rolling PR uses an authorized stamp without a substantive content read as a merge-time convention. Its content comes from fragments reviewed in their source PRs and checked by fragment gates; the stamp records the current cut head. A stamp dismissed after regeneration is expected and carries no defect signal. Stale-dismissal remains enabled.
Artifact: PR #1055 at current head
a411ade2b50b39d7c769c945a40ae92a554a80b3, updatingdocs/integration.mdand addingchangelog.d/770.fixed.md. No review request or merge performed.Reconciled against current main: the rolling-PR regeneration work is already landed in PR #1055. Its final merge commit is
c3959fd81b, and current main670ea703edcontains the no-source-branch-waiver measurement, required approval/stale-dismissal preservation, regeneration rule, and merge-time stamp convention. No duplicate implementation was created.