diff --git a/.changeset/zesty-quails-wave.md b/.changeset/zesty-quails-wave.md
new file mode 100644
index 000000000..aefa45d43
--- /dev/null
+++ b/.changeset/zesty-quails-wave.md
@@ -0,0 +1,5 @@
+---
+type: Fixed
+pr: 280
+---
+Fixed execute-phase subagent prompt file paths to anchor reads to PROJECT_ROOT in worktree execution.
diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md
index 435ed9898..1048ca121 100644
--- a/get-shit-done/workflows/execute-phase.md
+++ b/get-shit-done/workflows/execute-phase.md
@@ -619,18 +619,20 @@ increases monotonically across waves. `{status}` is `complete` (success),
- Read these files at execution start using the Read tool:
- - {phase_dir}/{plan_file} (Plan)
- - .planning/PROJECT.md (Project context — core value, requirements, evolution rules)
- - .planning/STATE.md (State)
- - .planning/config.json (Config, if exists)
+ Read these files at execution start using the Read tool.
+ First resolve repo root so every path is anchored:
+ \`PROJECT_ROOT=$(git rev-parse --show-toplevel 2>/dev/null)\`
+ - ${PROJECT_ROOT}/{phase_dir}/{plan_file} (Plan)
+ - ${PROJECT_ROOT}/.planning/PROJECT.md (Project context — core value, requirements, evolution rules)
+ - ${PROJECT_ROOT}/.planning/STATE.md (State)
+ - ${PROJECT_ROOT}/.planning/config.json (Config, if exists)
${CONTEXT_WINDOW >= 500000 ? `
- - ${phase_dir}/*-CONTEXT.md (User decisions from discuss-phase — honors locked choices)
- - ${phase_dir}/*-RESEARCH.md (Technical research — pitfalls and patterns to follow)
- - ${prior_wave_summaries} (SUMMARY.md files from earlier waves in this phase — what was already built)
+ - ${PROJECT_ROOT}/${phase_dir}/*-CONTEXT.md (User decisions from discuss-phase — honors locked choices)
+ - ${PROJECT_ROOT}/${phase_dir}/*-RESEARCH.md (Technical research — pitfalls and patterns to follow)
+ - ${PROJECT_ROOT}/${prior_wave_summaries} (SUMMARY.md files from earlier waves in this phase — what was already built)
` : ''}
- - ./CLAUDE.md (Project instructions, if exists — follow project-specific guidelines and coding conventions)
- - .claude/skills/ or .agents/skills/ (Project skills, if either exists — list skills, read SKILL.md for each, follow relevant rules during implementation)
+ - ${PROJECT_ROOT}/CLAUDE.md (Project instructions, if exists — follow project-specific guidelines and coding conventions)
+ - ${PROJECT_ROOT}/.claude/skills/ or ${PROJECT_ROOT}/.agents/skills/ (Project skills, if either exists — list skills, read SKILL.md for each, follow relevant rules during implementation)
${AGENT_SKILLS}
diff --git a/tests/bug-3097-3099-executor-worktree-path-safety.test.cjs b/tests/bug-3097-3099-executor-worktree-path-safety.test.cjs
index 5a33a2f7e..e4331d222 100644
--- a/tests/bug-3097-3099-executor-worktree-path-safety.test.cjs
+++ b/tests/bug-3097-3099-executor-worktree-path-safety.test.cjs
@@ -84,6 +84,20 @@ describe('bug #3099: absolute-path safety guidance in gsd-executor.md', () => {
);
});
+ test('execute-phase prompt anchors subagent file paths to project_root before files_to_read (#280)', () => {
+ const filesIdx = executePhaseSrc.indexOf('');
+ assert.ok(filesIdx !== -1, 'files_to_read block not found in execute-phase.md');
+ const dispatchSnippet = executePhaseSrc.slice(filesIdx, filesIdx + 1800);
+ assert.ok(
+ dispatchSnippet.includes('PROJECT_ROOT=$(git rev-parse --show-toplevel'),
+ 'executor dispatch must compute PROJECT_ROOT in the prompt before file reads',
+ );
+ assert.ok(
+ dispatchSnippet.includes('${PROJECT_ROOT}/'),
+ 'executor files_to_read paths must be anchored to ${PROJECT_ROOT}/',
+ );
+ });
+
test('worktree-path-safety.md reference file exists', () => {
assert.ok(
fs.existsSync(path.join(ROOT, 'get-shit-done', 'references', 'worktree-path-safety.md')),