From ae05e76ddf65bba7a1dcffb6683634d060e1815e Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Fri, 6 Mar 2026 14:20:32 +0900 Subject: [PATCH] fix(start-work): remove worktree setup prompt when unspecified, add strong worktree active instructions When no worktree is specified in boulder, stop injecting 'Worktree Setup Required' instructions. When worktree IS present, inject emphatic instructions ensuring the agent and all subagents operate exclusively within the worktree directory. --- src/hooks/start-work/index.test.ts | 20 ++++++++++++-------- src/hooks/start-work/start-work-hook.ts | 22 ++++++++++++---------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/src/hooks/start-work/index.test.ts b/src/hooks/start-work/index.test.ts index 26b87eba4..565b2decb 100644 --- a/src/hooks/start-work/index.test.ts +++ b/src/hooks/start-work/index.test.ts @@ -417,7 +417,7 @@ describe("start-work hook", () => { detectSpy.mockRestore() }) - test("should inject model-decides instructions when no --worktree flag", async () => { + test("should NOT inject worktree instructions when no --worktree flag", async () => { // given - single plan, no worktree flag const plansDir = join(testDir, ".sisyphus", "plans") mkdirSync(plansDir, { recursive: true }) @@ -431,10 +431,10 @@ describe("start-work hook", () => { // when await hook["chat.message"]({ sessionID: "session-123" }, output) - // then - model-decides instructions should appear - expect(output.parts[0].text).toContain("Worktree Setup Required") - expect(output.parts[0].text).toContain("git worktree list --porcelain") - expect(output.parts[0].text).toContain("git worktree add") + // then - no worktree instructions should appear + expect(output.parts[0].text).not.toContain("Worktree Setup Required") + expect(output.parts[0].text).not.toContain("Worktree Active") + expect(output.parts[0].text).not.toContain("git worktree list --porcelain") }) test("should inject worktree path when --worktree flag is valid", async () => { @@ -452,8 +452,10 @@ describe("start-work hook", () => { // when await hook["chat.message"]({ sessionID: "session-123" }, output) - // then - validated path shown, no model-decides instructions - expect(output.parts[0].text).toContain("**Worktree**: /validated/worktree") + // then - strong worktree active instructions shown + expect(output.parts[0].text).toContain("Worktree Active") + expect(output.parts[0].text).toContain("/validated/worktree") + expect(output.parts[0].text).toContain("subagent") expect(output.parts[0].text).not.toContain("Worktree Setup Required") }) @@ -548,8 +550,10 @@ describe("start-work hook", () => { // when await hook["chat.message"]({ sessionID: "session-789" }, output) - // then - shows existing worktree, no model-decides instructions + // then - shows strong worktree active instructions + expect(output.parts[0].text).toContain("Worktree Active") expect(output.parts[0].text).toContain("/existing/wt") + expect(output.parts[0].text).toContain("subagent") expect(output.parts[0].text).not.toContain("Worktree Setup Required") }) }) diff --git a/src/hooks/start-work/start-work-hook.ts b/src/hooks/start-work/start-work-hook.ts index 03cdb540e..81ec85cc3 100644 --- a/src/hooks/start-work/start-work-hook.ts +++ b/src/hooks/start-work/start-work-hook.ts @@ -34,26 +34,28 @@ function findPlanByName(plans: string[], requestedName: string): string | null { return partialMatch || null } -const MODEL_DECIDES_WORKTREE_BLOCK = ` -## Worktree Setup Required +function createWorktreeActiveBlock(worktreePath: string): string { + return ` +## Worktree Active -No worktree specified. Before starting work, you MUST choose or create one: +**Worktree**: \`${worktreePath}\` -1. \`git worktree list --porcelain\` — list existing worktrees -2. Create if needed: \`git worktree add \` -3. Update \`.sisyphus/boulder.json\` — add \`"worktree_path": ""\` -4. Work exclusively inside that worktree directory` +**CRITICAL — DO NOT FORGET**: You are working inside a git worktree. ALL operations MUST be performed exclusively within this worktree directory. +- Every file read, write, edit, and git operation MUST target paths under: \`${worktreePath}\` +- When delegating tasks to subagents, you MUST include the worktree path in your delegation prompt so they also operate exclusively within the worktree +- NEVER operate on the main repository directory — always use the worktree path above` +} function resolveWorktreeContext( explicitWorktreePath: string | null, ): { worktreePath: string | undefined; block: string } { if (explicitWorktreePath === null) { - return { worktreePath: undefined, block: MODEL_DECIDES_WORKTREE_BLOCK } + return { worktreePath: undefined, block: "" } } const validatedPath = detectWorktreePath(explicitWorktreePath) if (validatedPath) { - return { worktreePath: validatedPath, block: `\n**Worktree**: ${validatedPath}` } + return { worktreePath: validatedPath, block: createWorktreeActiveBlock(validatedPath) } } return { @@ -165,7 +167,7 @@ No incomplete plans available. Create a new plan with: /plan "your task"` appendSessionId(ctx.directory, sessionId) } - const worktreeDisplay = effectiveWorktree ? `\n**Worktree**: ${effectiveWorktree}` : worktreeBlock + const worktreeDisplay = effectiveWorktree ? createWorktreeActiveBlock(effectiveWorktree) : worktreeBlock contextInfo = ` ## Active Work Session Found