fix(pull-sdlc): clean no-op sync, honest -WhatIf, opt-in auto-merge#225
fix(pull-sdlc): clean no-op sync, honest -WhatIf, opt-in auto-merge#225MarkMichaelis wants to merge 2 commits into
Conversation
A no-op sync on main bumped the syncedAt timestamp, dirtying the tree, producing an empty sync commit and opening an empty PR. -WhatIf said "nothing to sync" while a real run did visible work, and scaffolded starter files landed in the throwaway worktree instead of the user branch. - RC1: gate the sync-state write so an already-current 0-op run touches nothing (no commit, no push, no PR). - RC2: honest -WhatIf -- preview the scaffold files and the PR/merge a real run would perform. - RC3: scaffold consumer-owned files into the invoking directory, not the scratch worktree, via ScaffoldTargetRoot. - RC4: optional squash-merge of the sync PR through the standard -Confirm path (Invoke-SyncPRMerge); default opens the PR without merging, -Confirm:$false auto-merges and updates local main, a blocked merge leaves the PR open for manual action. Closes #224 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…, WhatIf accuracy Follow-up to the RC1-RC4 fix, from an independent code review: - Delete the local scratch branch after a successful squash-merge. Removing the worktree alone left 'chore/sdlc-sync' at pre-squash history, which the next run reused (re-opening a PR with stale content). - Check git exit codes after the post-merge fast-forward and worktree cleanup; warn with remediation and return $false when the PR merged but the local update did not complete, instead of silently reporting success. - Make the -WhatIf preview mirror the real run's decision inputs: it now accounts for -CommitOnMain (direct-commit vs PR) and treats a stale/missing sync-state as a change even when there are zero file ops. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
@copilot review |
Code review complete. No issues found that require changes. Here's the summary: Test run: 245 passed, 1 failed, 4 skipped (250 total). The 1 failure ( RC1 -- no-op gate ( RC2 -- honest RC3 -- scaffold target: RC4 --
No actionable changes needed. |
There was a problem hiding this comment.
Pull request overview
This PR adjusts Pull-SDLC.ai.ps1 to behave like a clean “update me” command by eliminating no-op sync commits/PRs, making -WhatIf reflect real outcomes, ensuring scaffolds land in the invoking working tree (not the scratch worktree), and adding an opt-in PR squash-merge flow using standard -Confirm / ShouldProcess semantics.
Changes:
- Gate
.sdlc-ai-sync.jsonupdates so a truly no-op rerun doesn’t rewrite the state file (and therefore doesn’t create empty commits/PRs). - Add
Invoke-SyncPRMergeand thread an internal merge intent through auto-worktree mode to optionally squash-merge the sync PR. - Improve
-WhatIfoutput to preview scaffolded files and PR/merge outcomes; scaffold consumer-owned files into the invoking directory in auto-worktree mode.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| Pull-SDLC.ai.ps1 | Adds PR squash-merge helper, gates sync-state writes to prevent no-op commits, previews full outcomes under -WhatIf, and redirects scaffolding into the invoking directory in auto-worktree mode. |
| Pull-SDLC.ai.Tests.ps1 | Adds/updates Pester coverage for no-op sync behavior, scaffold location, auto-worktree merge control, and honest -WhatIf messaging. |
| git -C $RepoRoot worktree prune 2>&1 | Write-Information | ||
| # -D (not -d): after a squash-merge the scratch branch is not an ancestor of | ||
| # $ProtectedBranch, so git does not consider it "merged"; force-delete it. | ||
| git -C $RepoRoot branch -D $SyncBranch 2>&1 | Write-Information |
| if (-not $PSCmdlet.ShouldProcess("$ProtectedBranch (PR $PrRef)", "Squash-merge and delete branch '$SyncBranch'")) { | ||
| Write-Information "PR opened but not merged: $PrUrl" | ||
| return $false | ||
| } |
| else { | ||
| Write-Information "PR opened but not merged. Rerun with -Confirm:`$false to squash-merge it into '$ProtectedBranch' automatically." | ||
| } |
Summary
Makes
Pull-SDLC.ai.ps1behave as a clean "update me" command. Fixes the confusing behavior where a no-op sync onmainstill opened an empty PR and-WhatIfdisagreed with a real run.Root causes fixed
Set-SdlcSyncStatealways rewrote thesyncedAttimestamp, dirtying the tree even when 0 ops applied, which produced an empty sync commit and (in auto-worktree mode) an empty PR. The state write is now gated: an already-current 0-op run touches nothing -- no commit, no push, no PR.-WhatIf.-WhatIfnow previews the consumer-owned starter files that would be scaffolded and states the PR/merge outcome a real run would perform, instead of printing only the file-diff ops..worktrees/sdlc-synctree. Threaded via a new internalScaffoldTargetRootparameter.Invoke-SyncPRMergehelper can squash-merge the sync PR through the standard-Confirmmechanism. Default (no-Confirm) opens the PR without merging;-Confirm:$falseauto-squash-merges and fast-forwards localmain; a blocked merge leaves the PR open for manual action.Behavior contract
mainmain(not merged)... -Confirm:$falseonmainmain-WhatIfMerge control uses the standard
-Confirm/-WhatIf/ShouldProcessmechanism -- no new user-facing switches.Testing
Invoke-Pester -Path .\Pull-SDLC.ai.Tests.ps1-- 248 passed, 0 failed. Added behavior-first tests for each root cause (no-op-push-nothing, scaffold-into-invoking-dir,Invoke-SyncPRMergemerge/blocked/declined, auto-worktree merge control, honest-WhatIf).Assumptions
ShouldProcess(target, action)message:Performing the operation "Squash-merge and delete branch 'chore/sdlc-sync'" on target "main (PR <ref>)".(surfaced because the earlier question about the prompt wording went unanswered).Closes #224