feat: Move-Commit -UnderPRInStack (stack a commit under an existing PR) - #24
Draft
dkattan wants to merge 1 commit into
Draft
feat: Move-Commit -UnderPRInStack (stack a commit under an existing PR)#24dkattan wants to merge 1 commit into
dkattan wants to merge 1 commit into
Conversation
…existing PR New parameter set on Move-Commit that cherry-picks a commit onto a new lower branch (in a persistent, user-owned worktree), pushes it, opens a draft PR, repoints the source PR's base to the lower branch, and registers the GitHub-native stack. Non-destructive by default: the source branch is never rewritten or force-pushed (the moved commit leaves the source PR's diff because the diff becomes lower..source). -RemoveFromSource remains the opt-in rewrite path. Built on a new recorder plan core (op nodes as data with two interpreters: live execution + serialize-to-script), so strings are an output of serialization rather than hand-authored input — the "code in strings" smell from New-MoveCommitPlan. Additive only; Move-Commit's existing behavior is untouched (DefaultParameterSetName='Move'). - Recorder: New-PipelineOp/New-Pipeline + Invoke-GitOp/Invoke-GhOp/Invoke- StackOp/Invoke-GhApiOp builders (append, never execute), Invoke-Pipeline (live) and ConvertTo-PipelineScript (serialize), with per-op OnFailure/When/ Down and $runtime: token capture for runtime-resolved PR numbers. - gh/stacks machinery: Invoke-GhCommand/Invoke-StackCommand/Invoke-GhApi runners (mirror Invoke-GitQuery), Resolve-GitSplitPullRequestFromBranch, Find-GitSplitStaleStacks (hawkins-preview), New-GitStackProvider (gh-stack/graphite/none/auto). gh-stack registers via `gh stack link <pr> <pr>` (source-verified: submit requires local tracking state this flow never creates). - Flow: New-MoveCommitUnderPRPlan — discovery, pre-flight fail-fast, 3-way worktree detect-and-branch (persistent, never torn down), patch-id --stable cherry-pick idempotency, reactive D5 stale-stack unstack, base repoint, provider stack registration, Up/Down rollback buffer (D8 report-by-default, -RollbackOnFailure auto-plays). Tests: 285 Pester tests pass (243 original + 42 new), no regressions, zero PSScriptAnalyzer errors. D1-D9 design decisions settled via the Wayfinder approach and recorded in memory; patch-id and worktree-list primitives sanity-checked against real git before encoding. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.
Summary
Adds a
-UnderPRInStackparameter set toMove-Committhat automates the real-world workflow: "I have a commit in this PR I don't want bloating it — move that code to another branch/worktree/PR stacked UNDER this one, control draft status, then spawn another agent in that worktree."Non-destructive by default: the source branch is never rewritten or force-pushed. The moved commit leaves the source PR's diff purely because the diff becomes
lower..source(which excludes it).-RemoveFromSourceremains the opt-in rewrite path.What it does
Cherry-picks the commit onto a new lower branch (in a persistent, user-owned
-WorktreePathhanded to a second agent), pushes it, opens a draft PR, repoints the source PR's base to the lower branch, and registers the GitHub-native stack.Architecture: a new recorder plan core
The existing
New-MoveCommitPlanbuilds ~250 lines of PowerShell as string literals — hard to extend to a flow that's 60% liveghside-effects. This PR adds a recorder plan core where ops are data nodes with two interpreters (execute-live + serialize-to-script), so strings become an output of serialization, never an input. This is the tagless-final/command pattern (precedented in theory via Darcs, novel in git tooling).Move-Commit's existing behavior is untouched (DefaultParameterSetName='Move'). The recorder is used by the new parameter set only; retrofittingMove-Commitis a deferred follow-up.OnFailure, onefinally,Whenguards,Downrollback companions,$runtime:token capture. No general control-flow AST.Stack registration:
gh stack link, notgh stack submitSource-level verification of
gh-stackv0.0.8 showedgh stack submit --autofails in this flow (requires local.git/gh-stacktracking state we never create) and would re-touch the base from a divergent local model.gh stack link <lower-pr> <source-pr>needs no local state, computes bases from argument order, reuses existing PRs, and is a no-op confirmation when the base is already correct.Idempotent re-run + rollback (D8)
Because the default is COPY (no source rewrite), the whole flow is idempotently re-runnable — primary recovery is "re-run the command." Pre-flight fail-fast checks and per-step "already done?" resume checks are one mechanism used twice. Each op carries a
Down(undo) companion; on failure it reports the exact rollback commands by default, and-RollbackOnFailureauto-plays them. The cherry-pick step usesgit patch-id --stableto skip when the patch is already applied (sanity-checked against real git).Test plan
$runtime:capture; gh/stacks runners, branch→PR resolution, stale-stack discovery, provider dispatch; the under-stack flow's parameter set, zero-arg ergonomics, the persistent-worktree guardrail (noworktree remove), patch-id idempotency, 3-way worktree detection, D5 stale-unstack ordering, D7 draft/title, and D8 failure reporting + rollback buffer.Design provenance
The 9 design decisions (D1–D9) were settled via the Wayfinder approach — charted as tickets, resolved one at a time, with the two load-bearing primitives (
git patch-id --stable,git worktree list --porcelain) sanity-checked against real git before encoding. Key decisions recorded in repo memory.Follow-ups (deferred by design)
Move-Commit/New-MoveCommitPlanonto the recorder.--continue/--abort(only if transient failures prove frequent in practice).🤖 Generated with Claude Code