fm-pr-merge: make the merge method configurable, and document why squash is load-bearing #11

Merged
john merged 3 commits from fm/fm-merge-default-merge-commit into main 2026-08-19 07:15:37 +00:00
Owner

What this changes

bin/fm-pr-merge.sh hard-coded --squash. It is now a configurable default that still defaults to squash, with --merge and --rebase available explicitly, and the reason for that default is written down where the next reader will find it.

Why the default did NOT become a merge commit, which is what was originally asked for

The captain asked for merge commits, on the reasonable grounds that squashing destroys history and offers no benefit beyond it. Partway through this work, a live merge attempt settled the question:

Forgejo: branch main is protected from unverified commit 382b3265aa

pyr0-dev-infra protects main with a signed-commits requirement. A squash merge satisfies it because Forgejo creates a new commit signed with the instance key. A merge commit does not, because it carries the branch's own unsigned commits onto main. The identical merge succeeded as a squash immediately afterwards.

So squashing on that repository is not a style preference — it is what makes the protection satisfiable. A default flip would have broken merging there outright, and would break it on any repository that later adopts signing. Since the stated long-term intent is signed commits everywhere, hard-coding merge commits would have been wrong in exactly the direction the fleet is heading.

An earlier iteration of this branch did flip the default, passed its pipeline, and was caught by reading the diff rather than by any check. That is worth recording.

The real trade-off, unresolved and not resolved here

Squashing is what removed the revertable unit behind a defect found the same day: a rollback runbook instructed git revert on a Dockerfile pin that had landed inside a nine-file squash commit, so following it during an incident would have deleted the runbook itself.

Both concerns are legitimate and they conflict. They only stop conflicting if worker commits themselves are signed — at which point merge commits become possible under a signed-everywhere policy and history stays revertable. That is a fleet decision, not a change to this script, and it is deliberately left open.

What else is in here

  • Teardown's landed-work detection, which previously assumed a squash had rewritten commits, is verified for both merge shapes. That check is what prevents unlanded work being destroyed, so it was proven rather than assumed — see the tests.
  • Explicit --merge and --rebase fail loudly with the forge's real reason when they cannot land, rather than silently falling back to squash. That loud failure is what made this whole finding visible in the first place; silently succeeding would have hidden it.
  • Header, help and docs/configuration.md now state the default and why it is what it is, so nobody "simplifies" it back.
  • Tests pin the default so it cannot flip silently again.
## What this changes `bin/fm-pr-merge.sh` hard-coded `--squash`. It is now a **configurable default that still defaults to squash**, with `--merge` and `--rebase` available explicitly, and the reason for that default is written down where the next reader will find it. ## Why the default did NOT become a merge commit, which is what was originally asked for The captain asked for merge commits, on the reasonable grounds that squashing destroys history and offers no benefit beyond it. Partway through this work, a live merge attempt settled the question: ``` Forgejo: branch main is protected from unverified commit 382b3265aa ``` `pyr0-dev-infra` protects `main` with a signed-commits requirement. **A squash merge satisfies it because Forgejo creates a new commit signed with the instance key. A merge commit does not, because it carries the branch's own unsigned commits onto `main`.** The identical merge succeeded as a squash immediately afterwards. So squashing on that repository is not a style preference — it is what makes the protection satisfiable. A default flip would have broken merging there outright, and would break it on any repository that later adopts signing. Since the stated long-term intent is signed commits everywhere, hard-coding merge commits would have been wrong in exactly the direction the fleet is heading. An earlier iteration of this branch did flip the default, passed its pipeline, and was caught by reading the diff rather than by any check. That is worth recording. ## The real trade-off, unresolved and not resolved here Squashing is what removed the revertable unit behind a defect found the same day: a rollback runbook instructed `git revert` on a Dockerfile pin that had landed inside a nine-file squash commit, so following it during an incident would have deleted the runbook itself. **Both concerns are legitimate and they conflict.** They only stop conflicting if *worker commits themselves are signed* — at which point merge commits become possible under a signed-everywhere policy and history stays revertable. That is a fleet decision, not a change to this script, and it is deliberately left open. ## What else is in here - **Teardown's landed-work detection**, which previously assumed a squash had rewritten commits, is verified for **both** merge shapes. That check is what prevents unlanded work being destroyed, so it was proven rather than assumed — see the tests. - Explicit `--merge` and `--rebase` **fail loudly with the forge's real reason** when they cannot land, rather than silently falling back to squash. That loud failure is what made this whole finding visible in the first place; silently succeeding would have hidden it. - Header, help and `docs/configuration.md` now state the default **and why it is what it is**, so nobody "simplifies" it back. - Tests pin the default so it cannot flip silently again.
Change bin/fm-pr-merge.sh so a caller with no explicit method merges with
--merge (a merge commit) rather than --squash, for both GitHub and Forgejo.
Explicit --squash, --merge, and --rebase keep working unchanged.

Update the script header, docs/architecture.md, and the teardown/fleet-sync
comments that claimed squash as the default. Add tests pinning the merge-commit
default and the preserved explicit --squash path, and a teardown test proving
landed-work detection still allows a merge-commit PR with a deleted branch.
Revert the default from --merge back to --squash: a live Forgejo rejection
('branch main is protected from unverified commit') proved squash is what
satisfies signed-commit branch protection, because the forge signs the single
new commit whereas a merge commit or rebase carries the branch's unsigned
commits onto main. It is not a style preference.

Add a home-wide config/merge-default override (squash, merge, rebase) read
when the caller passes no explicit method; an invalid value fails loudly.
Explicit --squash/--merge/--rebase still win outright and a rejected explicit
method surfaces the forge's reason without silently falling back to squash.

Update the header and docs to state the default and why, and extend the tests
to pin the squash default, the config override, explicit --rebase, and the
no-fallback failure path.
no-mistakes(document): document config/merge-default PR merge default in owner docs
Some checks failed
CI / Lint shell scripts (pull_request) Has been cancelled
CI / Test coverage guard (pull_request) Has been cancelled
CI / Behavior portable parallel 1 (pull_request) Has been cancelled
CI / Behavior portable parallel 2 (pull_request) Has been cancelled
CI / Behavior portable serial 1 (pull_request) Has been cancelled
CI / Behavior portable serial 2 (pull_request) Has been cancelled
CI / Behavior portable serial 3 (pull_request) Has been cancelled
CI / Behavior portable serial 4 (pull_request) Has been cancelled
CI / Behavior tests (Herdr) (pull_request) Has been cancelled
CI / Behavior timing aggregate (pull_request) Has been cancelled
CI / Stock macOS Bash snapshot compatibility (pull_request) Has been cancelled
CI / Repo invariants (pull_request) Has been cancelled
Require no-mistakes / PR must be raised via no-mistakes (pull_request) Has been cancelled
03728fa784
john merged commit 8823455b56 into main 2026-08-19 07:15:37 +00:00
Sign in to join this conversation.
No reviewers
No labels
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
john/firstmate!11
No description provided.