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.
68 lines
2.6 KiB
Markdown
68 lines
2.6 KiB
Markdown
# 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.
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|