feat(dx): add worktree:sync mise task to rebase clean worktrees on origin/main - #218
ClintEastman02 wants to merge 7 commits into
Conversation
…igin/main Worktrees branched from origin/main drift silently as other PRs merge, causing unnecessary conflicts at push time and CI runs against stale bases. Issue aws-samples#197. The new `mise run worktree:sync` task fetches origin/main and rebases every clean linked worktree onto it. Dirty worktrees and main itself are skipped; conflicting rebases are aborted and reported with a non-zero exit so the operator can resolve them explicitly rather than leaving a half-rebased state. Closes aws-samples#197
|
Works as designed. I noticed that the script could be enhanced with a "detect already-merged branches" check (e.g., |
Address review feedback on PR aws-samples#218: detect worktrees whose HEAD is already an ancestor of origin/main (i.e. the branch has merged) and skip them with a MERGED message rather than attempting a no-op rebase. The operator can then prune the worktree at their leisure. Suggested by @scottschreckengaust.
|
Good call — added the merged-branch detection in fe30fb7. Worktrees whose HEAD is already an ancestor of Test matrix (all four cases verified locally):
|
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #218 +/- ##
=======================================
Coverage ? 86.09%
=======================================
Files ? 167
Lines ? 39535
Branches ? 3923
=======================================
Hits ? 34037
Misses ? 5498
Partials ? 0 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
|
Thanks for wiring the rebase automation — the task itself is solid. Main thing first: this branch is currently conflicting with Two smaller observations on the task while you're in there (neither blocking):
|
scottschreckengaust
left a comment
There was a problem hiding this comment.
Verdict: Request changes
The worktree:sync task itself is well-built — correct escaping, fail-closed rebase handling, and it parses cleanly (mise tasks --all lists //:worktree:sync). But two blockers stand between it and merge: the backing issue is not approved, and the branch is stale enough that it both conflicts with main and edits an AGENTS.md that no longer exists in its current form.
Vision alignment
Fits well. This is bounded, reviewable developer-experience tooling that reduces base-branch drift (issue #197) — no blast-radius, cost, or control-plane implications. It reinforces the worktree-per-branch workflow the repo already mandates.
Blocking issues
-
Backing issue #197 is not
approved(ADR-003 governance gate).gh issue view 197shows labelsenhancement,tooling,v1— noapprovedlabel. Per ADR-003 and this repo's standing gate, implementation requires an approved issue. This alone precludes an APPROVE verdict. Fix: have a maintainer applyapprovedto #197 (the issue is well-formed and the PR implements its recommended Option A). -
Branch is CONFLICTING and edits a since-rewritten
AGENTS.md.mergeable: CONFLICTING; head is from June. Beyond the raw conflict, currentmain'sAGENTS.mdhas been restructured — theGit worktreescommon-mistake bullet and theCommands you can uselist this PR patches no longer exist onmain(verified:grep 'Git worktrees' AGENTS.mdreturns nothing). After rebasing, the twoworktree:syncreferences must be re-placed into the new structure (theWorktrees:bullet under Git workflow and the## Commands (run these)block). A blind rebase will drop or misplace them. (Prior reviewer @isadeks flagged the conflict; it remains true at head.)
Non-blocking suggestions / nits
-
Squash-merge detection gap (mise.toml:198).
git merge-base --is-ancestor HEAD origin/mainonly catches fast-forward / merge-commit integrations.aws-samplesmerges recent PRs via squash (history shows... (#NNN)squashed tips), so a squash-merged branch's HEAD stays unique to the branch and falls through to the rebase path, replaying already-merged content (usually a harmless "up to date" or an empty-commit drop, occasionally a spurious conflict). Consider anorigin/<branch>-gone check orgh pr list --state merged --head <branch>as a secondary MERGED signal. (Also raised by @isadeks.) -
Add a scannable summary footer. Across N worktrees the per-line output is noisy; a closing
rebased: … / merged: … / skipped: … / failed: …line would help. (Also raised by @isadeks.) -
TOML string style (mise.toml:180). This is the repo's first
"""(basic, escape-processed) multilinerun— every other multiline task uses arrays. It forced the\\tdouble-escaping in the awk/IFS. A'''literal multiline string would carry the script verbatim (\t,$'\t') with no escaping and less fragility. Correct as written, but literal strings are the safer convention for embedded shell. -
Detached-HEAD worktrees are silently skipped (they emit no
branchline, so the awk never prints them). Acceptable, but worth a one-line comment so a future reader doesn't think it's a bug.
Documentation
AGENTS.md is updated in two places (good instinct), but against the obsolete pre-rewrite file — see blocker #2. No docs/guides/ or docs/design/ change is required for contributor tooling, so no Starlight mirror regeneration is needed. ROADMAP.md update is not clearly warranted for a dx task. Once rebased, re-verify the two AGENTS.md references land in the current structure.
Tests & CI
No automated test — appropriate, as the repo has no harness for embedded mise shell tasks (and the known shellcheck toolchain gap, #278, means this script isn't linted in CI either). The PR description documents a thorough manual test matrix (clean/behind, even, dirty, conflicting, merged, main). I independently verified bash -n syntax passes and mise parses/lists the task. CI: Validate PR title pass, build (agentcore) pass, auto-approve skipped, Codecov green — but checks ran on a stale base; they must re-run after rebase.
Review agents run
Execution context cannot spawn nested sub-agents, so pr-review-toolkit agents were performed by equivalent hand-analysis (stated per Stage 3):
- code-reviewer — done by hand (mise.toml shell task + AGENTS.md prose): escaping,
set -eu -o pipefail, guard clauses, exit-status propagation reviewed. - silent-failure-hunter — done by hand: rebase failure path aborts and sets
status=1;--is-ancestorandstatus --porcelainguarded;rebase --abort || trueintentional. No swallowed failures except the documented squash-merge/detached-HEAD skips. - comment-analyzer — done by hand: the mise.toml header comment and AGENTS.md bullet accurately describe behavior.
- pr-test-analyzer — done by hand: no test harness applies (see Tests & CI).
- type-design-analyzer — omitted: no types (config/prose only).
- /security-review — omitted: no IAM/Cedar/network/secrets/input-gateway surface. The task runs
git rebaselocally on clean trees only and aborts on conflict — no privilege or secret exposure.
Human heuristics
- Proportionality — Pass. A single mise task for a single, well-scoped drift problem; no over-abstraction.
- Coherence — Concern (mise.toml:180): first
"""multilinerunin a file that otherwise uses arrays; a'''literal would match intent better and match the repo's escape-free style. - Clarity — Pass.
MERGED/SKIP/FAILprefixes and the header comment make behavior legible. - Appropriateness — Concern (mise.toml:198): the merged-detection heuristic isn't verified against this repo's actual squash-merge behavior, so it under-detects real-world merged branches.
| # If the branch tip is already in origin/main, the work has merged and a | ||
| # rebase would either no-op or replay merged commits depending on history. | ||
| # Report and skip so the operator can prune the worktree at their leisure. | ||
| if git -C "$wt" merge-base --is-ancestor HEAD origin/main 2>/dev/null; then |
There was a problem hiding this comment.
Non-blocking: merge-base --is-ancestor HEAD origin/main misses squash-merged branches. After a squash merge the branch HEAD is still unique to the branch, so it falls through to the rebase path and replays already-merged content. Since this repo squash-merges recent PRs, consider a secondary signal — an origin/<branch>-gone check or gh pr list --state merged --head <branch>.
| # conflict explicitly rather than silently leaving a half-rebased state. | ||
| [tasks."worktree:sync"] | ||
| description = "Fetch origin/main and rebase every clean linked worktree onto it" | ||
| run = """ |
There was a problem hiding this comment.
Non-blocking style: this is the repo's first """ (escape-processed) multiline run, which forced the \\t double-escaping below. A ''' literal multiline string would carry the script verbatim (\t, $'\t') with no escaping and less risk of a future edit mis-escaping.
Summary
Adds a new mise task
mise run worktree:syncthat fetchesorigin/mainand rebases every clean linked worktree onto it. Implements Option A from #197.git worktree list --porcelainand rebases each linked worktreeSKIPmessagemain/master(rebase isn't the right tool for the primary branch)git rebase --abortand exits non-zero so the operator surfaces the conflict explicitly rather than leaving a half-rebased stateAlso updates
AGENTS.mdto point contributors at the new task in two places (the worktree common-mistake bullet and the Commands you can use list).Closes #197
Test plan
Tested locally with four worktree states:
origin/main→ rebased successfullyorigin/main→ no-op (rebase reports "up to date")SKIPmessage, not touchedFAILreported,git rebase --abortruns, working tree restored to pre-rebase HEAD, task exits non-zeromainworktree → skipped silentlymise tasks --alllists//:worktree:sync)