Phase 2 review fix#1 (architectural pushback): replace force-push of
contributor's gh-pr-N/* branch with a three-step synthetic-branch flow:
1. Worktree on local branch _merged-{slug} from origin/main
2. git merge --no-ff origin/{branch} into the local branch
3. Push merge commit to origin/_merged/{branch} (synthetic audit ref)
4. Function ff-pushes merge_sha → origin/main directly
Contributor's gh-pr-N/* branch on Forgejo is now never touched.
Force-pushing it would have rewritten the tip with a merge commit the
contributor didn't author — confusing bot force-push in Forgejo PR UI.
Mirrors the _clean/* synthetic branch pattern in cherry-pick.
Function now owns the push to main (was dispatch's job for cherry-pick
and reweave). Returns sentinel "merged --no-ff (external PR #N, M=<sha>,
audit_ref=...)" that dispatch detects to skip its ff-push and route
directly to PR-close + mark_merged + audit. Audit detail JSON now
includes merge_commit_sha + audit_ref + github_pr (Ship review #5).
Smoke-tested in scratch repo end-to-end:
- contributor branch tip unchanged ✓
- audit ref _merged/gh-pr-90/... carries merge SHA ✓
- main tip equals merge SHA (ff-push, no force) ✓
- contributor SHA ancestor of main → GitHub badge fires ✓
Sentinel return parsed via 3 regexes in dispatch (full 40-char SHA in
return string for durability). Branch comment in dispatch explicitly
notes contributor branch is left in place — sync-mirror keeps the
GitHub PR <-> Forgejo PR link observable through it.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>