diff --git a/.claude/BRANCHING.md b/.claude/BRANCHING.md index 47a14fe..8075646 100644 --- a/.claude/BRANCHING.md +++ b/.claude/BRANCHING.md @@ -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** head. The run you saw before the update was for a merge base that no longer 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. `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, not mid-feature. `promote(from="dev", to="main")` opens the PR with the 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 around it with cherry-picks.** @@ -161,17 +161,23 @@ SHAs, and merging it would fire the target's deploys for nothing. ### Promotion: `main` → `release` -Prepare with `promote`, then ask. On the owner's yes, merge with `method="merge"` -and tag it: `forge_releases(create={tag:"v", target:"release", ...})`. +Prepare with `promote`, then ask. On the owner's yes, merge with +`method="fast-forward-only"` and tag it: `forge_releases(create={tag:"v", target:"release", ...})`. -**On fast-forward.** The written policy is that `release` moves only by -fast-forward. It cannot today: every promotion so far has been a merge commit -*into* the target, which the source never receives, so the branches are mutually -divergent by construction and Forgejo will refuse a `fast-forward-only` merge — -correctly. The style is implemented and accepted by `forge_pr_merge`; adopting -it needs a one-time reconciliation of the protected branches, which is the -owner's decision. Until that happens, promotions are merge commits. Do not -attempt to "fix" this with a rebase or a force-push. +**On fast-forward.** Both promotions move by fast-forward, and as of +2026-08-01 that is in force rather than aspirational. It was blocked until then +for a structural reason worth remembering: every promotion had been a merge +commit *into* the target, which the source never receives, so the branches were +mutually divergent by construction and Forgejo refused `fast-forward-only` — +correctly. The one-time reconciliation the old policy was waiting for has +happened; the trees were already identical, so it carried no content. + +`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