# GitHub - Pull Request Workflow
Squash merges break git's merge bookkeeping. A deleted base branch closes every stacked child PR, and a new push invalidates review anchors. These procedures keep cleanup, stacking, and review reliable.
## Stacked PRs and squash merges
- Squash-merged branches never appear as merged to git: the tip never becomes an ancestor of `main`. Delete them with `git branch -D` once the remote ref shows `[gone]`.
- `git branch --merged` catches only true merge commits. Diff each tip against `main` to detect squash-merges, and categorize before you delete.
- `git fetch --prune` before local pruning confirms what truly merged into `origin/main`, so cleanup cannot delete unmerged work.
- Stacked PRs need every base branch in the same repository as the target. A cross-repo stack, fork head against upstream base, breaks because GitHub cannot resolve the shared history.
- `gh pr merge --delete-branch` on a stacked PR does not retarget the children. GitHub closes every PR whose base branch was deleted. Retarget remaining PRs to `main` first, merge without branch deletion, clean up last.
- Plan the rebase order from `gh pr list --repo <org/repo> --author <handle> --state open --json number,title,headRefName,baseRefName`.
- MERGEABLE compares each branch against `main` in isolation. Same-day PRs that write one shared file can all show clean while each is a divergent add/add. Mergeability says nothing about PR-to-PR overlap. The merge order decides where conflicts surface, and taking one side is silent data loss.
- Prove a union merge lost nothing: grep the merged file for a signature phrase from every source PR. If all hit, nothing dropped.
- After a parent squash-merge, `git rebase --onto main <old-parent-tip>` replays only the child's unique commits.
## Review threads and anchors
- Only `gh pr diff` defines a PR's scope. A merge-base diff pulls in already-merged commits from other PRs, and a divergent worktree yields phantom findings. The GitHub diff is authoritative for added files.
- Read PR-head code without a checkout: `git show <sha>:<path>` and `git diff <base> <head> -- <path>`.
- `refs/pull/<n>/head` exists on the base repository regardless of the source fork: `git fetch <remote> pull/<n>/head:review-pr-<n>` plus a scratch worktree.
- Read the thread before you draft: `pulls/<n>/reviews`, `pulls/<n>/comments`, `gh pr view --comments`. This removes findings maintainers already handled or resolved.
- A PR head can move mid-review and invalidate every anchor. Re-verify at the live head before posting and after every force push.
- `CHANGES_REQUESTED` with a last commit older than the last review means the author's turn. `mergeStateStatus: DIRTY` or `CONFLICTING` means rebase is the first blocker.
- Post reviews with `gh api repos/{owner}/{repo}/pulls/{n}/reviews --input payload.json` and `event: COMMENT`. The `--input` form preserves backticks and markdown where `-f` flags do not.
- A batched `COMMENT` review needs a non-empty top-level body and 422s when any `path`, `line`, or `side` anchor misses the diff. Post inline-only sets individually via `pulls/<n>/comments`.
- Anchor inlines at the live head: `commit_id`, `side: RIGHT`, `line` inside a hunk. The API silently drops out-of-hunk lines and rejects binary-file anchors. Verify every inline landed.
- Build payloads with jq `--arg body "$body"`, which escapes newlines where a raw heredoc through `jq --argjson` throws a parse error.
- Verify handles with `gh api users/<handle>` and the repo's MAINTAINERS or CODEOWNERS before tagging people in outward-facing issues. Near-identical spellings exist.
- Confirm a suspected bypass with a throwaway probe test at the PR head: run it, then delete it. It turns a suspicion into a confirmed finding.
## Reviewing versioned and duplicated code
- Copy-paste versioning hides its bugs in the divergence. Three byte-identical middleware copies differed by one dropped `runtime.SuppressHTTPClientInstrumentation(ctx)` line, so one version emitted a duplicate net/http span per call. CI stayed green because the only end-to-end test covered one version. The divergence surfaced from mechanically diffing the copies.
- Read prior maintainer rounds before you post. One finding was the deliberate opposite of an earlier round's request for tools-only aggregator packages, and the retraction is part of adjudication. The misleading error had a real cause in unchecked load errors.
- When the key files do not exist yet, such as a PR that adds stubs, standard diff review fails. Compare the stubs against the parallel implementations of the same pattern and the design intent.
- Near-duplicate implementations inherit each other's bugs. Two recompressors that differed only in the output writer type shared a goroutine-blocking bug. Unify the duplicates first, then fix once through the shared close path.
## How to apply
1. `git fetch --prune`, then list the stack with the `gh pr list` command above.
2. Retarget every child PR to `main` before you merge its parent, without `--delete-branch`.
3. Rebase a child after the parent squash-merge: `git rebase --onto main <old-parent-tip>`.
4. Categorize by tip diff, confirm `[gone]`, then `git branch -D`.
5. Read `pulls/<n>/reviews`, `pulls/<n>/comments`, `gh pr view --comments` before you draft.
6. Re-verify findings at the live head. Build the payload with jq `--arg`, post with `event: COMMENT`, then verify every inline landed.
## Observations
- [git] Squash-merged branches never appear as merged to git: the tip never becomes an ancestor of `main`. #branch-cleanup (source: agent sessions, 2026-02)
- [github] Merging a stacked PR with branch deletion closes every child PR. #stacked-prs (source: agent sessions, 2026-07)
- [github] The review API silently drops inline comments anchored outside diff hunks or on binary files. #github-api (source: agent sessions, 2026-10)
- [code-review] Mechanically diffing versioned copies surfaces divergence that reading one copy hides. #duplication (source: agent sessions, 2026-06)
## Relations
- relates_to [[resources/documentation/Git - Rebase, Stash, and Recovery]] (`git rebase --onto` after a squash-merge)
- relates_to [[resources/documentation/GitHub - gh CLI and API Usage]] (review posting via `gh api`)
- relates_to [[resources/documentation/Agents/Agents - Worktree Workflows and Pre-PR Review]] (scratch review worktrees)
## Sources
- Personal agent-session notes, February 2026 to November 2026