Today every task worker that completes its work calls orchestra sync push, which:
origin/<branch>.origin/main.git merge --squash <branch> into local main.git push --force-with-lease=main:<sha> origin main.This works but bypasses GitHub's PR infrastructure entirely:
--squash, but that means individual commits on the branch are lost. For multi-step task work that's a real loss when investigating a regression.orch.execute %s fix being silently reverted twice) made the cost of this concrete. Migrating to PRs would have caught at least one of them at the GitHub-side mergeability check.orchestra/sync.py:_push_main_local() (and the push_main_via_gh_pr variant we'll add) moves from direct push to:
origin/<branch> (unchanged).gh pr create --base main --head <branch> --title <generated> --body <generated>.gh pr merge <PR#> --squash --delete-branch --auto.--auto flag means the PR merges as soon as required status checks pass (which we add in a sibling spec).gh pr view <PR#> --json state to wait for merge before returning, OR fire-and-forget if the caller doesn't care about ordering.The post-merge regression guard moves from sync.py-local to a GitHub Actions workflow (sibling spec: regression_guard_action_spec.md) that runs against the PR's merge commit. This way the same guard fires for direct gh pr merge from a human and for orchestra sync push automation.
orchestra sync push becomes a thin wrapper around gh pr create + gh pr merge --auto. The flock + retry + smart-merge logic in sync.py is preserved for the branch-push step; everything after that step delegates to GitHub.
ORCHESTRA_MERGE_VIA_PR=1 enables the new path._push_main_local() checks the env var; default keeps current behavior.ORCHESTRA_MERGE_VIA_PR=1 orchestra sync push --project SciDEX --branch test.slot >= 50 only) to get baseline metrics: success rate, mean time-to-merge, false-positive rate of the guard.ORCHESTRA_MERGE_VIA_PR=1.ORCHESTRA_MERGE_VIA_PR=0 for emergency rollback.AGENTS.md PR section to remove the "transitioning" caveat.pull_request rule (require PRs for all changes to main).required_status_checks with strict: true (must be up to date) once the smoke check status check is wired (sibling spec).gh uses the env's GITHUB_TOKEN or gh auth status token. The SciDEX-AI-User-01 PAT (or app token) is what currently does direct pushes; same credentials will create + merge PRs. Verify gh auth status works inside the worker sandbox before flipping the default.bwrap sandboxes don't have network access to GitHub. The deploy step runs OUTSIDE the sandbox (Orchestra invokes _deploy_to_main from the supervisor process), so this is fine — but worth verifying with a smoke test in Phase A.gh pr merge --delete-branch deletes the remote branch. Local cleanup happens via orchestra hygiene already.gh pr create succeeds but gh pr merge fails (e.g., status check pending), the PR sits open. Add a sweeper (orchestra hygiene extension) that polls open PRs older than N hours and either auto-merges them if checks pass, or notifies on Slack/dashboard if blocked.--force-with-lease=main:<sha> ensures we don't overwrite concurrent pushes. With gh pr merge, GitHub's internal merge logic enforces concurrency safely; no force-with-lease needed.regression_guard_action_spec.md — GitHub Action that runs .orchestra/post_merge_guard.txt patterns + scripts/smoke_check.py forbidden-keys against PR merge commits. Becomes a required status check in Phase C.pull_main_sh_retire_spec.md (already partially shipped) — once main updates only via merged PRs, the 30-second git reset --hard FETCH_HEAD cycle on /home/ubuntu/scidex/ becomes simpler (just git pull --ff-only) since there's no concurrent direct-push race.pull_request and the strict status check; no direct-push paths remain in sync.py.--required_approving_review_count: 0 initially, then bump).