When a parent pull request is squash-merged, the child branch that was stacked
on it still carries the parent's original commits. Restacking the child onto
the new target requires an exclusive replay boundary (OLD_BASE): the last
commit the child inherited from the parent. OLD_BASE is not the target
merge-base, not the squash landing SHA, and not the apparent first
child commit — the graph's ordinary merge-base is only a topology fact, and the
squash commit is a landing record, not a boundary. Choosing the wrong boundary
either silently drops the first genuine child commit or replays already-landed
parent work back onto the target.
The rebase skill and its
squashed-parent reference
document how to establish this boundary and audit a replay against it. See also
the Stacked pull requests and
Entity-aware Git merges sections above for the
surrounding stack and merge-driver context.
Planning a restack
For a confirmed squash-merged parent with a known pull request identity, the
skill bundles a planner, skills/rebase/scripts/plan_restack.py. It is a
Cyclopts command-line tool run with uv run, and requires Python 3.13 or later
and an authenticated gh:
In the common case, no maintained refs/stack-bases/<branch> receipt exists
and the parent head is still inherited, so --boundary-ref is left off
entirely:
uv run skills/rebase/scripts/plan_restack.py . \
--branch "$BRANCH" --target-ref "$TARGET_REF" \
--parent-repository "$PARENT_REPOSITORY" --parent-pr "$PARENT_PR"
When a maintained refs/stack-bases/<branch> receipt exists, pass it via
--boundary-ref:
uv run skills/rebase/scripts/plan_restack.py . \
--branch "$BRANCH" --target-ref "$TARGET_REF" \
--parent-repository "$PARENT_REPOSITORY" --parent-pr "$PARENT_PR" \
--boundary-ref "$BOUNDARY_REF"
The positional argument is the repository path (. for the current checkout).
--boundary-ref is optional: either pass it with a receipt's value, or leave
the whole flag off. An empty value is not the same as omitting it — the planner
rejects an empty --boundary-ref rather than treating it as absent.
The planner leaves branches, tracking refs, the index, and the working tree
unchanged: it never rebases, pushes, or prunes. Discovery does write one thing,
deliberately: it fetches the parent pull request's head into a private
refs/agent-rebase/… evidence ref, retained for later review and recovery. The
read path, build_plan(), performs no ref writes and no network access at all.
A successful run prints a JSON plan to standard output with
status: review-required, or status: no-op-decision-required when the
computed range is empty. That status is a request for human or agent review; it
is never authorization to replay. The plan carries boundary_evidence (the
provenance of the chosen OLD_BASE), boundary_corroborated (whether
preserved parent history proves the boundary), evidence_ref (the private
fetch ref), commits (the exact ordered commit list the plan proposes to
replay), and rebase_argv (the exact proposed rebase command). A blocked run
prints a status: blocked JSON object on standard error and exits with status
2; treat this, and any gh or fetch error, as a stop, not as a negative
ancestry result.
What the operator still owns
The planner narrows the evidence gathering; it does not discharge review. The operator (human or supervising agent) still owns:
- Confirming the parent relationship and that the parent pull request was actually squash-merged, not merged by another method.
- Judging the freshness of any boundary receipt and the ownership of each commit in the proposed range.
- Applying worktree, merge-driver and acceptance policy, including the Weave driver-selection checks and the repository's formatting, lint, type and test gates, before and after replay.
Do not use `gh stack sync --prune` for discovery
gh stack sync --prune must not be used as a way to discover the replay
boundary. It can rebase, push and prune branches — destroying recovery evidence
— before any proposed range has been reviewed. A clean gh stack sync exit is
a safety net against a diverged remote, not proof of replay ownership: it says
nothing about which commits each layer owns, so it cannot by itself confirm
that no inherited parent commit remains in a cascading rebase. Establish and
review the boundary evidence first.
Host restriction
The bundled planner's fetch currently targets github.com explicitly, even
when the child branch lives in a fork. Other GitHub hosts (for example GitHub
Enterprise Server) are not supported by the planner and need the separately
documented recovery path in the
squashed-parent reference.