feat(decide): a bump label changed on the rolling PR re-prepares it at once, and a stale prepare cannot be merged #1545

Open
opened 2026-10-04 11:24:02 +02:00 by claude · 0 comments
Member

Symptom, measured on an adopter (frankenbit/storey#20, 2026-10-04). A bump/patch label attached to the open rolling PR at about 11:16 changed nothing on it: #20 still read chore(release): v0.2.0 at its prepare commit 168b65f (updated_at 11:17:28). The label is read only when release.yml next runs on a push to main (integration.md, When the label takes effect). The next push to Storey's main was a merge some minutes later, made for that purpose, and its release run had not started yet when last checked. Until a run arrives, the label sits on a PR whose content contradicts it.

Why it matters: it can cut the wrong version. The cut does not consult the label. detectCut (internal/decide/decide.go) takes the version from the prepare commit's subject. Only updateDecision reads readRollingBumpLabel, and no other reader of bump/* exists besides setup-bump-labels. So an operator who adds bump/patch and then approves the PR, the order the docs suggest, merges and tags v0.2.0 with bump/patch showing on the PR. Nothing in between refuses it.

The case is routine for an adopter whose numbering is not plain SemVer. Storey cuts 0.<milestone>.<increment>, one release per increment. Every increment brings a feat: commit or an added fragment, so every new rolling PR opens as a minor bump and needs bump/patch by hand.

Proposal

  1. Re-prepare on a label change. When a bump/* label is added to or removed from the open rolling PR, run decide and prep in rolling mode against main, not against the PR's merge ref, so the PR's title and changelog follow at once. Forgejo's pull_request event has labeled and unlabeled types. Whether Forgejo 15.0.7 (our instance) fires them on a bot-authored PR from a same-repo branch is unverified; measure it before relying on it.
  2. Refuse a stale prepare. A gate on the rolling PR fails when the version in its prepare commit is not the version its current bump/* label (or the auto-detected bump, when there is no label) yields. This part prevents the wrong cut, and it still holds when (1) is queued, cancelled or unsupported by the forge.

Acceptance criteria

  • With no push to main, attaching or removing a bump/* label on the open rolling PR re-titles and re-composes it within one workflow run, or the docs say plainly why it cannot and what to do instead.
  • Merging a rolling PR whose prepared version disagrees with its current bump label is refused by a check, with a message naming both versions and the remedy.
  • integration.md's When the label takes effect describes the new behaviour, and drops step 2 ("push any new commit") if (1) makes it unnecessary.

Reported from Storey's adoption (#1478).

**Symptom, measured on an adopter (frankenbit/storey#20, 2026-10-04).** A `bump/patch` label attached to the open rolling PR at about 11:16 changed nothing on it: #20 still read `chore(release): v0.2.0` at its prepare commit `168b65f` (`updated_at` 11:17:28). The label is read only when `release.yml` next runs on a push to `main` (integration.md, *When the label takes effect*). The next push to Storey's `main` was a merge some minutes later, made for that purpose, and its release run had not started yet when last checked. Until a run arrives, the label sits on a PR whose content contradicts it. **Why it matters: it can cut the wrong version.** The cut does not consult the label. `detectCut` (internal/decide/decide.go) takes the version from the prepare commit's subject. Only `updateDecision` reads `readRollingBumpLabel`, and no other reader of `bump/*` exists besides `setup-bump-labels`. So an operator who adds `bump/patch` and then approves the PR, the order the docs suggest, merges and tags **v0.2.0** with `bump/patch` showing on the PR. Nothing in between refuses it. The case is routine for an adopter whose numbering is not plain SemVer. Storey cuts `0.<milestone>.<increment>`, one release per increment. Every increment brings a `feat:` commit or an `added` fragment, so every new rolling PR opens as a minor bump and needs `bump/patch` by hand. **Proposal** 1. **Re-prepare on a label change.** When a `bump/*` label is added to or removed from the open rolling PR, run decide and prep in rolling mode against `main`, not against the PR's merge ref, so the PR's title and changelog follow at once. Forgejo's `pull_request` event has `labeled` and `unlabeled` types. Whether Forgejo 15.0.7 (our instance) fires them on a bot-authored PR from a same-repo branch is unverified; measure it before relying on it. 2. **Refuse a stale prepare.** A gate on the rolling PR fails when the version in its prepare commit is not the version its current `bump/*` label (or the auto-detected bump, when there is no label) yields. This part prevents the wrong cut, and it still holds when (1) is queued, cancelled or unsupported by the forge. **Acceptance criteria** - [ ] With no push to `main`, attaching or removing a `bump/*` label on the open rolling PR re-titles and re-composes it within one workflow run, or the docs say plainly why it cannot and what to do instead. - [ ] Merging a rolling PR whose prepared version disagrees with its current bump label is refused by a check, with a message naming both versions and the remedy. - [ ] integration.md's *When the label takes effect* describes the new behaviour, and drops step 2 ("push any new commit") if (1) makes it unnecessary. Reported from Storey's adoption (#1478).
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
frankenbit/release-toolkit#1545
No description provided.