What exists
releaseCommitMode: pr opens a release pull request instead of committing to main, and autoMergeReleases: true enables auto-merge on it. Both work today.
The gap
The release PR is not persistent — it is keyed to the version it was opened for, so a new commit spawns a second PR instead of updating the first. In src/monorepo/run/execute.rs:
let branch_name = format!("release/{}", release_parts.first()...); // version-derived
...
forge_instance.create_merge_request(&branch_name, ...) // create-only (POST /pulls)
So the flow is:
- A
feat: lands → FerrFlow opens release/...v1.3.0 with the bump + changelog.
- Another
feat: lands before you merge → the computed version is now v1.4.0, the branch name changes, and a new PR opens. The v1.3.0 PR is left open and stale.
release-please and Changesets instead keep one long-lived release PR: each new commit recomputes the version and changelog and force-pushes the same branch, updating the existing PR's title and body in place. That is what turns the comparison table's ~ into a ✓.
Proposed
A persistent release PR under releaseCommitMode: pr:
- Stable branch name — not version-derived. Something like
ferrflow/release (or release/<target-branch>), so there is exactly one release branch per target.
- Update, don't duplicate — before creating, look for an open release PR from that branch (
find_merge_request alongside the existing find_comment). If one exists, force-push the recomputed branch and update the PR title/body; only create_merge_request when there is none.
- Recompute on every commit — the bump and changelog already recompute per run; the branch just needs to be reset to the new release commit rather than branched afresh.
- Auto-merge stays opt-in —
autoMergeReleases already gates enable_auto_merge; it should re-apply on update, and be a no-op when off (the PR simply waits for a human).
Out of scope / caveats
- Per-package scope (
releaseCommitScope: perPackage) currently makes one commit per package on the branch — the persistent branch has to rebuild that commit set each time, not append.
- Force-pushing a branch a human may have pushed review commits onto is a real hazard; the update path should refuse (or warn) if the release branch has commits it didn't author, the same way the direct-commit path guards against a stale
main.
Verified the current create-only / version-keyed behaviour on main at time of writing.
What exists
releaseCommitMode: propens a release pull request instead of committing tomain, andautoMergeReleases: trueenables auto-merge on it. Both work today.The gap
The release PR is not persistent — it is keyed to the version it was opened for, so a new commit spawns a second PR instead of updating the first. In
src/monorepo/run/execute.rs:So the flow is:
feat:lands → FerrFlow opensrelease/...v1.3.0with the bump + changelog.feat:lands before you merge → the computed version is nowv1.4.0, the branch name changes, and a new PR opens. Thev1.3.0PR is left open and stale.release-please and Changesets instead keep one long-lived release PR: each new commit recomputes the version and changelog and force-pushes the same branch, updating the existing PR's title and body in place. That is what turns the comparison table's
~into a✓.Proposed
A persistent release PR under
releaseCommitMode: pr:ferrflow/release(orrelease/<target-branch>), so there is exactly one release branch per target.find_merge_requestalongside the existingfind_comment). If one exists, force-push the recomputed branch and update the PR title/body; onlycreate_merge_requestwhen there is none.autoMergeReleasesalready gatesenable_auto_merge; it should re-apply on update, and be a no-op when off (the PR simply waits for a human).Out of scope / caveats
releaseCommitScope: perPackage) currently makes one commit per package on the branch — the persistent branch has to rebuild that commit set each time, not append.main.Verified the current create-only / version-keyed behaviour on
mainat time of writing.