Files
msd-core/get-shit-done/references/worktree-path-safety.md
Tom Boucher b88d6d6ef1 refactor(#588): consolidate duplicated worktree_branch_check into one canonical fragment (#589)
Extracts the fail-closed worktree branch-check guard into a single canonical fragment (get-shit-done/references/worktree-branch-check.md) and repoints all five sites at it; orchestrator embeds the runnable block at dispatch. All safety invariants preserved; adversarially reviewed; full matrix green. Closes #588.
2026-06-01 18:08:10 -04:00

2.6 KiB

Worktree Path Safety

Guards for executor agents running inside Claude Code worktrees. Three checks must run before any staging, Edit, or Write operation in worktree mode.


Worktree branch check (run once at spawn-time)

The spawn-time HEAD/base guard now lives in the canonical fragment get-shit-done/references/worktree-branch-check.md, which the orchestrator embeds directly into your prompt at dispatch. Run that block FIRST, before any reset/checkout or staging. If your prompt contains a <worktree_branch_check> embed instruction rather than the block itself, complete that read-and-embed step before any reset/checkout or staging.


cwd-drift sentinel — step 0a (#3097)

A prior Bash call may have cd'd out of the worktree into the main repo. When that happens [ -f .git ] is false (main repo's .git is a directory), silently skipping all worktree guards. The sentinel captures the spawn-time toplevel and detects drift before every commit.

if [ -f .git ]; then  # we are in a worktree
  WT_GIT_DIR=$(git rev-parse --git-dir 2>/dev/null)
  case "$WT_GIT_DIR" in
    *.git/worktrees/*)
      SENTINEL="$WT_GIT_DIR/gsd-spawn-toplevel"
      [ ! -f "$SENTINEL" ] && git rev-parse --show-toplevel > "$SENTINEL" 2>/dev/null
      EXPECTED_TL=$(cat "$SENTINEL" 2>/dev/null)
      ACTUAL_TL=$(git rev-parse --show-toplevel 2>/dev/null)
      if [ -n "$EXPECTED_TL" ] && [ "$ACTUAL_TL" != "$EXPECTED_TL" ]; then
        echo "FATAL: cwd drifted from spawn-time worktree root (#3097)" >&2
        echo "  Spawn-time: $EXPECTED_TL" >&2
        echo "  Current:    $ACTUAL_TL" >&2
        echo "RECOVERY: cd \"$EXPECTED_TL\" before staging, then re-run this commit." >&2
        exit 1
      fi
      ;;
  esac
fi

Absolute-path guard — step 0b (#3099)

Edit/Write calls using absolute paths constructed from the orchestrator's pwd (main repo root) will resolve to the main repo, not the worktree. Writes land in the wrong directory; git commit from the worktree sees a clean tree and the work is silently lost.

Before any Edit or Write using an absolute path:

WT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)
# Fail fast if ABS_PATH resolves outside the worktree
if [[ "$ABS_PATH" != "$WT_ROOT"* ]]; then
  echo "WARNING: $ABS_PATH is outside the worktree ($WT_ROOT)" >&2
  echo "Use a relative path or recompute the absolute path from WT_ROOT." >&2
fi

Prefer relative paths for all Edit/Write operations. When an absolute path is unavoidable, always derive it from git rev-parse --show-toplevel run inside the worktree — never from pwd captured in the orchestrator context.