bug(worktrees): rebase silently targets the PRIMARY checkout when dir= is the repo root - and ignores path=/branch= #125

Closed
opened 2026-07-15 22:49:54 +00:00 by hexajon · 0 comments
hexajon commented 2026-07-15 22:49:54 +00:00 (Migrated from codeberg.org)

madt_worktrees action=rebase is documented as "targets a named worktree, never the cwd checkout" (tool schema) and "dir= is REQUIRED (the named worktree); other checkouts are left untouched" (madt_help topic=worktrees). Neither guarantee is enforced. Live repro, 2026-07-15, in sixfold_space/madtea with linked worktree .worktrees/issue-121 on branch issue-121-stamp-plugin-version, one commit behind main:

  1. action=rebase dir=<repo-root> path=.worktrees/issue-121 onto=main
  2. action=rebase dir=<repo-root> path=<repo-root>/.worktrees/issue-121 onto=main
  3. action=rebase dir=<repo-root> branch=issue-121-stamp-plugin-version onto=main

All three returned Already up to date - main already on top of main and left the worktree unrebased. Every call operated on the PRIMARY checkout's branch (main), because dir= named the repo root and rebase treats dir= as the checkout to rebase - while path= and branch= (the params every OTHER worktree action uses for targeting) were silently swallowed.

Why this is severe

  • A mutating git operation accepted the primary checkout as its target and produced success-shaped output. This run was harmlessly "up to date", but with a feature branch checked out in the primary and a moving onto=, this rewrites the primary's checked-out branch - the exact shared-state clobber the foreign-primary guards, madt_pull refusals, and madt_commit refusals exist to prevent. The foreign-repo path even has this guard already (rebase restricted to session-created worktrees); the own-repo path has none.
  • Silent parameter swallowing: path= and branch= are accepted-and-ignored on rebase, so a caller following the add/remove targeting convention gets the wrong checkout with no diagnostic. Three differently-shaped calls, one identical wrong result.
  • The result line does not name the checkout it operated on, so the mis-target is invisible unless the caller independently verifies the worktree head afterwards.

Asks

  1. rebase REFUSES a dir= that resolves to the primary checkout (or any non-linked-worktree path), with a rail naming the correct form: action=rebase dir=".worktrees/<name>" onto=<base>.
  2. Inapplicable params on an action are rejected loudly, not swallowed - at minimum path=/branch= on rebase error out naming the right param (dir=).
  3. The rebase result line always names the branch and checkout path it operated on ("rebased in onto "), so a mis-target is visible even when the operation is a no-op.
  4. Hermetic tests: primary-dir refusal; swallowed-param rejection; result-line content.

Acceptance

  • The three repro calls above all fail with actionable errors instead of no-op success.
  • action=rebase dir=".worktrees/issue-121" onto=main (the documented form) still works.
  • Gate green.
`madt_worktrees action=rebase` is documented as "targets a named worktree, never the cwd checkout" (tool schema) and "dir= is REQUIRED (the named worktree); other checkouts are left untouched" (madt_help topic=worktrees). Neither guarantee is enforced. Live repro, 2026-07-15, in sixfold_space/madtea with linked worktree `.worktrees/issue-121` on branch `issue-121-stamp-plugin-version`, one commit behind main: 1. `action=rebase dir=<repo-root> path=.worktrees/issue-121 onto=main` 2. `action=rebase dir=<repo-root> path=<repo-root>/.worktrees/issue-121 onto=main` 3. `action=rebase dir=<repo-root> branch=issue-121-stamp-plugin-version onto=main` All three returned `Already up to date - main already on top of main` and left the worktree unrebased. Every call operated on the PRIMARY checkout's branch (main), because dir= named the repo root and rebase treats dir= as the checkout to rebase - while `path=` and `branch=` (the params every OTHER worktree action uses for targeting) were silently swallowed. ## Why this is severe - A mutating git operation accepted the primary checkout as its target and produced success-shaped output. This run was harmlessly "up to date", but with a feature branch checked out in the primary and a moving `onto=`, this rewrites the primary's checked-out branch - the exact shared-state clobber the foreign-primary guards, madt_pull refusals, and madt_commit refusals exist to prevent. The foreign-repo path even has this guard already (rebase restricted to session-created worktrees); the own-repo path has none. - Silent parameter swallowing: `path=` and `branch=` are accepted-and-ignored on rebase, so a caller following the add/remove targeting convention gets the wrong checkout with no diagnostic. Three differently-shaped calls, one identical wrong result. - The result line does not name the checkout it operated on, so the mis-target is invisible unless the caller independently verifies the worktree head afterwards. ## Asks 1. rebase REFUSES a dir= that resolves to the primary checkout (or any non-linked-worktree path), with a rail naming the correct form: `action=rebase dir=".worktrees/<name>" onto=<base>`. 2. Inapplicable params on an action are rejected loudly, not swallowed - at minimum `path=`/`branch=` on rebase error out naming the right param (`dir=`). 3. The rebase result line always names the branch and checkout path it operated on ("rebased <branch> in <path> onto <base>"), so a mis-target is visible even when the operation is a no-op. 4. Hermetic tests: primary-dir refusal; swallowed-param rejection; result-line content. ## Acceptance - The three repro calls above all fail with actionable errors instead of no-op success. - `action=rebase dir=".worktrees/issue-121" onto=main` (the documented form) still works. - Gate green.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
sixfold-space/madtea#125
No description provided.