Skip to content

Commit a793928

Browse files
PureWeenCopilot
andauthored
Add safe push workflow for PR review workers (#288)
## Problem PR review workers were pushing to the wrong remote (e.g., pushing to `origin/PureWeen` when the PR comes from a fork like `vitek-karas`), and using `--force-with-lease` after rebasing unnecessarily. ### Root Cause (verified by testing) Workers were using manual checkout: ```bash git fetch origin pull/<N>/head:pr-<N> git checkout pr-<N> ``` This creates a branch with **no tracking info** (`Remote: NONE`, `Merge: NONE`). When the worker then runs `git push` or `git push origin`, git defaults to `origin` (PureWeen/PolyPilot) — even if the PR came from a fork. In contrast, `gh pr checkout <N>` correctly sets up tracking: - Same-repo PR #247: `Remote: origin`, `Merge: refs/heads/copilot/build-notification-and-timers` ✅ - Fork PR #280: `Remote: vitek-karas`, `Merge: refs/heads/Add-a-way-to-specify-polypilot-clone-root-7299` ✅ The second problem was `git rebase origin/main` + `--force-with-lease`, which requires force pushing. Using `git merge origin/main` adds a merge commit instead — no force push needed. ## Changes ### `push-to-pr.sh` A shell script that: 1. Reads PR metadata via `gh pr view` to find the correct owner and branch 2. Maps the owner to the correct git remote 3. Pushes to that remote **without** `--force` 4. Verifies the push landed by comparing local and remote HEADs 5. Refuses to push if current branch doesn't match the PR branch (guards against wrong-branch pushes) ### `.squad/routing.md` Updated Fix Process replacing rebase+force-push with merge+safe-push, with clear explanation of why `gh pr checkout` is required. ### `.squad/decisions.md` Explicit shared rules: - Never force push - Always use `gh pr checkout` (not manual fetch) - Always merge (not rebase) - Always verify push target before pushing - Use `push-to-pr.sh` for all PR pushes ### `.squad/team.md` Team definition file (required for squad directory to be recognized by PolyPilot's squad discovery). --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 18ce5b6 commit a793928

4 files changed

Lines changed: 195 additions & 0 deletions

File tree

.squad/decisions.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# PR Review Squad — Shared Decisions
2+
3+
These rules apply to every worker on every PR fix task. Deviating from them causes commits landing on the wrong remote or history rewrites that break collaborators.
4+
5+
## Push Safety
6+
7+
1. **NEVER force push** — no `--force`, `--force-with-lease`, or any force variant. Force pushing after a rebase is what caused wrong-remote pushes in the first place.
8+
9+
2. **ALWAYS use `gh pr checkout <N>`** to check out a PR branch — never `git fetch origin pull/<N>/head:pr-<N>`. The `gh` tool sets the branch tracking to the correct remote (fork or origin) automatically. A manually fetched branch has no tracking and `git push` will default to `origin`, silently pushing to the wrong repo.
10+
11+
3. **ALWAYS integrate with `git merge origin/main`** — never `git rebase origin/main`. Merge adds a merge commit (no history rewrite, no force push needed).
12+
13+
4. **ALWAYS verify the push target before pushing**:
14+
```bash
15+
gh pr view <N> --json headRepositoryOwner,headRefName \
16+
--jq '"Expected: " + .headRepositoryOwner.login + "/" + .headRefName'
17+
git config branch.$(git branch --show-current).remote
18+
```
19+
These must agree. If they don't, something is wrong — stop and investigate.
20+
21+
## Review Workflow
22+
23+
5. When reviewing only (no fix), use `gh pr diff <N>` — never check out the branch.
24+
25+
6. Consensus filter: include a finding in the final report only if flagged by 2+ of the 5 sub-agent models.
26+
27+
7. Do not comment on style, naming, or formatting. Flag only: bugs, data loss, race conditions, security issues, logic errors.

.squad/push-to-pr.sh

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/env bash
2+
# push-to-pr.sh — Safe push helper for PR review workers
3+
#
4+
# Usage: .squad/push-to-pr.sh <PR-number>
5+
#
6+
# This script:
7+
# 1. Reads PR metadata to find the correct remote and branch
8+
# 2. Verifies the current branch matches the PR branch
9+
# 3. Pushes to the correct remote (handles forks transparently)
10+
# 4. Verifies the push landed by comparing local and remote HEADs
11+
12+
set -euo pipefail
13+
14+
PR_NUMBER="${1:?Usage: push-to-pr.sh <PR-number>}"
15+
16+
echo "==> Fetching PR #${PR_NUMBER} metadata..."
17+
PR_JSON=$(gh pr view "$PR_NUMBER" --json headRefName,headRepositoryOwner,headRepository)
18+
BRANCH=$(echo "$PR_JSON" | jq -r '.headRefName')
19+
OWNER=$(echo "$PR_JSON" | jq -r '.headRepositoryOwner.login')
20+
REPO=$(echo "$PR_JSON" | jq -r '.headRepository.name')
21+
22+
echo " PR branch: ${BRANCH}"
23+
echo " PR owner: ${OWNER}"
24+
echo " PR repo: ${REPO}"
25+
26+
# Verify current branch matches the PR branch
27+
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
28+
if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then
29+
echo "ERROR: Current branch '${CURRENT_BRANCH}' does not match PR branch '${BRANCH}'"
30+
echo " Run: gh pr checkout ${PR_NUMBER}"
31+
exit 1
32+
fi
33+
34+
# Find the remote that points to owner/repo
35+
# gh pr checkout registers the fork owner's login as the remote name
36+
REMOTE=$(git remote -v | grep "${OWNER}/${REPO}" | head -1 | awk '{print $1}' || true)
37+
if [ -z "$REMOTE" ]; then
38+
echo "ERROR: No remote found matching ${OWNER}/${REPO}"
39+
echo "Available remotes:"
40+
git remote -v
41+
exit 1
42+
fi
43+
44+
echo "==> Pushing to remote '${REMOTE}' (${OWNER}/${REPO}), branch '${BRANCH}'..."
45+
git push "$REMOTE" HEAD:"$BRANCH"
46+
47+
# Verify push succeeded by comparing SHAs
48+
LOCAL_SHA=$(git rev-parse HEAD)
49+
REMOTE_SHA=$(git ls-remote "$REMOTE" "refs/heads/${BRANCH}" | awk '{print $1}')
50+
51+
if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then
52+
echo "✅ Push verified: ${LOCAL_SHA}"
53+
else
54+
echo "❌ Push verification failed!"
55+
echo " Local: ${LOCAL_SHA}"
56+
echo " Remote: ${REMOTE_SHA}"
57+
exit 1
58+
fi

.squad/routing.md

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# PR Review Squad — Work Routing
2+
3+
## Fix Process (when told to fix a PR)
4+
5+
> **Critical:** Follow this process exactly. Deviating — especially using rebase or force push — causes commits to land on the wrong remote.
6+
7+
### 1. Check out the PR branch
8+
```bash
9+
gh pr checkout <number>
10+
```
11+
This sets the branch tracking to the correct remote automatically (fork or origin).
12+
**Never** use `git fetch origin pull/<N>/head:...` — that creates a branch with no tracking.
13+
14+
> **Worktree conflict?** If `gh pr checkout` fails with "already checked out at...", run:
15+
> ```bash
16+
> git worktree list # find which worktree has the branch
17+
> git worktree remove <path> # remove stale worktree if safe, OR
18+
> gh pr checkout <number> -b pr-<number>-fix # use a unique local branch name
19+
> ```
20+
21+
### 2. Integrate with main (MERGE, not rebase)
22+
```bash
23+
git fetch origin main
24+
git merge origin/main
25+
```
26+
**Never** use `git rebase origin/main`. Merge adds a merge commit; no force push needed.
27+
If there are conflicts, resolve them, then `git add <files> && git merge --continue`.
28+
29+
### 3. Make the fix
30+
- Use the `edit` tool for file changes, never `sed`
31+
- Make minimal, surgical changes
32+
33+
### 4. Run tests
34+
Discover and run the repo's test suite. Look for test projects, Makefiles, CI scripts, or package.json test scripts. Run them and verify only pre-existing failures remain.
35+
36+
### 5. Commit
37+
```bash
38+
git add <specific-files> # Never git add -A blindly
39+
git commit -m "fix: <description>
40+
41+
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>"
42+
```
43+
44+
### 6. Push to the correct remote
45+
46+
**Always verify the push target before pushing:**
47+
```bash
48+
# Get expected owner/branch from the PR
49+
gh pr view <N> --json headRepositoryOwner,headRefName \
50+
--jq '"Expected: " + .headRepositoryOwner.login + "/" + .headRefName'
51+
52+
# Confirm branch tracking matches
53+
git config branch.$(git branch --show-current).remote
54+
```
55+
These must agree. If they don't, stop and investigate before pushing.
56+
57+
Once verified:
58+
```bash
59+
git push
60+
```
61+
`gh pr checkout` sets branch tracking correctly, so bare `git push` lands on the right remote.
62+
63+
**If `git push` fails** (e.g., tracking not set up correctly), push explicitly using the owner's remote.
64+
`gh pr checkout` registers the fork owner's GitHub login as a named remote — use it directly:
65+
```bash
66+
# Discover the owner's remote name
67+
OWNER=$(gh pr view <N> --json headRepositoryOwner --jq '.headRepositoryOwner.login')
68+
BRANCH=$(gh pr view <N> --json headRefName --jq '.headRefName')
69+
git remote -v | grep "$OWNER" # confirm remote exists
70+
71+
git push "$OWNER" HEAD:"$BRANCH"
72+
```
73+
Alternatively, use `.squad/push-to-pr.sh <N>` which automates the above.
74+
75+
### 7. Verify the push landed
76+
```bash
77+
gh pr view <N> --json commits --jq '.commits[-1].messageHeadline'
78+
```
79+
The last commit headline should match your fix commit message.
80+
81+
### 8. Re-review
82+
Dispatch 5 parallel sub-agent reviews with the updated diff (include previous findings for status tracking).
83+
84+
---
85+
86+
## Review Process (no fix)
87+
88+
Use `gh pr diff <N>`**never** check out the branch for review-only tasks.
89+
90+
Dispatch 5 parallel reviews:
91+
- claude-opus-4.6
92+
- claude-opus-4.6
93+
- claude-sonnet-4.6
94+
- gemini-3-pro-preview
95+
- gpt-5.3-codex
96+
97+
Synthesize with 2+ model consensus filter.
98+
99+
---
100+
101+
## Why `gh pr checkout` + merge beats manual fetch + rebase
102+
103+
`gh pr checkout` reads PR metadata and configures the branch to track the correct remote (fork or origin). Bare `git fetch pull/<N>/head:...` creates a local branch with no upstream — `git push` then defaults to `origin`, silently pushing to the base repository instead of the author's fork.

.squad/team.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# PR Review Squad
2+
3+
Workers that review and fix pull requests using multi-model consensus review.
4+
5+
| Member | Role | Expertise |
6+
|--------|------|-----------|
7+
| reviewer | PR Reviewer | Multi-model dispatch, consensus synthesis, bug detection |

0 commit comments

Comments
 (0)