From 46ac3296b45c0fbe6753b3b5275d6a6f16ee1281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=96mer=20Can=20G=C3=BCm=C3=BC=C5=9F?= Date: Tue, 14 Jul 2026 09:01:01 +0300 Subject: [PATCH] docs(templates): warn before parallel background spawns in a shared worktree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A real incident (#1014): two background agents on one worktree, agent B's commit workflow ran a global git stash and swept agent A's three untracked files. Git doesn't complain — the files just vanish, and only the in-session context remembers they existed. This is the issue's Option A, warn-only: the Parallel Fan-Out section of the coordinator template now tells the coordinator to check worktree mode before spawning 2+ background agents in one turn and show the user a one-shot warning when isolation is off. Once per session, proceed after warning — a caution, not a gate. No behavior change, no auto-isolation (that's Option B, a possible follow-up). Also drops the worktree reference's claim that shared-worktree concurrency is safe when agents modify different files — untracked files were never covered by that safety argument. The issue's sample warning referenced a SQUAD_WORKTREES env var that doesn't exist in the codebase; the added text points at the real mechanism (worktree mode via Pre-Spawn: Worktree Setup) instead. Fixes #1014 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .changeset/fix-1014-parallel-spawn-warning.md | 6 ++ .github/agents/squad.agent.md | 11 ++++ .squad-templates/squad.agent.md | 11 ++++ .squad-templates/worktree-reference.md | 2 +- .../templates/squad.agent.md.template | 11 ++++ .../squad-cli/templates/worktree-reference.md | 2 +- .../templates/squad.agent.md.template | 11 ++++ .../squad-sdk/templates/worktree-reference.md | 2 +- templates/squad.agent.md.template | 11 ++++ templates/worktree-reference.md | 2 +- test/ci/parallel-spawn-warning.test.ts | 64 +++++++++++++++++++ 11 files changed, 129 insertions(+), 4 deletions(-) create mode 100644 .changeset/fix-1014-parallel-spawn-warning.md create mode 100644 test/ci/parallel-spawn-warning.test.ts diff --git a/.changeset/fix-1014-parallel-spawn-warning.md b/.changeset/fix-1014-parallel-spawn-warning.md new file mode 100644 index 000000000..80ffe2f18 --- /dev/null +++ b/.changeset/fix-1014-parallel-spawn-warning.md @@ -0,0 +1,6 @@ +--- +"@bradygaster/squad-cli": patch +"@bradygaster/squad-sdk": patch +--- + +Coordinator template now warns before launching 2+ parallel background agents in a shared worktree: global-scope git operations (stash/clean/restore) from one agent can silently delete another agent's untracked files. Warn-only — worktree mode remains opt-in. The worktree reference no longer claims shared-worktree concurrency is safe when agents touch different files. diff --git a/.github/agents/squad.agent.md b/.github/agents/squad.agent.md index abbfcaaed..eb545668f 100644 --- a/.github/agents/squad.agent.md +++ b/.github/agents/squad.agent.md @@ -509,6 +509,17 @@ When the user gives any task, the Coordinator MUST: ``` 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. +**Shared-worktree guard.** Before spawning 2+ background agents in one turn, check whether worktree mode is active (see Pre-Spawn: Worktree Setup). If it is NOT, show the user this warning before launching: + +``` +⚠️ Launching {N} parallel background agents in a shared worktree. + Global-scope git operations (stash, clean, restore) from one agent can + silently delete another agent's untracked files. Enable worktree mode + for per-stream isolation, or accept the risk for this wave. +``` + +Warn once per session, then proceed — this is a caution, not a gate. + **Example — "Team, build the login page":** - Turn 1: Spawn {Lead} (architecture), {Frontend} (UI), {Backend} (API), {Tester} (test cases from spec) — ALL background, ALL in one tool call - Collect results. Scribe merges decisions. diff --git a/.squad-templates/squad.agent.md b/.squad-templates/squad.agent.md index abbfcaaed..eb545668f 100644 --- a/.squad-templates/squad.agent.md +++ b/.squad-templates/squad.agent.md @@ -509,6 +509,17 @@ When the user gives any task, the Coordinator MUST: ``` 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. +**Shared-worktree guard.** Before spawning 2+ background agents in one turn, check whether worktree mode is active (see Pre-Spawn: Worktree Setup). If it is NOT, show the user this warning before launching: + +``` +⚠️ Launching {N} parallel background agents in a shared worktree. + Global-scope git operations (stash, clean, restore) from one agent can + silently delete another agent's untracked files. Enable worktree mode + for per-stream isolation, or accept the risk for this wave. +``` + +Warn once per session, then proceed — this is a caution, not a gate. + **Example — "Team, build the login page":** - Turn 1: Spawn {Lead} (architecture), {Frontend} (UI), {Backend} (API), {Tester} (test cases from spec) — ALL background, ALL in one tool call - Collect results. Scribe merges decisions. diff --git a/.squad-templates/worktree-reference.md b/.squad-templates/worktree-reference.md index 958fee2e9..564a45d52 100644 --- a/.squad-templates/worktree-reference.md +++ b/.squad-templates/worktree-reference.md @@ -71,7 +71,7 @@ When worktree mode is enabled, the coordinator creates dedicated worktrees for i - Before creating a new worktree, check if one exists for the same issue - `git worktree list` shows all active worktrees - If found, reuse it (cd to the path, verify branch is correct, `git pull` to sync) -- Multiple agents can work in the same worktree concurrently if they modify different files +- Multiple agents can work in the same worktree concurrently if they modify different files. Untracked files are still at risk: a global `git stash` / `git clean` from one agent can delete another agent's not-yet-committed files — prefer per-issue worktrees for parallel background work **Cleanup:** - After a PR is merged, the worktree should be removed diff --git a/packages/squad-cli/templates/squad.agent.md.template b/packages/squad-cli/templates/squad.agent.md.template index abbfcaaed..eb545668f 100644 --- a/packages/squad-cli/templates/squad.agent.md.template +++ b/packages/squad-cli/templates/squad.agent.md.template @@ -509,6 +509,17 @@ When the user gives any task, the Coordinator MUST: ``` 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. +**Shared-worktree guard.** Before spawning 2+ background agents in one turn, check whether worktree mode is active (see Pre-Spawn: Worktree Setup). If it is NOT, show the user this warning before launching: + +``` +⚠️ Launching {N} parallel background agents in a shared worktree. + Global-scope git operations (stash, clean, restore) from one agent can + silently delete another agent's untracked files. Enable worktree mode + for per-stream isolation, or accept the risk for this wave. +``` + +Warn once per session, then proceed — this is a caution, not a gate. + **Example — "Team, build the login page":** - Turn 1: Spawn {Lead} (architecture), {Frontend} (UI), {Backend} (API), {Tester} (test cases from spec) — ALL background, ALL in one tool call - Collect results. Scribe merges decisions. diff --git a/packages/squad-cli/templates/worktree-reference.md b/packages/squad-cli/templates/worktree-reference.md index 958fee2e9..564a45d52 100644 --- a/packages/squad-cli/templates/worktree-reference.md +++ b/packages/squad-cli/templates/worktree-reference.md @@ -71,7 +71,7 @@ When worktree mode is enabled, the coordinator creates dedicated worktrees for i - Before creating a new worktree, check if one exists for the same issue - `git worktree list` shows all active worktrees - If found, reuse it (cd to the path, verify branch is correct, `git pull` to sync) -- Multiple agents can work in the same worktree concurrently if they modify different files +- Multiple agents can work in the same worktree concurrently if they modify different files. Untracked files are still at risk: a global `git stash` / `git clean` from one agent can delete another agent's not-yet-committed files — prefer per-issue worktrees for parallel background work **Cleanup:** - After a PR is merged, the worktree should be removed diff --git a/packages/squad-sdk/templates/squad.agent.md.template b/packages/squad-sdk/templates/squad.agent.md.template index abbfcaaed..eb545668f 100644 --- a/packages/squad-sdk/templates/squad.agent.md.template +++ b/packages/squad-sdk/templates/squad.agent.md.template @@ -509,6 +509,17 @@ When the user gives any task, the Coordinator MUST: ``` 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. +**Shared-worktree guard.** Before spawning 2+ background agents in one turn, check whether worktree mode is active (see Pre-Spawn: Worktree Setup). If it is NOT, show the user this warning before launching: + +``` +⚠️ Launching {N} parallel background agents in a shared worktree. + Global-scope git operations (stash, clean, restore) from one agent can + silently delete another agent's untracked files. Enable worktree mode + for per-stream isolation, or accept the risk for this wave. +``` + +Warn once per session, then proceed — this is a caution, not a gate. + **Example — "Team, build the login page":** - Turn 1: Spawn {Lead} (architecture), {Frontend} (UI), {Backend} (API), {Tester} (test cases from spec) — ALL background, ALL in one tool call - Collect results. Scribe merges decisions. diff --git a/packages/squad-sdk/templates/worktree-reference.md b/packages/squad-sdk/templates/worktree-reference.md index 958fee2e9..564a45d52 100644 --- a/packages/squad-sdk/templates/worktree-reference.md +++ b/packages/squad-sdk/templates/worktree-reference.md @@ -71,7 +71,7 @@ When worktree mode is enabled, the coordinator creates dedicated worktrees for i - Before creating a new worktree, check if one exists for the same issue - `git worktree list` shows all active worktrees - If found, reuse it (cd to the path, verify branch is correct, `git pull` to sync) -- Multiple agents can work in the same worktree concurrently if they modify different files +- Multiple agents can work in the same worktree concurrently if they modify different files. Untracked files are still at risk: a global `git stash` / `git clean` from one agent can delete another agent's not-yet-committed files — prefer per-issue worktrees for parallel background work **Cleanup:** - After a PR is merged, the worktree should be removed diff --git a/templates/squad.agent.md.template b/templates/squad.agent.md.template index abbfcaaed..eb545668f 100644 --- a/templates/squad.agent.md.template +++ b/templates/squad.agent.md.template @@ -509,6 +509,17 @@ When the user gives any task, the Coordinator MUST: ``` 5. **Chain follow-ups.** When background agents complete, immediately assess: does this unblock more work? Launch it without waiting for the user to ask. +**Shared-worktree guard.** Before spawning 2+ background agents in one turn, check whether worktree mode is active (see Pre-Spawn: Worktree Setup). If it is NOT, show the user this warning before launching: + +``` +⚠️ Launching {N} parallel background agents in a shared worktree. + Global-scope git operations (stash, clean, restore) from one agent can + silently delete another agent's untracked files. Enable worktree mode + for per-stream isolation, or accept the risk for this wave. +``` + +Warn once per session, then proceed — this is a caution, not a gate. + **Example — "Team, build the login page":** - Turn 1: Spawn {Lead} (architecture), {Frontend} (UI), {Backend} (API), {Tester} (test cases from spec) — ALL background, ALL in one tool call - Collect results. Scribe merges decisions. diff --git a/templates/worktree-reference.md b/templates/worktree-reference.md index 958fee2e9..564a45d52 100644 --- a/templates/worktree-reference.md +++ b/templates/worktree-reference.md @@ -71,7 +71,7 @@ When worktree mode is enabled, the coordinator creates dedicated worktrees for i - Before creating a new worktree, check if one exists for the same issue - `git worktree list` shows all active worktrees - If found, reuse it (cd to the path, verify branch is correct, `git pull` to sync) -- Multiple agents can work in the same worktree concurrently if they modify different files +- Multiple agents can work in the same worktree concurrently if they modify different files. Untracked files are still at risk: a global `git stash` / `git clean` from one agent can delete another agent's not-yet-committed files — prefer per-issue worktrees for parallel background work **Cleanup:** - After a PR is merged, the worktree should be removed diff --git a/test/ci/parallel-spawn-warning.test.ts b/test/ci/parallel-spawn-warning.test.ts new file mode 100644 index 000000000..db5e5f431 --- /dev/null +++ b/test/ci/parallel-spawn-warning.test.ts @@ -0,0 +1,64 @@ +/** + * CI tests for the shared-worktree spawn warning (#1014). + * + * A real incident showed that parallel background agents on a shared + * worktree can silently lose untracked files to another stream's global + * git operations (stash/clean/restore). The coordinator template must + * warn before launching 2+ background agents without worktree isolation, + * and the worktree reference must not claim shared-worktree concurrency + * is safe for untracked files. + * + * Canonical source: .squad-templates/ + */ + +import { describe, it, expect } from 'vitest'; +import { readFileSync } from 'node:fs'; +import { resolve, dirname } from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = resolve(__dirname, '../..'); + +function readTemplate(relPath: string): string { + return readFileSync(resolve(ROOT, relPath), 'utf-8'); +} + +describe('shared-worktree spawn warning contract (#1014)', () => { + const squadTemplate = readTemplate('.squad-templates/squad.agent.md'); + const worktreeReference = readTemplate('.squad-templates/worktree-reference.md'); + + it('keeps the shared-worktree guard inside the Parallel Fan-Out section', () => { + const fanOut = squadTemplate.slice( + squadTemplate.indexOf('### Parallel Fan-Out'), + squadTemplate.indexOf('### Shared File Architecture'), + ); + + expect(fanOut).toContain('**Shared-worktree guard.**'); + expect(fanOut).toContain('2+ background agents'); + expect(fanOut).toContain('Pre-Spawn: Worktree Setup'); + expect(fanOut).toContain('untracked files'); + expect(fanOut).toContain('a caution, not a gate'); + }); + + it('warns about stash/clean/restore specifically', () => { + expect(squadTemplate).toContain('stash, clean, restore'); + }); + + it('worktree reference no longer claims shared-worktree concurrency is safe for untracked files', () => { + expect(worktreeReference).toContain('Untracked files are still at risk'); + }); + + it('all governed copies of the coordinator template carry the guard', () => { + const copies = [ + '.github/agents/squad.agent.md', + 'templates/squad.agent.md.template', + 'packages/squad-cli/templates/squad.agent.md.template', + 'packages/squad-sdk/templates/squad.agent.md.template', + ]; + for (const copy of copies) { + expect(readTemplate(copy), `${copy} is missing the shared-worktree guard`).toContain( + '**Shared-worktree guard.**', + ); + } + }); +});