From d1ff0437f1ba1c536bf18e300a51260967bf3b86 Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Sat, 28 Mar 2026 10:11:49 -0700 Subject: [PATCH 1/2] feat(config): add workflow.use_worktrees toggle to disable worktree isolation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds `workflow.use_worktrees` config option (default: `true`) that allows users to disable git worktree isolation for executor agents. When set to `false`: - Executor agents run without `isolation="worktree"` - Plans execute sequentially on the main working tree - No worktree merge ordering issues or orphaned worktrees - Normal git hooks run (no --no-verify needed) This provides an escape hatch for solo developers and users who experience worktree merge conflicts, as worktree ordering issues are inherently difficult when parallel agents modify overlapping files. Usage: /gsd:settings → set workflow.use_worktrees to false Or directly: gsd-tools config-set workflow.use_worktrees false Changes: - config.cjs: add workflow.use_worktrees to valid keys - planning-config.md: document the option - execute-phase.md: read config, conditional worktree + sequential mode - execute-plan.md: conditional worktree in Pattern A - quick.md: conditional worktree for quick executor - diagnose-issues.md: conditional worktree for debug agents - 2 new tests (config set + workflow structural check) Closes #1451 Co-Authored-By: Claude Opus 4.6 (1M context) --- get-shit-done/bin/lib/config.cjs | 1 + get-shit-done/references/planning-config.md | 1 + get-shit-done/workflows/diagnose-issues.md | 8 ++++++- get-shit-done/workflows/execute-phase.md | 23 +++++++++++++++++++++ get-shit-done/workflows/execute-plan.md | 2 +- get-shit-done/workflows/quick.md | 6 +++++- tests/config.test.cjs | 10 +++++++++ tests/execute-phase-wave.test.cjs | 16 ++++++++++++++ 8 files changed, 64 insertions(+), 3 deletions(-) diff --git a/get-shit-done/bin/lib/config.cjs b/get-shit-done/bin/lib/config.cjs index 67fe5a6b9..9cfb7dc9f 100644 --- a/get-shit-done/bin/lib/config.cjs +++ b/get-shit-done/bin/lib/config.cjs @@ -22,6 +22,7 @@ const VALID_CONFIG_KEYS = new Set([ 'workflow.discuss_mode', 'workflow.skip_discuss', 'workflow._auto_chain_active', + 'workflow.use_worktrees', 'git.branching_strategy', 'git.phase_branch_template', 'git.milestone_branch_template', 'git.quick_branch_template', 'planning.commit_docs', 'planning.search_gitignored', 'hooks.context_warnings', diff --git a/get-shit-done/references/planning-config.md b/get-shit-done/references/planning-config.md index f8276c761..20ee56f37 100644 --- a/get-shit-done/references/planning-config.md +++ b/get-shit-done/references/planning-config.md @@ -24,6 +24,7 @@ Configuration options for `.planning/` directory behavior. | `git.phase_branch_template` | `"gsd/phase-{phase}-{slug}"` | Branch template for phase strategy | | `git.milestone_branch_template` | `"gsd/{milestone}-{slug}"` | Branch template for milestone strategy | | `git.quick_branch_template` | `null` | Optional branch template for quick-task runs | +| `workflow.use_worktrees` | `true` | Whether executor agents run in isolated git worktrees. Set to `false` to disable worktrees — agents execute sequentially on the main working tree instead. Recommended for solo developers or when worktree merges cause issues. | diff --git a/get-shit-done/workflows/diagnose-issues.md b/get-shit-done/workflows/diagnose-issues.md index ea53fe26e..47f3e09d6 100644 --- a/get-shit-done/workflows/diagnose-issues.md +++ b/get-shit-done/workflows/diagnose-issues.md @@ -55,6 +55,12 @@ gaps = [ +**Read worktree config:** + +```bash +USE_WORKTREES=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.use_worktrees 2>/dev/null || echo "true") +``` + **Report diagnosis plan to user:** ``` @@ -92,7 +98,7 @@ For each gap, fill the debug-subagent-prompt template and spawn: Task( prompt=filled_debug_subagent_prompt + "\n\n\n- {phase_dir}/{phase_num}-UAT.md\n- .planning/STATE.md\n\n${AGENT_SKILLS_DEBUGGER}", subagent_type="gsd-debugger", - isolation="worktree", + ${USE_WORKTREES !== "false" ? 'isolation="worktree",' : ''} description="Debug: {truth_short}" ) ``` diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 0bdb11174..f403da146 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -68,6 +68,14 @@ AGENT_SKILLS=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" agent-skills Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`, `phase_req_ids`. +Read worktree config: + +```bash +USE_WORKTREES=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.use_worktrees 2>/dev/null || echo "true") +``` + +When `USE_WORKTREES` is `false`, all executor agents run without `isolation="worktree"` — they execute sequentially on the main working tree instead of in parallel worktrees. + **If `phase_found` is false:** Error — phase directory not found. **If `plan_count` is 0:** Error — no plans found in phase. **If `state_exists` is false but `.planning/` exists:** Offer reconstruct or continue. @@ -223,6 +231,8 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT For 200k models, this keeps orchestrator context lean (~10-15%). For 1M+ models (Opus 4.6, Sonnet 4.6), richer context can be passed directly. + **Worktree mode** (`USE_WORKTREES` is not `false`): + ``` Task( subagent_type="gsd-executor", @@ -279,6 +289,19 @@ Execute each selected wave in sequence. Within a wave: parallel if `PARALLELIZAT ) ``` + **Sequential mode** (`USE_WORKTREES` is `false`): + + Omit `isolation="worktree"` from the Task call. Replace the `` block with: + + ``` + + You are running as a SEQUENTIAL executor agent on the main working tree. + Use normal git commits (with hooks). Do NOT use --no-verify. + + ``` + + When worktrees are disabled, execute plans **one at a time within each wave** (sequential) regardless of the `PARALLELIZATION` setting — multiple agents writing to the same working tree concurrently would cause conflicts. + 3. **Wait for all agents in wave to complete.** **Completion signal fallback (Copilot and runtimes where Task() may not return):** diff --git a/get-shit-done/workflows/execute-plan.md b/get-shit-done/workflows/execute-plan.md index cbbf71330..920e92ab9 100644 --- a/get-shit-done/workflows/execute-plan.md +++ b/get-shit-done/workflows/execute-plan.md @@ -72,7 +72,7 @@ grep -n "type=\"checkpoint" .planning/phases/XX-name/{phase}-{plan}-PLAN.md | Verify-only | B (segmented) | Segments between checkpoints. After none/human-verify → SUBAGENT. After decision/human-action → MAIN | | Decision | C (main) | Execute entirely in main context | -**Pattern A:** init_agent_tracking → spawn Task(subagent_type="gsd-executor", model=executor_model, isolation="worktree") with prompt: execute plan at [path], autonomous, all tasks + SUMMARY + commit, follow deviation/auth rules, report: plan name, tasks, SUMMARY path, commit hash → track agent_id → wait → update tracking → report. +**Pattern A:** init_agent_tracking → spawn Task(subagent_type="gsd-executor", model=executor_model) with prompt: execute plan at [path], autonomous, all tasks + SUMMARY + commit, follow deviation/auth rules, report: plan name, tasks, SUMMARY path, commit hash → track agent_id → wait → update tracking → report. **Include `isolation="worktree"` only if `workflow.use_worktrees` is not `false`** (read via `config-get workflow.use_worktrees`). **Pattern B:** Execute segment-by-segment. Autonomous segments: spawn subagent for assigned tasks only (no SUMMARY/commit). Checkpoints: main context. After all segments: aggregate, create SUMMARY, commit. See segment_execution. diff --git a/get-shit-done/workflows/quick.md b/get-shit-done/workflows/quick.md index 89441f965..bfd46f718 100644 --- a/get-shit-done/workflows/quick.md +++ b/get-shit-done/workflows/quick.md @@ -126,6 +126,10 @@ AGENT_SKILLS_VERIFIER=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" age Parse JSON for: `planner_model`, `executor_model`, `checker_model`, `verifier_model`, `commit_docs`, `branch_name`, `quick_id`, `slug`, `date`, `timestamp`, `quick_dir`, `task_dir`, `roadmap_exists`, `planning_exists`. +```bash +USE_WORKTREES=$(node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" config-get workflow.use_worktrees 2>/dev/null || echo "true") +``` + **If `roadmap_exists` is false:** Error — Quick mode requires an active project with ROADMAP.md. Run `/gsd:new-project` first. Quick tasks can run mid-phase - validation only checks ROADMAP.md exists, not phase status. @@ -559,7 +563,7 @@ ${AGENT_SKILLS_EXECUTOR} ", subagent_type="gsd-executor", model="{executor_model}", - isolation="worktree", + ${USE_WORKTREES !== "false" ? 'isolation="worktree",' : ''} description="Execute: ${DESCRIPTION}" ) ``` diff --git a/tests/config.test.cjs b/tests/config.test.cjs index 31a2da656..74fa46852 100644 --- a/tests/config.test.cjs +++ b/tests/config.test.cjs @@ -232,6 +232,16 @@ describe('config-set command', () => { assert.strictEqual(config.workflow.text_mode, true); }); + test('sets workflow.use_worktrees to disable worktree isolation', () => { + writeConfig(tmpDir, {}); + + const result = runGsdTools('config-set workflow.use_worktrees false', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const config = readConfig(tmpDir); + assert.strictEqual(config.workflow.use_worktrees, false); + }); + test('errors when no key path provided', () => { const result = runGsdTools('config-set', tmpDir); assert.strictEqual(result.success, false); diff --git a/tests/execute-phase-wave.test.cjs b/tests/execute-phase-wave.test.cjs index 871e9904c..d00b453db 100644 --- a/tests/execute-phase-wave.test.cjs +++ b/tests/execute-phase-wave.test.cjs @@ -105,4 +105,20 @@ describe('execute-phase docs: user-facing wave flag', () => { 'help.md should include wave-filter usage' ); }); + + test('workflow supports use_worktrees config toggle', () => { + const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8'); + assert.ok( + content.includes('USE_WORKTREES'), + 'workflow should reference USE_WORKTREES variable' + ); + assert.ok( + content.includes('config-get workflow.use_worktrees'), + 'workflow should read use_worktrees from config' + ); + assert.ok( + content.includes('Sequential mode'), + 'workflow should document sequential mode when worktrees disabled' + ); + }); }); From 74cd8f2bd0a93f9fe71b9ec63c3aa2598e2eaaec Mon Sep 17 00:00:00 2001 From: Tibsfox Date: Sun, 29 Mar 2026 14:42:08 -0700 Subject: [PATCH 2/2] test(config): add config-get/set roundtrip and cross-workflow structural tests for use_worktrees Add comprehensive test coverage for the workflow.use_worktrees config toggle: - config-get returns false after setting to false (roundtrip verification) - config-get errors with "Key not found" when not set (validates workflow fallback behavior where `|| echo "true"` provides the default) - config-get returns true after setting to true - Toggle back and forth works correctly - Structural tests verify USE_WORKTREES is wired into quick.md, diagnose-issues.md, execute-plan.md, planning-config.md, and config.cjs Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/config.test.cjs | 55 ++++++++++++++++++++++++++++ tests/execute-phase-wave.test.cjs | 60 +++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+) diff --git a/tests/config.test.cjs b/tests/config.test.cjs index 74fa46852..6280df25e 100644 --- a/tests/config.test.cjs +++ b/tests/config.test.cjs @@ -775,3 +775,58 @@ describe('config-set workflow.skip_discuss', () => { assert.strictEqual(output, true); }); }); + +// ─── config-set/config-get workflow.use_worktrees ──────────────────────────── + +describe('config-set/config-get workflow.use_worktrees', () => { + let tmpDir; + + beforeEach(() => { + tmpDir = createTempProject(); + runGsdTools('config-ensure-section', tmpDir, { HOME: tmpDir, USERPROFILE: tmpDir }); + }); + + afterEach(() => { + cleanup(tmpDir); + }); + + test('config-get workflow.use_worktrees returns false after setting to false', () => { + runGsdTools('config-set workflow.use_worktrees false', tmpDir); + const result = runGsdTools('config-get workflow.use_worktrees', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output, false); + }); + + test('config-get workflow.use_worktrees errors when not set (default config)', () => { + // config-ensure-section does NOT include use_worktrees in hardcoded defaults, + // so config-get should error with "Key not found". This is the expected behavior + // that workflows rely on: the shell fallback `|| echo "true"` provides the default. + const result = runGsdTools('config-get workflow.use_worktrees', tmpDir); + assert.strictEqual(result.success, false); + assert.ok( + result.error.includes('Key not found'), + `Expected "Key not found" in error: ${result.error}` + ); + }); + + test('config-get workflow.use_worktrees returns true after setting to true', () => { + runGsdTools('config-set workflow.use_worktrees true', tmpDir); + const result = runGsdTools('config-get workflow.use_worktrees', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output, true); + }); + + test('use_worktrees can be toggled back and forth', () => { + runGsdTools('config-set workflow.use_worktrees false', tmpDir); + runGsdTools('config-set workflow.use_worktrees true', tmpDir); + const result = runGsdTools('config-get workflow.use_worktrees', tmpDir); + assert.ok(result.success, `Command failed: ${result.error}`); + + const output = JSON.parse(result.output); + assert.strictEqual(output, true); + }); +}); diff --git a/tests/execute-phase-wave.test.cjs b/tests/execute-phase-wave.test.cjs index d00b453db..345bd50cc 100644 --- a/tests/execute-phase-wave.test.cjs +++ b/tests/execute-phase-wave.test.cjs @@ -122,3 +122,63 @@ describe('execute-phase docs: user-facing wave flag', () => { ); }); }); + +describe('use_worktrees config: cross-workflow structural coverage', () => { + const QUICK_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md'); + const DIAGNOSE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'diagnose-issues.md'); + const EXECUTE_PLAN_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-plan.md'); + const PLANNING_CONFIG_PATH = path.join(__dirname, '..', 'get-shit-done', 'references', 'planning-config.md'); + const CONFIG_CJS_PATH = path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'config.cjs'); + + test('quick workflow reads USE_WORKTREES from config', () => { + const content = fs.readFileSync(QUICK_PATH, 'utf-8'); + assert.ok( + content.includes('config-get workflow.use_worktrees'), + 'quick.md should read use_worktrees from config' + ); + assert.ok( + content.includes('USE_WORKTREES'), + 'quick.md should reference USE_WORKTREES variable' + ); + }); + + test('diagnose-issues workflow reads USE_WORKTREES from config', () => { + const content = fs.readFileSync(DIAGNOSE_PATH, 'utf-8'); + assert.ok( + content.includes('config-get workflow.use_worktrees'), + 'diagnose-issues.md should read use_worktrees from config' + ); + assert.ok( + content.includes('USE_WORKTREES'), + 'diagnose-issues.md should reference USE_WORKTREES variable' + ); + }); + + test('execute-plan workflow references use_worktrees config', () => { + const content = fs.readFileSync(EXECUTE_PLAN_PATH, 'utf-8'); + assert.ok( + content.includes('workflow.use_worktrees'), + 'execute-plan.md should reference workflow.use_worktrees' + ); + }); + + test('planning-config reference documents use_worktrees', () => { + const content = fs.readFileSync(PLANNING_CONFIG_PATH, 'utf-8'); + assert.ok( + content.includes('workflow.use_worktrees'), + 'planning-config.md should document workflow.use_worktrees' + ); + assert.ok( + content.includes('worktree'), + 'planning-config.md should describe worktree behavior' + ); + }); + + test('config.cjs includes workflow.use_worktrees in VALID_CONFIG_KEYS', () => { + const content = fs.readFileSync(CONFIG_CJS_PATH, 'utf-8'); + assert.ok( + content.includes("'workflow.use_worktrees'"), + 'config.cjs VALID_CONFIG_KEYS should include workflow.use_worktrees' + ); + }); +});