The merge request Merge action now runs through htmx for every mode (fast-forward, squash, merge) instead of a full-page POST and 303 reload.
- The form posts asynchronously, drops double submits and shows a pending indicator; without JS it still posts normally.
- htmx merges get OOB fragments (state badge + action area). Refusals, failures, expired sessions and rejected origins render a status in the merge box.
- Other open tabs of the same MR receive the merged state through the live stream.
- The checks-label observer watches the stable MR bar so it survives swaps.
Deferred: the fragment path still builds the full page view (diff, comments) before rendering only state/actions; a dedicated state view builder is a follow-up.
Checks: go test ./..., go vet, node jstest suite (198 pass). Not manually tested in a browser.