Skip to content

Document the stacked-PR workflow (Git Town) in CLAUDE.md #40

Description

@thecodedrift

We've run several stacked-PR efforts (the telemetry restructure, local-rule-routing) and hit avoidable footguns each time. Capture the workflow and the hard-won learnings in CLAUDE.md so both agents and humans handle stacks consistently.

Ask

Add a "Stacked PRs with Git Town" section to CLAUDE.md covering the items below.

Learnings to capture

Landing a stack: merge down, then one merge to main

Branch protection lives on main only (Validate required, strict_up_to_date: true, 0 required reviews). Merging each PR down into its parent branch (tip → bottom) is unprotected and instant, so:

  • Merge #tip into its parent's branch, then that into the next parent, … down to the bottom branch (which targets main). The bottom branch accumulates the whole stack.
  • Bring the bottom branch up to date with main, let Validate pass, then do the single protected merge to main.
  • Result: one CI cycle instead of N, and every PR gets a real Merged badge (not "closed/absorbed").

Never --delete-branch mid-stack

gh pr merge <n> --delete-branch on a stacked PR closes the child PR (its base branch vanished) instead of retargeting it. Leave branches in place during the stack; clean them up only after the whole stack has landed.

Use merge-commit, not squash, for a stack

Stacked branches share commits (each child contains its ancestors). --merge keeps children clean; squash rewrites the parent into a new commit the children don't have, forcing a git town sync reconciliation between every merge and inviting phantom conflicts. (Note: git town ship's default strategy can be squash — prefer explicit gh pr merge --merge.)

Recovery if a child PR gets closed by base-branch deletion

  1. Restore the deleted base branch ref at the merge commit's second parent:
    gh api --method POST repos/<owner>/<repo>/git/refs -f ref=refs/heads/<branch> -f sha=$(git rev-parse origin/main^2)
  2. Reopen the child via REST (GraphQL gh pr reopen fails on the Projects-classic deprecation): gh api --method PATCH repos/<owner>/<repo>/pulls/<n> -f state=open
  3. Retarget it: gh pr edit <n> --base main (only works once it's open).

Other gotchas

  • Projects-classic deprecation breaks several GraphQL-backed gh commands (gh pr reopen, git-town's gh connector updating proposals). Workarounds: REST API for PR state changes; git-town api connector (GITHUB_TOKEN) for proposals.
  • gh pr update-branch may not exist in the installed gh; update locally (git merge origin/main on the up-to-date remote branch) and push.
  • Stack-aware OpenSpec archive check (pr-check-openspec.yml) skips on non-tip PRs and runs on the tip; "tip" recomputes as branches merge, so it lands green when the archiving PR reaches main.
  • git town sync --all propagates fixes up the stack and prunes; run it after the stack lands to clean local branches.

Source

These come from the telemetry stack landing on 2026-06-14 (PRs #32#37) and the local-rule-routing stack review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions