feat: ez move restructures the GitHub native stack it moves within - #155
Merged
Conversation
`ez move --onto <parent>` rebased locally, updated ez's metadata, then called `update_pr_base`. For a PR inside a GitHub native stack that call is rejected — "Cannot change the base branch because the pull request is part of a stack" — and the failure was swallowed into a warning while the command still printed "Moved" and emitted a receipt with no remote status at all. Local and ez topology were correct; GitHub's was stale, and nothing said so. Inserting a PR into the middle of an existing native stack is only supported by dissolving and recreating the stack. ez already knows how — `ez sync --repair-native-stack` does exactly that via `repair_native_stack_exact` — so `ez move` now does it too. - Detection happens before any local mutation: `detect_native_stack` looks the PR's stack up first, so the decision to restructure is made with full knowledge rather than discovered from a failed edit afterwards. - A PR in a native stack skips `update_pr_base` entirely. That edit was never going to succeed, and the restructure is what moves the base. - The restructure runs only when the branch was actually in a stack. A move that involves no native stack has no business reconciling every other chain in the repo, and doing so would make `ez move` fail on installations that have nothing to do with this move. - Receipts split local from remote. The local receipt reports `local: "moved"`, `restacked`, `pr_base` (`updated` | `deferred_to_native_stack` | `failed` with the reason | null), and the stack the branch came from. The restructure emits its own receipts under `cmd: "move"`, and a failure exits 2 *after* the local receipt, so an agent can tell exactly how far the move got. Sync's native-stack reconciliation moves to `cmd/native_stack::reconcile_stacks` so both commands restructure identically instead of diverging. Costs one `repo view` round-trip per move on a branch with a PR — the probe that makes the detection possible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the gap reported from a real stack:
ez move --onto <parent>rebased locally and updated ez's metadata, then calledupdate_pr_base, which GitHub rejected with "Cannot change the base branch because the pull request is part of a stack". The failure became aui::warn, the command still printed Moved, and the receipt carried no remote status at all. Local and ez topology were right; GitHub's was stale, and nothing said so. Recovering meantgh stack unstack+gh stack linkby hand.What changed
Inserting a PR into the middle of an existing native stack is only supported by dissolving and recreating the stack — and ez already knows how, via
repair_native_stack_exactbehindez sync --repair-native-stack.ez movenow performs that restructure itself.Detection before mutation.
detect_native_stacklooks the PR's stack up before anything local is touched, so the decision is made with full knowledge rather than discovered from a failed edit after the fact. A lookup error is non-fatal —ez moveis primarily a local operation and shouldn't refuse to work because GitHub is unreachable.No more doomed base edit. A PR in a native stack skips
update_pr_baseentirely; the restructure is what moves the base. That removes the misleading warning at its source.Scoped to moves that involve a stack. The restructure runs only when the branch was actually in one. An early version reconciled every chain in the repo unconditionally, which made
ez movefail on repos and GitHub installations with nothing to do with the move — caught bymove_force_allows_descendant_merge_linearization.Receipts split local from remote, which was the specific agent-facing complaint:
{"cmd":"move","branch":"feat/x","from":"main","onto":"feat/base", "local":"moved","restacked":1, "pr_base":"deferred_to_native_stack", "native_stack_before":{"number":88,"size":4}}pr_baseis one ofupdated,deferred_to_native_stack,{"status":"failed","detail":"..."}, or null when there's no PR. The restructure emits its own receipts undercmd: "move", and a restructure failure exits 2 after the local receipt — so an agent can always tell exactly how far the move got.Refactor
Sync's native-stack reconciliation moved to
cmd/native_stack::reconcile_stacks(state, cmd, retry_command, repair), soez syncandez moverestructure identically instead of drifting apart the way merge's restack loop did.Cost
One
repo viewround-trip per move on a branch with a PR — the probe that makes detection-before-mutation possible. Pinned inmove_warns_when_pr_base_update_fails_but_persists_local_move.493 unit tests + all 25 integration suites green, clippy clean.