From 7807b28e9b771df6fbe838824275b5e977f38e29 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 5 Mar 2026 10:52:03 -0600 Subject: [PATCH 1/3] Add safe push workflow for PR review workers Root cause of wrong-remote pushes: - Workers used 'git fetch origin pull//head:pr-' which creates a branch with NO tracking info. Subsequent 'git push' defaulted to origin, silently pushing to PureWeen/PolyPilot even for fork PRs. - Workers also used 'git rebase + --force-with-lease' which is unnecessary when using merge. Fix: - .squad/routing.md: updated Fix Process to use 'gh pr checkout ' (sets correct tracking) and 'git merge origin/main' (no force push needed) - .squad/decisions.md: explicit rules against force push and manual fetch - push-to-pr.sh: script that derives correct remote from gh pr view metadata and pushes without --force, with pre-push safety checks Verified: 'gh pr checkout' correctly sets tracking to fork remote for PR #280 (vitek-karas) and to origin for same-repo PRs like #247 (PureWeen). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/decisions.md | 29 +++++++++++++ .squad/routing.md | 87 +++++++++++++++++++++++++++++++++++++ .squad/team.md | 7 +++ push-to-pr.sh | 103 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 226 insertions(+) create mode 100644 .squad/decisions.md create mode 100644 .squad/routing.md create mode 100644 .squad/team.md create mode 100755 push-to-pr.sh diff --git a/.squad/decisions.md b/.squad/decisions.md new file mode 100644 index 00000000..0c230af2 --- /dev/null +++ b/.squad/decisions.md @@ -0,0 +1,29 @@ +# PR Review Squad — Shared Decisions + +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. + +## Push Safety + +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. + +2. **ALWAYS use `gh pr checkout `** to check out a PR branch — never `git fetch origin pull//head:pr-`. 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. + +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). + +4. **ALWAYS verify the push target before pushing**: + ```bash + gh pr view --json headRepositoryOwner,headRefName \ + --jq '"Expected: " + .headRepositoryOwner.login + "/" + .headRefName' + git config branch.$(git branch --show-current).remote + ``` + These must agree. If they don't, something is wrong — stop and investigate. + +5. **Use `./push-to-pr.sh `** for all PR pushes. It derives the correct remote from `gh pr view` metadata and refuses to push if the target doesn't match. + +## Review Workflow + +6. When reviewing only (no fix), use `gh pr diff ` — never check out the branch. + +7. Consensus filter: include a finding in the final report only if flagged by 2+ of the 5 sub-agent models. + +8. Do not comment on style, naming, or formatting. Flag only: bugs, data loss, race conditions, security issues, logic errors. diff --git a/.squad/routing.md b/.squad/routing.md new file mode 100644 index 00000000..b148f037 --- /dev/null +++ b/.squad/routing.md @@ -0,0 +1,87 @@ +# PR Review Squad — Work Routing + +## Fix Process (when told to fix a PR) + +> **Critical:** Follow this process exactly. Deviating — especially using rebase or force push — causes commits to land on the wrong remote. + +### 1. Check out the PR branch +```bash +gh pr checkout +``` +This sets the branch tracking to the correct remote automatically (fork or origin). +**Never** use `git fetch origin pull//head:...` — that creates a branch with no tracking. + +### 2. Integrate with main (MERGE, not rebase) +```bash +git fetch origin main +git merge origin/main +``` +**Never** use `git rebase origin/main`. Merge adds a merge commit; no force push needed. +If there are conflicts, resolve them, then `git add && git merge --continue`. + +### 3. Make the fix +- Use the `edit` tool for file changes, never `sed` +- Make minimal, surgical changes + +### 4. Run tests +```bash +cd PolyPilot.Tests && dotnet test +``` +Verify only pre-existing failures fail (e.g., `PopupThemeTests`). + +### 5. Commit +```bash +git add # Never git add -A blindly +git commit -m "fix: + +Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" +``` + +### 6. Push to the correct remote +```bash +./push-to-pr.sh +``` +This script derives the correct remote from `gh pr view` and pushes without `--force`. + +**Or manually:** +```bash +# Determine the correct remote +OWNER=$(gh pr view --json headRepositoryOwner --jq '.headRepositoryOwner.login') +BRANCH=$(gh pr view --json headRefName --jq '.headRefName') +git push $OWNER HEAD:$BRANCH +``` + +### 7. Verify the push landed +```bash +gh pr view --json commits --jq '.commits[-1].messageHeadline' +``` +The last commit headline should match your fix commit message. + +### 8. Re-review +Dispatch 5 parallel sub-agent reviews with the updated diff (include previous findings for status tracking). + +--- + +## Review Process (no fix) + +Use `gh pr diff ` — **never** check out the branch for review-only tasks. + +Dispatch 5 parallel reviews: +- claude-opus-4.6 +- claude-opus-4.6 +- claude-sonnet-4.6 +- gemini-3-pro-preview +- gpt-5.3-codex + +Synthesize with 2+ model consensus filter. + +--- + +## Why `gh pr checkout` + merge beats manual fetch + rebase + +| Approach | Tracking set? | Force push needed? | Risk | +|----------|--------------|-------------------|------| +| `gh pr checkout ` + `git merge` | ✅ Yes (correct remote) | ❌ No | Low | +| `git fetch pull//head:...` + `git rebase` | ❌ No (NONE) | ✅ Yes | Pushes to wrong remote | + +`gh pr checkout` reads the PR metadata and configures the branch to track the fork remote when applicable. Bare `git fetch pull//head:...` creates a detached local branch with no upstream — when you then `git push`, git picks `origin` as the default, silently pushing to PureWeen/PolyPilot instead of the author's fork. diff --git a/.squad/team.md b/.squad/team.md new file mode 100644 index 00000000..8c94be8d --- /dev/null +++ b/.squad/team.md @@ -0,0 +1,7 @@ +# PR Review Squad + +Workers that review and fix pull requests on PureWeen/PolyPilot. + +| Member | Role | Expertise | +|--------|------|-----------| +| reviewer | PR Reviewer | Multi-model dispatch, consensus synthesis, bug detection | diff --git a/push-to-pr.sh b/push-to-pr.sh new file mode 100755 index 00000000..aba0533b --- /dev/null +++ b/push-to-pr.sh @@ -0,0 +1,103 @@ +#!/usr/bin/env bash +# push-to-pr.sh — Push the current branch to the correct PR remote. +# +# Usage: ./push-to-pr.sh +# +# This script derives the correct remote and branch name from GitHub PR metadata +# so commits always land on the PR author's fork (not always origin). +# It NEVER uses --force or --force-with-lease. +# +# Why this exists: +# - `gh pr checkout ` correctly sets branch tracking (fork or origin) +# - Manual `git fetch pull//head:...` creates a branch with NO tracking +# - Without correct tracking, `git push` silently defaults to origin, +# pushing to PureWeen/PolyPilot even when the PR comes from a fork. + +set -euo pipefail + +if [[ $# -ne 1 ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +PR_NUMBER="$1" + +# Resolve PR metadata +echo "🔍 Resolving PR #${PR_NUMBER} metadata..." +PR_INFO=$(gh pr view "$PR_NUMBER" --json headRefName,headRepositoryOwner,headRepository \ + --jq '{owner: .headRepositoryOwner.login, branch: .headRefName, repo: .headRepository.name}') + +OWNER=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['owner'])") +BRANCH=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['branch'])") +REPO=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['repo'])") + +echo " PR owner: $OWNER" +echo " PR branch: $BRANCH" +echo " PR repo: $REPO" + +# Find the git remote whose URL contains the owner +REMOTE="" +while IFS= read -r line; do + REMOTE_NAME=$(echo "$line" | awk '{print $1}') + REMOTE_URL=$(echo "$line" | awk '{print $2}') + if echo "$REMOTE_URL" | grep -qi "github.com[/:]${OWNER}/"; then + REMOTE="$REMOTE_NAME" + break + fi +done < <(git remote -v | grep '(push)') + +if [[ -z "$REMOTE" ]]; then + echo "❌ ERROR: No git remote found for owner '$OWNER'." >&2 + echo " Available remotes:" >&2 + git remote -v | grep '(push)' >&2 + echo "" >&2 + echo " Add the remote first:" >&2 + echo " git remote add $OWNER https://github.com/$OWNER/$REPO.git" >&2 + exit 1 +fi + +echo " Resolved remote: $REMOTE" + +# Verify current branch matches the PR branch +CURRENT_BRANCH=$(git branch --show-current) +if [[ "$CURRENT_BRANCH" != "$BRANCH" ]]; then + echo "" >&2 + echo "⚠️ WARNING: Current branch '$CURRENT_BRANCH' does not match PR branch '$BRANCH'." >&2 + echo " Did you run: gh pr checkout $PR_NUMBER ?" >&2 + echo " Refusing to push — checkout the PR branch first." >&2 + exit 1 +fi + +# Safety check: ensure no unstaged changes +if ! git diff --quiet || ! git diff --cached --quiet; then + echo "❌ ERROR: You have uncommitted changes. Commit or stash them first." >&2 + exit 1 +fi + +# Show what will be pushed +echo "" +echo "📦 About to push:" +echo " $(git log --oneline -3 | head -3)" +echo "" +echo "🎯 Target: $REMOTE/$BRANCH (PR #$PR_NUMBER by $OWNER)" +echo "" + +# Push (no --force, no --force-with-lease) +echo "🚀 Pushing..." +git push "$REMOTE" "HEAD:${BRANCH}" + +echo "" +echo "✅ Push complete. Verifying..." +git fetch "$REMOTE" "$BRANCH" --quiet + +REMOTE_HEAD=$(git log --oneline "${REMOTE}/${BRANCH}" -1 2>/dev/null || echo "UNKNOWN") +LOCAL_HEAD=$(git log --oneline HEAD -1) +echo " Local HEAD: $LOCAL_HEAD" +echo " Remote HEAD: $REMOTE_HEAD" + +if [[ "$REMOTE_HEAD" == "$LOCAL_HEAD" ]]; then + echo "✅ Verified: remote matches local HEAD." +else + echo "⚠️ Remote HEAD does not match local HEAD — check for concurrent pushes." + exit 1 +fi From fb12482888fe4fc85dedfb398ba5225022fa1fd7 Mon Sep 17 00:00:00 2001 From: Shane Neuville Date: Thu, 5 Mar 2026 14:43:56 -0600 Subject: [PATCH 2/3] refactor: make squad definition generic and drop push-to-pr.sh The push safety rules in decisions.md are sufficient. Squad files are now repo-agnostic so they can be reused across any repository. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/decisions.md | 8 ++-- .squad/routing.md | 24 +++-------- .squad/team.md | 2 +- push-to-pr.sh | 103 -------------------------------------------- 4 files changed, 11 insertions(+), 126 deletions(-) delete mode 100755 push-to-pr.sh diff --git a/.squad/decisions.md b/.squad/decisions.md index 0c230af2..cd8b21a2 100644 --- a/.squad/decisions.md +++ b/.squad/decisions.md @@ -18,12 +18,10 @@ These rules apply to every worker on every PR fix task. Deviating from them caus ``` These must agree. If they don't, something is wrong — stop and investigate. -5. **Use `./push-to-pr.sh `** for all PR pushes. It derives the correct remote from `gh pr view` metadata and refuses to push if the target doesn't match. - ## Review Workflow -6. When reviewing only (no fix), use `gh pr diff ` — never check out the branch. +5. When reviewing only (no fix), use `gh pr diff ` — never check out the branch. -7. Consensus filter: include a finding in the final report only if flagged by 2+ of the 5 sub-agent models. +6. Consensus filter: include a finding in the final report only if flagged by 2+ of the 5 sub-agent models. -8. Do not comment on style, naming, or formatting. Flag only: bugs, data loss, race conditions, security issues, logic errors. +7. Do not comment on style, naming, or formatting. Flag only: bugs, data loss, race conditions, security issues, logic errors. diff --git a/.squad/routing.md b/.squad/routing.md index b148f037..a461687d 100644 --- a/.squad/routing.md +++ b/.squad/routing.md @@ -24,10 +24,7 @@ If there are conflicts, resolve them, then `git add && git merge --conti - Make minimal, surgical changes ### 4. Run tests -```bash -cd PolyPilot.Tests && dotnet test -``` -Verify only pre-existing failures fail (e.g., `PopupThemeTests`). +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. ### 5. Commit ```bash @@ -39,17 +36,15 @@ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" ### 6. Push to the correct remote ```bash -./push-to-pr.sh +git push ``` -This script derives the correct remote from `gh pr view` and pushes without `--force`. +`gh pr checkout` sets branch tracking correctly, so bare `git push` lands on the right remote. -**Or manually:** +If `git push` fails, verify the remote first: ```bash -# Determine the correct remote -OWNER=$(gh pr view --json headRepositoryOwner --jq '.headRepositoryOwner.login') -BRANCH=$(gh pr view --json headRefName --jq '.headRefName') -git push $OWNER HEAD:$BRANCH +gh pr view --json headRepositoryOwner,headRefName ``` +Then push explicitly: `git push HEAD:` ### 7. Verify the push landed ```bash @@ -79,9 +74,4 @@ Synthesize with 2+ model consensus filter. ## Why `gh pr checkout` + merge beats manual fetch + rebase -| Approach | Tracking set? | Force push needed? | Risk | -|----------|--------------|-------------------|------| -| `gh pr checkout ` + `git merge` | ✅ Yes (correct remote) | ❌ No | Low | -| `git fetch pull//head:...` + `git rebase` | ❌ No (NONE) | ✅ Yes | Pushes to wrong remote | - -`gh pr checkout` reads the PR metadata and configures the branch to track the fork remote when applicable. Bare `git fetch pull//head:...` creates a detached local branch with no upstream — when you then `git push`, git picks `origin` as the default, silently pushing to PureWeen/PolyPilot instead of the author's fork. +`gh pr checkout` reads PR metadata and configures the branch to track the correct remote (fork or origin). Bare `git fetch pull//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. diff --git a/.squad/team.md b/.squad/team.md index 8c94be8d..9dc5ceaa 100644 --- a/.squad/team.md +++ b/.squad/team.md @@ -1,6 +1,6 @@ # PR Review Squad -Workers that review and fix pull requests on PureWeen/PolyPilot. +Workers that review and fix pull requests using multi-model consensus review. | Member | Role | Expertise | |--------|------|-----------| diff --git a/push-to-pr.sh b/push-to-pr.sh deleted file mode 100755 index aba0533b..00000000 --- a/push-to-pr.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash -# push-to-pr.sh — Push the current branch to the correct PR remote. -# -# Usage: ./push-to-pr.sh -# -# This script derives the correct remote and branch name from GitHub PR metadata -# so commits always land on the PR author's fork (not always origin). -# It NEVER uses --force or --force-with-lease. -# -# Why this exists: -# - `gh pr checkout ` correctly sets branch tracking (fork or origin) -# - Manual `git fetch pull//head:...` creates a branch with NO tracking -# - Without correct tracking, `git push` silently defaults to origin, -# pushing to PureWeen/PolyPilot even when the PR comes from a fork. - -set -euo pipefail - -if [[ $# -ne 1 ]]; then - echo "Usage: $0 " >&2 - exit 1 -fi - -PR_NUMBER="$1" - -# Resolve PR metadata -echo "🔍 Resolving PR #${PR_NUMBER} metadata..." -PR_INFO=$(gh pr view "$PR_NUMBER" --json headRefName,headRepositoryOwner,headRepository \ - --jq '{owner: .headRepositoryOwner.login, branch: .headRefName, repo: .headRepository.name}') - -OWNER=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['owner'])") -BRANCH=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['branch'])") -REPO=$(echo "$PR_INFO" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d['repo'])") - -echo " PR owner: $OWNER" -echo " PR branch: $BRANCH" -echo " PR repo: $REPO" - -# Find the git remote whose URL contains the owner -REMOTE="" -while IFS= read -r line; do - REMOTE_NAME=$(echo "$line" | awk '{print $1}') - REMOTE_URL=$(echo "$line" | awk '{print $2}') - if echo "$REMOTE_URL" | grep -qi "github.com[/:]${OWNER}/"; then - REMOTE="$REMOTE_NAME" - break - fi -done < <(git remote -v | grep '(push)') - -if [[ -z "$REMOTE" ]]; then - echo "❌ ERROR: No git remote found for owner '$OWNER'." >&2 - echo " Available remotes:" >&2 - git remote -v | grep '(push)' >&2 - echo "" >&2 - echo " Add the remote first:" >&2 - echo " git remote add $OWNER https://github.com/$OWNER/$REPO.git" >&2 - exit 1 -fi - -echo " Resolved remote: $REMOTE" - -# Verify current branch matches the PR branch -CURRENT_BRANCH=$(git branch --show-current) -if [[ "$CURRENT_BRANCH" != "$BRANCH" ]]; then - echo "" >&2 - echo "⚠️ WARNING: Current branch '$CURRENT_BRANCH' does not match PR branch '$BRANCH'." >&2 - echo " Did you run: gh pr checkout $PR_NUMBER ?" >&2 - echo " Refusing to push — checkout the PR branch first." >&2 - exit 1 -fi - -# Safety check: ensure no unstaged changes -if ! git diff --quiet || ! git diff --cached --quiet; then - echo "❌ ERROR: You have uncommitted changes. Commit or stash them first." >&2 - exit 1 -fi - -# Show what will be pushed -echo "" -echo "📦 About to push:" -echo " $(git log --oneline -3 | head -3)" -echo "" -echo "🎯 Target: $REMOTE/$BRANCH (PR #$PR_NUMBER by $OWNER)" -echo "" - -# Push (no --force, no --force-with-lease) -echo "🚀 Pushing..." -git push "$REMOTE" "HEAD:${BRANCH}" - -echo "" -echo "✅ Push complete. Verifying..." -git fetch "$REMOTE" "$BRANCH" --quiet - -REMOTE_HEAD=$(git log --oneline "${REMOTE}/${BRANCH}" -1 2>/dev/null || echo "UNKNOWN") -LOCAL_HEAD=$(git log --oneline HEAD -1) -echo " Local HEAD: $LOCAL_HEAD" -echo " Remote HEAD: $REMOTE_HEAD" - -if [[ "$REMOTE_HEAD" == "$LOCAL_HEAD" ]]; then - echo "✅ Verified: remote matches local HEAD." -else - echo "⚠️ Remote HEAD does not match local HEAD — check for concurrent pushes." - exit 1 -fi From d5338f70ad9b224ae254531aa348543f37863f3d Mon Sep 17 00:00:00 2001 From: "Shane Neuville (HE/HIM)" Date: Sun, 8 Mar 2026 18:26:47 -0500 Subject: [PATCH 3/3] fix: resolve push verification order, define owner-remote, add worktree recovery, add push script - Move pre-push verification before git push in routing.md step 6 to match decisions.md rule 4 (was contradictory: push-first vs verify-first) - Replace undefined placeholder with concrete discovery commands using gh pr view + git remote -v; document that gh pr checkout registers the fork owner's login as the remote name - Add worktree conflict recovery note to step 1 (gh pr checkout -b fallback) - Add .squad/push-to-pr.sh: safe push script that validates branch match, finds the correct remote, pushes, and verifies via SHA comparison Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .squad/push-to-pr.sh | 58 ++++++++++++++++++++++++++++++++++++++++++++ .squad/routing.md | 32 +++++++++++++++++++++--- 2 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 .squad/push-to-pr.sh diff --git a/.squad/push-to-pr.sh b/.squad/push-to-pr.sh new file mode 100644 index 00000000..3c44b02e --- /dev/null +++ b/.squad/push-to-pr.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +# push-to-pr.sh — Safe push helper for PR review workers +# +# Usage: .squad/push-to-pr.sh +# +# This script: +# 1. Reads PR metadata to find the correct remote and branch +# 2. Verifies the current branch matches the PR branch +# 3. Pushes to the correct remote (handles forks transparently) +# 4. Verifies the push landed by comparing local and remote HEADs + +set -euo pipefail + +PR_NUMBER="${1:?Usage: push-to-pr.sh }" + +echo "==> Fetching PR #${PR_NUMBER} metadata..." +PR_JSON=$(gh pr view "$PR_NUMBER" --json headRefName,headRepositoryOwner,headRepository) +BRANCH=$(echo "$PR_JSON" | jq -r '.headRefName') +OWNER=$(echo "$PR_JSON" | jq -r '.headRepositoryOwner.login') +REPO=$(echo "$PR_JSON" | jq -r '.headRepository.name') + +echo " PR branch: ${BRANCH}" +echo " PR owner: ${OWNER}" +echo " PR repo: ${REPO}" + +# Verify current branch matches the PR branch +CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD) +if [ "$CURRENT_BRANCH" != "$BRANCH" ]; then + echo "ERROR: Current branch '${CURRENT_BRANCH}' does not match PR branch '${BRANCH}'" + echo " Run: gh pr checkout ${PR_NUMBER}" + exit 1 +fi + +# Find the remote that points to owner/repo +# gh pr checkout registers the fork owner's login as the remote name +REMOTE=$(git remote -v | grep "${OWNER}/${REPO}" | head -1 | awk '{print $1}' || true) +if [ -z "$REMOTE" ]; then + echo "ERROR: No remote found matching ${OWNER}/${REPO}" + echo "Available remotes:" + git remote -v + exit 1 +fi + +echo "==> Pushing to remote '${REMOTE}' (${OWNER}/${REPO}), branch '${BRANCH}'..." +git push "$REMOTE" HEAD:"$BRANCH" + +# Verify push succeeded by comparing SHAs +LOCAL_SHA=$(git rev-parse HEAD) +REMOTE_SHA=$(git ls-remote "$REMOTE" "refs/heads/${BRANCH}" | awk '{print $1}') + +if [ "$LOCAL_SHA" = "$REMOTE_SHA" ]; then + echo "✅ Push verified: ${LOCAL_SHA}" +else + echo "❌ Push verification failed!" + echo " Local: ${LOCAL_SHA}" + echo " Remote: ${REMOTE_SHA}" + exit 1 +fi diff --git a/.squad/routing.md b/.squad/routing.md index a461687d..73228b8a 100644 --- a/.squad/routing.md +++ b/.squad/routing.md @@ -11,6 +11,13 @@ gh pr checkout This sets the branch tracking to the correct remote automatically (fork or origin). **Never** use `git fetch origin pull//head:...` — that creates a branch with no tracking. +> **Worktree conflict?** If `gh pr checkout` fails with "already checked out at...", run: +> ```bash +> git worktree list # find which worktree has the branch +> git worktree remove # remove stale worktree if safe, OR +> gh pr checkout -b pr--fix # use a unique local branch name +> ``` + ### 2. Integrate with main (MERGE, not rebase) ```bash git fetch origin main @@ -35,16 +42,35 @@ Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>" ``` ### 6. Push to the correct remote + +**Always verify the push target before pushing:** +```bash +# Get expected owner/branch from the PR +gh pr view --json headRepositoryOwner,headRefName \ + --jq '"Expected: " + .headRepositoryOwner.login + "/" + .headRefName' + +# Confirm branch tracking matches +git config branch.$(git branch --show-current).remote +``` +These must agree. If they don't, stop and investigate before pushing. + +Once verified: ```bash git push ``` `gh pr checkout` sets branch tracking correctly, so bare `git push` lands on the right remote. -If `git push` fails, verify the remote first: +**If `git push` fails** (e.g., tracking not set up correctly), push explicitly using the owner's remote. +`gh pr checkout` registers the fork owner's GitHub login as a named remote — use it directly: ```bash -gh pr view --json headRepositoryOwner,headRefName +# Discover the owner's remote name +OWNER=$(gh pr view --json headRepositoryOwner --jq '.headRepositoryOwner.login') +BRANCH=$(gh pr view --json headRefName --jq '.headRefName') +git remote -v | grep "$OWNER" # confirm remote exists + +git push "$OWNER" HEAD:"$BRANCH" ``` -Then push explicitly: `git push HEAD:` +Alternatively, use `.squad/push-to-pr.sh ` which automates the above. ### 7. Verify the push landed ```bash