* fix(#853): gate manager/autonomous bg dispatch by runtime /gsd-manager and /gsd-autonomous --interactive dispatched Plan/Execute via Agent(run_in_background=true). On Claude Code a backgrounded agent has no Agent/Task tool, so it cannot spawn the nested subagents those pipelines need — per-plan worktree-isolated executors, the plan-checker, and the verifier. The phases reported complete but isolation and independent verification silently never ran, even with use_worktrees / plan_check / verifier enabled. Both workflows now resolve the runtime (config-get runtime, default claude) before dispatching: run plan/execute INLINE on Claude Code so the nested pipeline runs, and background-dispatch only on runtimes where a backgrounded agent can still nest. Mirrors execute-phase.md's existing Codex fail-closed precedent. Reconciles the stale unconditional background/overlap/lean-context claims elsewhere in both workflows and in the docs. Adds a content regression test pinning the gate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(#853): add changeset for runtime-gated bg dispatch Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/happy-finches-travel.md
Normal file
5
.changeset/happy-finches-travel.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 863
|
||||
---
|
||||
**`/gsd-manager` and `/gsd-autonomous --interactive` no longer silently skip worktree isolation and independent verification on Claude Code.** They dispatched plan/execute as background agents, but a backgrounded Claude Code agent has no Agent/Task tool and cannot spawn the nested executors, plan-checker, or verifier — so isolation and verification silently never ran. Both workflows now resolve the runtime and run plan/execute inline on Claude Code (background dispatch is kept on runtimes that support nested subagents).
|
||||
@@ -546,7 +546,7 @@ Interactive command center for managing multiple phases from one terminal.
|
||||
**Behavior:**
|
||||
- Dashboard of all phases with visual status indicators
|
||||
- Recommends optimal next actions based on dependencies and progress
|
||||
- Dispatches work: discuss runs inline, plan/execute run as background agents
|
||||
- Dispatches work: discuss runs inline; plan/execute run as background agents on runtimes that support nested background dispatch, or inline on Claude Code
|
||||
- Designed for power users parallelizing work across phases from one terminal
|
||||
- Supports per-step passthrough flags via `manager.flags` config (see [Configuration](CONFIGURATION.md#manager-passthrough-flags))
|
||||
|
||||
|
||||
@@ -1986,18 +1986,18 @@ Test suite that scans all agent, workflow, and command files for embedded inject
|
||||
|
||||
**Flag:** `/gsd-autonomous --interactive`
|
||||
|
||||
**Purpose:** Lean-context autonomous mode that keeps discuss-phase interactive (user answers questions) while dispatching plan and execute as background agents.
|
||||
**Purpose:** Lean-context autonomous mode that keeps discuss-phase interactive (user answers questions) while dispatching plan and execute as background agents on runtimes that support nested background dispatch; on Claude Code, plan and execute run inline to preserve worktree isolation and independent verification.
|
||||
|
||||
**Requirements:**
|
||||
- REQ-INTERACT-01: `--interactive` MUST run discuss-phase inline with interactive questions (not auto-answered)
|
||||
- REQ-INTERACT-02: `--interactive` MUST dispatch plan-phase and execute-phase as background agents for context isolation
|
||||
- REQ-INTERACT-03: `--interactive` MUST enable pipeline parallelism — discuss Phase N+1 while Phase N builds
|
||||
- REQ-INTERACT-04: Main context MUST only accumulate discuss conversations (lean context)
|
||||
- REQ-INTERACT-02: `--interactive` MUST dispatch plan-phase and execute-phase as background agents for context isolation on runtimes where a backgrounded agent can spawn subagents; on Claude Code, plan and execute run inline
|
||||
- REQ-INTERACT-03: `--interactive` MUST enable pipeline parallelism — discuss Phase N+1 while Phase N builds (applies on runtimes that support nested background dispatch; on Claude Code, discuss does not overlap planning/execution)
|
||||
- REQ-INTERACT-04: Main context MUST only accumulate discuss conversations (lean context) on runtimes that support nested background dispatch; on Claude Code, inline plan/execute also accumulate in the main context
|
||||
|
||||
**Process:**
|
||||
1. **Discuss inline** — Run discuss-phase in the main context with user interaction
|
||||
2. **Dispatch** — Send plan and execute to background agents with fresh context windows
|
||||
3. **Pipeline** — While background agents build Phase N, begin discussing Phase N+1
|
||||
2. **Dispatch** — On runtimes that support nested background dispatch: send plan and execute to background agents with fresh context windows. On Claude Code: run plan and execute inline.
|
||||
3. **Pipeline** — On runtimes with background dispatch: while background agents build Phase N, begin discussing Phase N+1. On Claude Code: phases run sequentially.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ By default, autonomous mode answers discuss questions automatically using smart
|
||||
|
||||
In interactive mode:
|
||||
- `/gsd-discuss-phase` runs inline and waits for your answers
|
||||
- Planning and execution are dispatched as background agents so you can discuss the next phase while the current one builds
|
||||
- The main context stays lean — only discuss conversations accumulate
|
||||
- On runtimes that support nested background dispatch, planning and execution are dispatched as background agents so you can discuss the next phase while the current one builds; on Claude Code, planning and execution run inline (the next phase's discuss does not overlap)
|
||||
- The main context stays lean — only discuss conversations accumulate (on runtimes with background dispatch; on Claude Code, inline plan/execute also accumulate)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ fi
|
||||
|
||||
When `--only` is set, also set `FROM_PHASE` to the same value so existing filter logic applies.
|
||||
|
||||
When `--interactive` is set, discuss runs inline with questions (not auto-answered), while plan and execute are dispatched as background agents. This keeps the main context lean — only discuss conversations accumulate — while preserving user input on all design decisions.
|
||||
When `--interactive` is set, discuss runs inline with questions (not auto-answered). On runtimes where a backgrounded agent can spawn subagents, plan and execute are dispatched as background agents — keeping the main context lean (only discuss conversations accumulate) and enabling overlap. On Claude Code, where a backgrounded agent cannot nest subagents, plan and execute run inline to preserve worktree isolation and independent verification, so they run sequentially and their work accumulates in the main context. Either way, user input is preserved on all design decisions.
|
||||
|
||||
Bootstrap via milestone-level init:
|
||||
|
||||
@@ -322,9 +322,21 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1)
|
||||
|
||||
**3b. Plan**
|
||||
|
||||
**If `INTERACTIVE` is set:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
||||
**If `INTERACTIVE` is set:** Background dispatch is only safe where a backgrounded agent can still spawn subagents. On Claude Code a backgrounded agent has no `Agent`/`Task` tool, so the plan-checker never runs and `workflow.plan_check` silently degrades to a self-check. Resolve the runtime first:
|
||||
|
||||
Print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
||||
```bash
|
||||
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
||||
```
|
||||
|
||||
- **On Claude Code (`RUNTIME` is `claude`):** Run plan **inline** (do NOT background) so the plan-checker runs. The next phase's discuss does not overlap planning here — correctness over overlap.
|
||||
|
||||
```
|
||||
Skill(skill="gsd-plan-phase", args="${PHASE_NUM}")
|
||||
```
|
||||
|
||||
- **On other runtimes:** Dispatch plan as a background agent to keep the main context lean. While plan runs, the workflow can immediately start discussing the next phase (see step 4).
|
||||
|
||||
Print: `◆ Spawning background planner for phase ${PHASE_NUM}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)`
|
||||
|
||||
```
|
||||
Agent(
|
||||
@@ -334,7 +346,7 @@ Agent(
|
||||
)
|
||||
```
|
||||
|
||||
Store the agent task_id. After discuss for the next phase completes (or if no next phase), wait for the plan agent to finish before proceeding to execute.
|
||||
Store the agent task_id. After discuss for the next phase completes (or if no next phase), wait for the plan agent to finish before proceeding to execute.
|
||||
|
||||
**If `INTERACTIVE` is NOT set (default):** Run plan inline as before.
|
||||
|
||||
@@ -346,7 +358,19 @@ Verify plan produced output — re-run `init phase-op` and check `has_plans`. If
|
||||
|
||||
**3c. Execute**
|
||||
|
||||
**If `INTERACTIVE` is set:** Wait for the plan agent to complete (if not already), verify plans exist, then dispatch execute as a background agent:
|
||||
**If `INTERACTIVE` is set:** Wait for the plan agent to complete (if not already) and verify plans exist. Background dispatch is only safe where a backgrounded agent can still spawn subagents. On Claude Code a backgrounded agent has no `Agent`/`Task` tool, so the per-plan worktree-isolated executors and the verifier never run (`workflow.use_worktrees` and `workflow.verifier` silently degrade). Resolve the runtime first:
|
||||
|
||||
```bash
|
||||
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
||||
```
|
||||
|
||||
- **On Claude Code (`RUNTIME` is `claude`):** Run execute **inline** (do NOT background) so worktree isolation and verification run:
|
||||
|
||||
```
|
||||
Skill(skill="gsd-execute-phase", args="${PHASE_NUM} --no-transition")
|
||||
```
|
||||
|
||||
- **On other runtimes:** Dispatch execute as a background agent:
|
||||
|
||||
```
|
||||
Agent(
|
||||
@@ -356,7 +380,7 @@ Agent(
|
||||
)
|
||||
```
|
||||
|
||||
Store the agent task_id. The workflow can now start discussing the next phase while this phase executes in the background. Before starting post-execution routing for this phase, wait for the execute agent to complete.
|
||||
Store the agent task_id. The workflow can now start discussing the next phase while this phase executes in the background. Before starting post-execution routing for this phase, wait for the execute agent to complete.
|
||||
|
||||
**If `INTERACTIVE` is NOT set (default):** Run execute inline as before.
|
||||
|
||||
@@ -572,12 +596,12 @@ Check for blockers in the Blockers/Concerns section. If blockers are found, go t
|
||||
|
||||
If incomplete phases remain: proceed to next phase, loop back to execute_phase.
|
||||
|
||||
**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism:
|
||||
**Interactive mode overlap:** When `INTERACTIVE` is set, the iterate step enables pipeline parallelism **on runtimes where a backgrounded agent can spawn subagents** (on Claude Code, plan/execute run inline — see 3b/3c — so there is no overlap and phases run sequentially):
|
||||
1. After discuss completes for Phase N, dispatch plan+execute as background agents
|
||||
2. Immediately start discuss for Phase N+1 (the next incomplete phase) while Phase N builds
|
||||
3. Before starting plan for Phase N+1, wait for Phase N's execute agent to complete and handle its post-execution routing (verification, gap closure, etc.)
|
||||
|
||||
This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents.
|
||||
This means the user is always answering discuss questions (lightweight, interactive) while the heavy work (planning, code generation) runs in the background. The main context only accumulates discuss conversations — plan and execute contexts are isolated in their agents. (On Claude Code, plan and execute run inline, so they run sequentially and their work accumulates in the main context.)
|
||||
|
||||
If all phases complete, proceed to lifecycle step.
|
||||
|
||||
@@ -789,9 +813,9 @@ When any phase operation fails or a blocker is detected, present 3 options via A
|
||||
- [ ] `--to N` handle_blocker resume message preserves --to flag
|
||||
- [ ] `--to N` skips lifecycle when not all milestone phases complete
|
||||
- [ ] `--interactive` runs discuss inline via gsd-discuss-phase (asks questions, waits for user)
|
||||
- [ ] `--interactive` dispatches plan and execute as background agents (context isolation)
|
||||
- [ ] `--interactive` enables pipeline parallelism: discuss Phase N+1 while Phase N builds
|
||||
- [ ] `--interactive` main context only accumulates discuss conversations (lean)
|
||||
- [ ] `--interactive` dispatches plan and execute as background agents on runtimes that support nested background dispatch; runs them inline on Claude Code
|
||||
- [ ] `--interactive` enables pipeline parallelism (discuss Phase N+1 while Phase N builds) on runtimes with background dispatch; phases run sequentially on Claude Code
|
||||
- [ ] `--interactive` main context only accumulates discuss conversations on runtimes with background dispatch (on Claude Code, inline plan/execute also accumulate)
|
||||
- [ ] `--interactive` waits for background agents before post-execution routing
|
||||
- [ ] `--interactive` compatible with `--only`, `--from`, and `--to` flags
|
||||
</success_criteria>
|
||||
|
||||
@@ -219,16 +219,18 @@ Go to exit step.
|
||||
|
||||
### Compound Action (background + inline)
|
||||
|
||||
When the user selects a compound option:
|
||||
When the user selects a compound option, behavior depends on the runtime — the Plan Phase N / Execute Phase N handlers below resolve it via `gsd_run query config-get runtime`:
|
||||
|
||||
1. **Spawn all background agents first** (plan/execute) — dispatch them in parallel using the Plan Phase N / Execute Phase N handlers below.
|
||||
2. **Then run the inline discuss:**
|
||||
- **On Claude Code:** a backgrounded agent cannot nest the pipeline's subagents, so run the chosen plan/execute step(s) **inline** via their handlers below (in order), then run the inline discuss. There is no overlap.
|
||||
- **On other runtimes:** **Spawn all background agents first** (plan/execute) — dispatch them in parallel using the Plan Phase N / Execute Phase N handlers below — then run the inline discuss; the background agents continue while you discuss.
|
||||
|
||||
Inline discuss:
|
||||
|
||||
```
|
||||
Skill(skill="gsd-discuss-phase", args="{PHASE_NUM} {manager_flags.discuss}")
|
||||
```
|
||||
|
||||
After discuss completes, loop back to dashboard step (background agents continue running).
|
||||
After discuss completes, loop back to dashboard step.
|
||||
|
||||
### Discuss Phase N
|
||||
|
||||
@@ -242,7 +244,27 @@ After discuss completes, loop back to dashboard step.
|
||||
|
||||
### Plan Phase N
|
||||
|
||||
Planning runs autonomously. Spawn a background agent that delegates to the Skill pipeline with any configured flags:
|
||||
Planning runs autonomously. **First resolve the runtime.** On Claude Code a backgrounded agent has no `Agent`/`Task` tool, so it cannot spawn the plan-checker the pipeline relies on — backgrounding it there silently turns `workflow.plan_check` into a self-check. So run plan **inline** on Claude Code, and **background** it only on runtimes where a backgrounded agent can still nest subagents.
|
||||
|
||||
```bash
|
||||
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
||||
```
|
||||
|
||||
**If `RUNTIME` is `claude` (Claude Code):** Run plan inline so the plan-checker and quality gates actually run — do NOT wrap it in `Agent(run_in_background=true, …)`:
|
||||
|
||||
```
|
||||
Skill(skill="gsd-plan-phase", args="{N} --auto {manager_flags.plan}")
|
||||
```
|
||||
|
||||
Display while it runs:
|
||||
|
||||
```
|
||||
◆ Planning Phase {N}: {phase_name}... (runs inline so the plan-checker runs — the dashboard resumes when it returns, ~1–5 min; expected, not a freeze)
|
||||
```
|
||||
|
||||
Then loop back to dashboard step.
|
||||
|
||||
**If `RUNTIME` is not `claude` (e.g. Codex):** Spawn a background agent that delegates to the Skill pipeline with any configured flags:
|
||||
|
||||
```
|
||||
Agent(
|
||||
@@ -264,7 +286,7 @@ Important: You are running in the background. Do NOT use AskUserQuestion — mak
|
||||
)
|
||||
```
|
||||
|
||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above with `run_in_background=true`, do NOT do any planning work for this phase independently. Return to the dashboard immediately and wait for the background agent to report back. Only resume planning-related work when the subagent result is available.
|
||||
> **ORCHESTRATOR RULE — NON-CLAUDE RUNTIME**: After calling Agent() above with `run_in_background=true`, do NOT do any planning work for this phase independently. Return to the dashboard immediately and wait for the background agent to report back. Only resume planning-related work when the subagent result is available.
|
||||
|
||||
Display:
|
||||
|
||||
@@ -276,7 +298,27 @@ Loop back to dashboard step.
|
||||
|
||||
### Execute Phase N
|
||||
|
||||
Execution runs autonomously. Spawn a background agent that delegates to the Skill pipeline with any configured flags:
|
||||
Execution runs autonomously. **First resolve the runtime.** On Claude Code a backgrounded agent has no `Agent`/`Task` tool, so it cannot spawn the per-plan worktree-isolated executors or the verifier — backgrounding it there silently disables `workflow.use_worktrees` isolation and `workflow.verifier`. So run execute **inline** on Claude Code, and **background** it only on runtimes where a backgrounded agent can still nest subagents.
|
||||
|
||||
```bash
|
||||
RUNTIME=$(gsd_run query config-get runtime --default claude 2>/dev/null || echo "claude")
|
||||
```
|
||||
|
||||
**If `RUNTIME` is `claude` (Claude Code):** Run execute inline so worktree isolation and the verifier actually run — do NOT wrap it in `Agent(run_in_background=true, …)`:
|
||||
|
||||
```
|
||||
Skill(skill="gsd-execute-phase", args="{N} {manager_flags.execute}")
|
||||
```
|
||||
|
||||
Display while it runs:
|
||||
|
||||
```
|
||||
◆ Executing Phase {N}: {phase_name}... (runs inline so worktree isolation and verification run — the dashboard resumes when it returns; expected, not a freeze)
|
||||
```
|
||||
|
||||
Then loop back to dashboard step.
|
||||
|
||||
**If `RUNTIME` is not `claude` (e.g. Codex):** Spawn a background agent that delegates to the Skill pipeline with any configured flags:
|
||||
|
||||
```
|
||||
Agent(
|
||||
@@ -298,7 +340,7 @@ Important: You are running in the background. Do NOT use AskUserQuestion — mak
|
||||
)
|
||||
```
|
||||
|
||||
> **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above with `run_in_background=true`, do NOT do any execution work for this phase independently. Return to the dashboard immediately and wait for the background agent to report back. Only resume execution-related work when the subagent result is available.
|
||||
> **ORCHESTRATOR RULE — NON-CLAUDE RUNTIME**: After calling Agent() above with `run_in_background=true`, do NOT do any execution work for this phase independently. Return to the dashboard immediately and wait for the background agent to report back. Only resume execution-related work when the subagent result is available.
|
||||
|
||||
Display:
|
||||
|
||||
|
||||
46
tests/bug-853-bg-dispatch-runtime-gating.test.cjs
Normal file
46
tests/bug-853-bg-dispatch-runtime-gating.test.cjs
Normal file
@@ -0,0 +1,46 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Regression guard — bug(#853): /gsd-manager and /gsd-autonomous --interactive
|
||||
* silently skipped worktree isolation + independent verification because they
|
||||
* dispatched Plan/Execute via Agent(run_in_background=true). On Claude Code a
|
||||
* backgrounded agent has no Agent/Task tool, so it cannot spawn the nested
|
||||
* subagents (worktree executors, plan-checker, verifier). The workflows must
|
||||
* now resolve the runtime and run inline on Claude Code.
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const MANAGER = fs.readFileSync(path.join(WORKFLOWS_DIR, 'manager.md'), 'utf8');
|
||||
const AUTONOMOUS = fs.readFileSync(path.join(WORKFLOWS_DIR, 'autonomous.md'), 'utf8');
|
||||
|
||||
describe('bug-853 — manager/autonomous gate background dispatch by runtime', () => {
|
||||
test('manager.md resolves the runtime before dispatching plan/execute', () => {
|
||||
// Two dispatch sites (plan + execute), each must resolve the runtime.
|
||||
const matches = MANAGER.match(/config-get runtime/g) || [];
|
||||
assert.ok(matches.length >= 2, 'manager.md must resolve runtime for both plan and execute dispatch');
|
||||
});
|
||||
|
||||
test('manager.md documents why Claude Code cannot background-dispatch', () => {
|
||||
assert.match(MANAGER, /backgrounded agent has no `Agent`\/`Task` tool/);
|
||||
});
|
||||
|
||||
test('manager.md runs plan/execute inline on Claude Code', () => {
|
||||
assert.match(MANAGER, /If `RUNTIME` is `claude`[\s\S]{0,400}?Skill\(skill="gsd-plan-phase"/);
|
||||
assert.match(MANAGER, /If `RUNTIME` is `claude`[\s\S]{0,400}?Skill\(skill="gsd-execute-phase"/);
|
||||
});
|
||||
|
||||
test('autonomous.md gates interactive background dispatch by runtime', () => {
|
||||
const autoRuntimeMatches = AUTONOMOUS.match(/config-get runtime/g) || [];
|
||||
assert.ok(autoRuntimeMatches.length >= 2, 'autonomous.md must resolve runtime in both 3b (plan) and 3c (execute) interactive branches');
|
||||
assert.match(AUTONOMOUS, /backgrounded agent has no `Agent`\/`Task` tool/);
|
||||
});
|
||||
|
||||
test('autonomous.md runs plan/execute inline on Claude Code in interactive mode', () => {
|
||||
assert.match(AUTONOMOUS, /On Claude Code \(`RUNTIME` is `claude`\)[\s\S]{0,400}?Skill\(skill="gsd-plan-phase"/);
|
||||
assert.match(AUTONOMOUS, /On Claude Code \(`RUNTIME` is `claude`\)[\s\S]{0,400}?Skill\(skill="gsd-execute-phase"/);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user