branching: the merge methods the orchestrator is told to use
All checks were successful
PR build (required check) / changes (pull_request) Successful in 14s
secrets-guard / encrypted (pull_request) Successful in 10s
shell-lint / shellcheck (pull_request) Successful in 11s
PR build (required check) / build-backend (pull_request) Has been skipped
PR build (required check) / build-frontend (pull_request) Has been skipped
PR build (required check) / validate-observability (pull_request) Has been skipped
PR build (required check) / gate (pull_request) Successful in 2s

Forgejo now allows only squash and fast-forward-only, so the three method= calls
in this file named styles the API rejects: rebase for feat->dev, and merge
commits for both promotions. An orchestrator following them gets a 405.

The fast-forward paragraph said adopting ff-only 'needs a one-time
reconciliation of the protected branches, which is the owner's decision'. That
decision was taken and the reconciliation done — the trees were already
identical, so it carried no content. release subset-of main subset-of dev now
holds and promotions land the branches on the same SHA. The paragraph keeps the
structural explanation, since it is what makes the failure mode recognisable,
and now points at reconciliation as the remedy.
This commit is contained in:
Emi Griffith 2026-08-01 17:50:12 -07:00
parent 960c936d86
commit fd12ad6740

View file

@ -134,7 +134,7 @@ You merge **one PR into `dev` at a time**. For each:
3. `forge_pr_await(pr=N, until="checks_complete")` — checks on the **rebased** 3. `forge_pr_await(pr=N, until="checks_complete")` — checks on the **rebased**
head. The run you saw before the update was for a merge base that no longer head. The run you saw before the update was for a merge base that no longer
exists. exists.
4. `forge_pr_merge(pr=N, method="rebase", delete_branch=true)`. 4. `forge_pr_merge(pr=N, method="squash", delete_branch=true)`.
5. Only then move to the next PR. 5. Only then move to the next PR.
`block_on_outdated_branch` is set on `dev`, so Forgejo enforces step 2 rather `block_on_outdated_branch` is set on `dev`, so Forgejo enforces step 2 rather
@ -150,7 +150,7 @@ context. Do not resolve a large conflict on someone else's behalf.
Promote when `dev` is green and a coherent batch is complete — not on a timer, Promote when `dev` is green and a coherent batch is complete — not on a timer,
not mid-feature. `promote(from="dev", to="main")` opens the PR with the not mid-feature. `promote(from="dev", to="main")` opens the PR with the
divergence, the conflict prediction and the changelog written into its body. divergence, the conflict prediction and the changelog written into its body.
Merge it with `method="merge"`. Merge it with `method="fast-forward-only"`.
Unready work belongs behind a feature flag, or not on `dev` yet. **Never promote Unready work belongs behind a feature flag, or not on `dev` yet. **Never promote
around it with cherry-picks.** around it with cherry-picks.**
@ -161,17 +161,23 @@ SHAs, and merging it would fire the target's deploys for nothing.
### Promotion: `main``release` ### Promotion: `main``release`
Prepare with `promote`, then ask. On the owner's yes, merge with `method="merge"` Prepare with `promote`, then ask. On the owner's yes, merge with
and tag it: `forge_releases(create={tag:"v<semver>", target:"release", ...})`. `method="fast-forward-only"` and tag it: `forge_releases(create={tag:"v<semver>", target:"release", ...})`.
**On fast-forward.** The written policy is that `release` moves only by **On fast-forward.** Both promotions move by fast-forward, and as of
fast-forward. It cannot today: every promotion so far has been a merge commit 2026-08-01 that is in force rather than aspirational. It was blocked until then
*into* the target, which the source never receives, so the branches are mutually for a structural reason worth remembering: every promotion had been a merge
divergent by construction and Forgejo will refuse a `fast-forward-only` merge — commit *into* the target, which the source never receives, so the branches were
correctly. The style is implemented and accepted by `forge_pr_merge`; adopting mutually divergent by construction and Forgejo refused `fast-forward-only`
it needs a one-time reconciliation of the protected branches, which is the correctly. The one-time reconciliation the old policy was waiting for has
owner's decision. Until that happens, promotions are merge commits. Do not happened; the trees were already identical, so it carried no content.
attempt to "fix" this with a rebase or a force-push.
`release ⊆ main ⊆ dev` now holds, and a promotion lands the branches on the
same SHA rather than merely the same tree. Merge commits and rebase merges are
disabled repo-wide, so a slip fails loudly instead of quietly re-diverging the
chain. If Forgejo refuses with "diverging branches", the target has picked up a
commit the source lacks — reconcile it once, target into source, fast-forward.
Do not attempt to "fix" it with a rebase or a force-push.
### Hotfix ### Hotfix