diff --git a/.squad/decisions.md b/.squad/decisions.md new file mode 100644 index 00000000..cd8b21a2 --- /dev/null +++ b/.squad/decisions.md @@ -0,0 +1,27 @@ +# 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. + +## Review Workflow + +5. When reviewing only (no fix), use `gh pr diff ` — never check out the branch. + +6. Consensus filter: include a finding in the final report only if flagged by 2+ of the 5 sub-agent models. + +7. Do not comment on style, naming, or formatting. Flag only: bugs, data loss, race conditions, security issues, logic errors. 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 new file mode 100644 index 00000000..73228b8a --- /dev/null +++ b/.squad/routing.md @@ -0,0 +1,103 @@ +# 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. + +> **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 +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 +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 +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 + +**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** (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 +# 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" +``` +Alternatively, use `.squad/push-to-pr.sh ` which automates the above. + +### 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 + +`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 new file mode 100644 index 00000000..9dc5ceaa --- /dev/null +++ b/.squad/team.md @@ -0,0 +1,7 @@ +# PR Review Squad + +Workers that review and fix pull requests using multi-model consensus review. + +| Member | Role | Expertise | +|--------|------|-----------| +| reviewer | PR Reviewer | Multi-model dispatch, consensus synthesis, bug detection |