* fix(workflows): assert HEAD on per-agent branch before worktree commits Worktree-mode setup could leave HEAD attached to a protected branch (master), causing agent commits to land there. The previous response was a destructive self-recovery via 'git update-ref refs/heads/master <sha>', which silently rewinds the protected branch and destroys concurrent commits in multi-active scenarios (parallel agents, user committing while agent runs). - Reorder <worktree_branch_check> in execute-phase.md and quick.md to assert HEAD via 'git symbolic-ref' BEFORE any 'git reset --hard'. HALT with a blocker if HEAD is on main/master/develop/trunk/release/* or detached. - Add a per-commit HEAD assertion (step 0) to gsd-executor.md <task_commit_protocol>; HEAD attachment can drift after 'git checkout <sha>'. - Forbid 'git update-ref refs/heads/<protected>' in <destructive_git_prohibition>; surface the blocker rather than self-heal. - Remove '--no-verify' as the worktree-mode default in execute-phase.md, execute-plan.md, quick.md, and references/git-integration.md. Hooks now run on every executor commit; opt out only via workflow.worktree_skip_hooks. - Add regression test that parses the worktree_branch_check blocks structurally and asserts the symbolic-ref check precedes the reset --hard, no workflow performs update-ref on a protected ref, and --no-verify is no longer the default in any parallel-execution prompt. * fix(#2924): address CodeRabbit review findings on worktree HEAD PR - Add positive worktree-agent-* allow-list to <task_commit_protocol> step 0 in gsd-executor.md and to <worktree_branch_check> in execute-phase.md and quick.md. The deny-list (main|master|develop|trunk|release/*) silently allowed feature/* and other arbitrary branches outside the agent namespace. - Register workflow.worktree_skip_hooks in both config schemas (sdk/src/query/config-schema.ts and get-shit-done/bin/lib/config-schema.cjs) and document it in docs/CONFIGURATION.md so config-set accepts it. - Fix stash lifecycle in execute-phase.md post-wave hook validation: stash under a named ref and pop after the hook run; warn on pop failure. - Pre-dispatch PLAN.md commit in quick.md: gate on git diff --cached --quiet for idempotency and exit 1 with a clear error on commit failure (both the --no-verify and the normal branches) — no more swallowing real errors. - Test fixes (tests/bug-2924-worktree-head-attachment.test.cjs): - Parse the protected-branch alternation structurally and require main, master, develop, trunk, release/.* (release/* was previously skipped by the \\b...\\b regex). - Use fs.readdirSync(dir, { recursive: true }) so workflows in nested subdirectories are also asserted against the update-ref ban. - Add allow-list assertions for execute-phase.md, quick.md, and gsd-executor.md to lock in the new positive namespace check. * test(#2924): assert sub-section end marker exists before slicing * test(#2924): use section boundary instead of fixed window for parallel-agents slice
This commit is contained in:
@@ -358,6 +358,30 @@ If RED or GREEN gate commits are missing, add a warning to SUMMARY.md under a `#
|
||||
<task_commit_protocol>
|
||||
After each task completes (verification passed, done criteria met), commit immediately.
|
||||
|
||||
**0. Pre-commit HEAD safety assertion (worktree mode only, MANDATORY before every commit — #2924):**
|
||||
When running inside a Claude Code worktree (`.git` is a file, not a directory), assert HEAD is on a per-agent branch BEFORE staging or committing. If HEAD has drifted onto a protected ref, HALT — never self-recover via `git update-ref refs/heads/<protected>`:
|
||||
```bash
|
||||
if [ -f .git ]; then # worktree
|
||||
HEAD_REF=$(git symbolic-ref --quiet HEAD || echo "DETACHED")
|
||||
ACTUAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
# Deny-list: never commit on a protected ref.
|
||||
if [ "$HEAD_REF" = "DETACHED" ] || \
|
||||
echo "$ACTUAL_BRANCH" | grep -Eq '^(main|master|develop|trunk|release/.*)$'; then
|
||||
echo "FATAL: refusing to commit — worktree HEAD is on '$ACTUAL_BRANCH' (expected per-agent branch)." >&2
|
||||
echo "DO NOT use 'git update-ref' to rewind the protected branch — surface as blocker (#2924)." >&2
|
||||
exit 1
|
||||
fi
|
||||
# Positive allow-list: HEAD must be on the canonical Claude Code worktree-agent
|
||||
# branch namespace (`worktree-agent-<id>`). This catches feature/* and any other
|
||||
# arbitrary branch that the deny-list would silently allow (#2924).
|
||||
if ! echo "$ACTUAL_BRANCH" | grep -Eq '^worktree-agent-[A-Za-z0-9._/-]+$'; then
|
||||
echo "FATAL: refusing to commit — worktree HEAD '$ACTUAL_BRANCH' is not in the worktree-agent-* namespace." >&2
|
||||
echo "Agent commits must live on per-agent branches; surface as blocker (#2924)." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
```
|
||||
|
||||
**1. Check modified files:** `git status --short`
|
||||
|
||||
**2. Stage task-related files individually** (NEVER `git add .` or `git add -A`):
|
||||
@@ -426,6 +450,15 @@ back, those deletions appear on the main branch, destroying prior-wave work (#20
|
||||
- `git rm` on files not explicitly created by the current task
|
||||
- `git checkout -- .` or `git restore .` (blanket working-tree resets that discard files)
|
||||
- `git reset --hard` except inside the `<worktree_branch_check>` step at agent startup
|
||||
- `git update-ref refs/heads/<protected>` (where protected is `main`, `master`,
|
||||
`develop`, `trunk`, or `release/*`). This is an absolute prohibition (#2924).
|
||||
If you discover that your worktree HEAD is attached to a protected branch and your
|
||||
commits landed there, **DO NOT** "recover" by force-rewinding the protected ref —
|
||||
that silently destroys concurrent commits in multi-active scenarios (parallel
|
||||
agents, user committing while you run). HALT and surface a blocker. The setup-time
|
||||
`<worktree_branch_check>` and per-commit `<pre_commit_head_assertion>` are the
|
||||
correct prevention; if either fails, the workflow MUST stop, not self-heal.
|
||||
- `git push --force` / `git push -f` to any branch you did not create.
|
||||
|
||||
If you need to discard changes to a specific file you modified during this task, use:
|
||||
```bash
|
||||
|
||||
Reference in New Issue
Block a user