diff --git a/.changeset/566-spawn-liveness-banner.md b/.changeset/566-spawn-liveness-banner.md new file mode 100644 index 000000000..233452bc2 --- /dev/null +++ b/.changeset/566-spawn-liveness-banner.md @@ -0,0 +1,5 @@ +--- +type: Changed +pr: 566 +--- +**Spawn announcements now include a liveness note** — every `◆ Spawning …` line and subagent dispatch instruction across all 26+ GSD workflows now carries the canonical phrase `runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze`. Silent subagents are visually identical to frozen sessions; this inline note sets the expectation so users wait instead of interrupting. Documented in `references/ui-brand.md § Spawning Indicators`, enforced by `tests/spawn-liveness-banner.test.cjs`, and explained in `docs/USER-GUIDE.md § Troubleshooting`. diff --git a/docs/USER-GUIDE.md b/docs/USER-GUIDE.md index 03dd832f1..de22e0fad 100644 --- a/docs/USER-GUIDE.md +++ b/docs/USER-GUIDE.md @@ -1126,6 +1126,14 @@ Always read a file before editing it. Never call Edit or Write on a file you hav You ran `/gsd-new-project` but `.planning/PROJECT.md` already exists. This is a safety check. If you want to start over, delete the `.planning/` directory first. +### A Command Looks Frozen After "Spawning..." + +If you see `◆ Spawning researcher...` (or any "Spawning…" line) and then nothing — no output, no spinner — for 1–5 minutes, **this is normal**. GSD subagents run in a separate context window; their work is invisible to the parent session while in progress. The liveness note on the spawn line confirms this: "(runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)". + +**Do not interrupt the session.** Killing it discards the in-progress subagent work and forces you to restart that step. Wait for the result to appear. Research and planning agents routinely take 1–5 minutes; verification agents can take longer on large phases. + +**If it truly seems stuck** (>10 minutes with no result): check whether Claude Code's agent task is still active in the sidebar. If the task shows as completed but no output appeared, the result may have been lost in a context switch — run the command again. + ### Context Degradation During Long Sessions Clear your context window between major commands: `/clear` in Claude Code. GSD is designed around fresh contexts -- every subagent gets a clean 200K window. If quality is dropping in the main session, clear and use `/gsd-resume-work` or `/gsd-progress` to restore state. diff --git a/get-shit-done/references/ui-brand.md b/get-shit-done/references/ui-brand.md index 47e6f741c..9a9676b78 100644 --- a/get-shit-done/references/ui-brand.md +++ b/get-shit-done/references/ui-brand.md @@ -83,10 +83,12 @@ Plans: 3/5 complete ## Spawning Indicators -``` -◆ Spawning researcher... +**Liveness convention:** Every spawn announcement must carry the canonical phrase `runs in a subagent` inline so users know that silence during a subagent run is expected. Without this, a healthy 1–5 minute agent looks identical to a frozen session. Single spawns use the singular form; parallel spawns use the plural form. -◆ Spawning 4 researchers in parallel... +``` +◆ Spawning researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) + +◆ Spawning 4 researchers in parallel... (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze) → Stack research → Features research → Architecture research diff --git a/get-shit-done/workflows/audit-fix.md b/get-shit-done/workflows/audit-fix.md index e6474bbe1..884d75ae9 100644 --- a/get-shit-done/workflows/audit-fix.md +++ b/get-shit-done/workflows/audit-fix.md @@ -94,7 +94,7 @@ final output — do not proceed to fixing. For each **auto-fixable** finding (up to `--max`, ordered by severity desc): -**a. Spawn executor agent:** +**a. Spawn executor agent** (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)**:** ``` Agent( prompt="Fix finding {ID}: {description}. Files: {file_refs}. Make the minimal change to resolve this specific finding. Do not refactor surrounding code.", diff --git a/get-shit-done/workflows/audit-milestone.md b/get-shit-done/workflows/audit-milestone.md index 709fe2278..579349c4c 100644 --- a/get-shit-done/workflows/audit-milestone.md +++ b/get-shit-done/workflows/audit-milestone.md @@ -67,6 +67,8 @@ With phase context collected: Extract `MILESTONE_REQ_IDS` from REQUIREMENTS.md traceability table — all REQ-IDs assigned to phases in this milestone. +Print: "Spawning integration checker (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)" + ``` Agent( prompt="Check cross-phase integration and E2E flows. diff --git a/get-shit-done/workflows/autonomous.md b/get-shit-done/workflows/autonomous.md index cae91ebfd..70e9f346d 100644 --- a/get-shit-done/workflows/autonomous.md +++ b/get-shit-done/workflows/autonomous.md @@ -324,6 +324,8 @@ UI_SPEC_FILE=$(ls "${PHASE_DIR}"/*-UI-SPEC.md 2>/dev/null | head -1) **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). +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( description="Plan phase ${PHASE_NUM}: ${PHASE_NAME}", diff --git a/get-shit-done/workflows/code-review-fix.md b/get-shit-done/workflows/code-review-fix.md index eedb60e11..3fa6e211f 100644 --- a/get-shit-done/workflows/code-review-fix.md +++ b/get-shit-done/workflows/code-review-fix.md @@ -179,7 +179,7 @@ If REVIEW.md contains a `files_reviewed_list` frontmatter field, use that as the -Spawn the gsd-code-fixer agent with config: +Spawn the gsd-code-fixer agent with config (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ```bash # Build config for agent @@ -271,7 +271,7 @@ if [ "$AUTO_MODE" = "true" ]; then done fi - # Spawn gsd-code-reviewer agent to re-review + # Spawn gsd-code-reviewer agent to re-review (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) # (This overwrites REVIEW_PATH with latest review state) Agent(subagent_type="gsd-code-reviewer", prompt=" @@ -304,7 +304,7 @@ Do NOT commit the output — the orchestrator handles that. break fi - # Still has issues — spawn fixer again + # Still has issues — spawn fixer again (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) echo "Issues remain. Applying fixes for iteration ${ITERATION}..." Agent(subagent_type="gsd-code-fixer", prompt=" diff --git a/get-shit-done/workflows/code-review.md b/get-shit-done/workflows/code-review.md index 974395668..9d41b6426 100644 --- a/get-shit-done/workflows/code-review.md +++ b/get-shit-done/workflows/code-review.md @@ -441,6 +441,8 @@ fi Spawn the gsd-code-reviewer agent: +Print: `◆ Spawning code reviewer... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + ``` Agent(subagent_type="gsd-code-reviewer", prompt=" diff --git a/get-shit-done/workflows/debug.md b/get-shit-done/workflows/debug.md index dec8d8495..bae8e6568 100644 --- a/get-shit-done/workflows/debug.md +++ b/get-shit-done/workflows/debug.md @@ -101,7 +101,7 @@ Eliminated: {count} Surface to user. Then delegate directly to the session manager (skip Steps 2 and 3 — pass `symptoms_prefilled: true` and set the slug from SLUG variable). The existing file IS the context. -Print before spawning: +Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ``` [debug] Session: .planning/debug/{SLUG}.md [debug] Status: {status} @@ -190,6 +190,11 @@ Create `.planning/debug/{slug}.md` with initial state using the Write tool (neve After initial context setup, spawn the session manager to handle the full checkpoint/continuation loop. The session manager handles specialist_hint dispatch internally: when gsd-debugger returns ROOT CAUSE FOUND it extracts the specialist_hint field and invokes the matching skill (e.g. typescript-expert, swift-concurrency) before offering fix options. +Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): +``` +[debug] Delegating loop to session manager... +``` + ``` Agent( prompt=""" diff --git a/get-shit-done/workflows/diagnose-issues.md b/get-shit-done/workflows/diagnose-issues.md index 003f84d2b..06a19ca05 100644 --- a/get-shit-done/workflows/diagnose-issues.md +++ b/get-shit-done/workflows/diagnose-issues.md @@ -96,6 +96,8 @@ EXPECTED_BASE=$(git rev-parse HEAD) For each gap, fill the debug-subagent-prompt template and spawn: +Print: `◆ Spawning diagnostics agent... (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze)` + ``` Agent( prompt=filled_debug_subagent_prompt + "\n\n\nFIRST ACTION: assert this is a disposable worktree branch before any repair. Run:\n```bash\nHEAD_REF=$(git symbolic-ref --quiet HEAD || echo \"DETACHED\")\nACTUAL_BRANCH=$(git rev-parse --abbrev-ref HEAD)\nif [ \"$HEAD_REF\" = \"DETACHED\" ] || echo \"$ACTUAL_BRANCH\" | grep -Eq '^(main|master|develop|trunk|release/.*)$'; then\n echo \"FATAL: diagnose worktree HEAD on '$ACTUAL_BRANCH'; refusing reset --hard on a protected branch.\" >&2\n exit 1\nfi\nif ! echo \"$ACTUAL_BRANCH\" | grep -Eq '^worktree-agent-[A-Za-z0-9._/-]+$'; then\n echo \"FATAL: diagnose worktree HEAD '$ACTUAL_BRANCH' is not in the worktree-agent-* namespace; refusing reset --hard.\" >&2\n exit 1\nfi\nACTUAL_BASE=$(git merge-base HEAD {EXPECTED_BASE})\nif [ \"$ACTUAL_BASE\" != \"{EXPECTED_BASE}\" ]; then\n git reset --hard {EXPECTED_BASE}\n [ \"$(git rev-parse HEAD)\" != \"{EXPECTED_BASE}\" ] && { echo \"ERROR: Could not correct worktree base\"; exit 1; }\nfi\n```\nFixes EnterWorktree creating branches from main on all platforms while preventing protected-branch data loss.\n\n\n\n- {phase_dir}/{phase_num}-UAT.md\n- .planning/STATE.md\n\n${AGENT_SKILLS_DEBUGGER}", diff --git a/get-shit-done/workflows/discuss-phase-assumptions.md b/get-shit-done/workflows/discuss-phase-assumptions.md index b674b7883..2ccf8ed24 100644 --- a/get-shit-done/workflows/discuss-phase-assumptions.md +++ b/get-shit-done/workflows/discuss-phase-assumptions.md @@ -252,7 +252,7 @@ Map to calibration tier: If no USER-PROFILE.md: calibration_tier = "standard" -**Spawn Explore subagent:** +**Spawn Explore subagent** (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)**:** ``` Agent(subagent_type="gsd-assumptions-analyzer", prompt=""" @@ -310,7 +310,7 @@ Parse the subagent's response. Extract: **Skip if:** `needs_research` from deep_codebase_analysis is empty. -If research topics were flagged, spawn a general-purpose research agent: +If research topics were flagged, spawn a general-purpose research agent (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ``` Agent(subagent_type="general-purpose", prompt=""" diff --git a/get-shit-done/workflows/discuss-phase/modes/advisor.md b/get-shit-done/workflows/discuss-phase/modes/advisor.md index b6be0d09b..f6a75c883 100644 --- a/get-shit-done/workflows/discuss-phase/modes/advisor.md +++ b/get-shit-done/workflows/discuss-phase/modes/advisor.md @@ -86,7 +86,7 @@ This reframing applies to: After the user selects gray areas in `present_gray_areas`, spawn parallel research agents. -1. Display brief status: `Researching {N} areas...` +1. Display brief status: `Researching {N} areas...` (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze) 2. For EACH user-selected gray area, spawn a `Agent()` in parallel: diff --git a/get-shit-done/workflows/docs-update.md b/get-shit-done/workflows/docs-update.md index caf274440..0498bc4f6 100644 --- a/get-shit-done/workflows/docs-update.md +++ b/get-shit-done/workflows/docs-update.md @@ -374,7 +374,7 @@ After all decisions recorded, continue to detect_runtime_capabilities. **Read the work manifest first:** `Read .planning/tmp/docs-work-manifest.json` — use `canonical_queue` items with `wave: 1` for this step. -Spawn 3 parallel gsd-doc-writer agents for Wave 1 docs: README, ARCHITECTURE, CONFIGURATION. +Spawn 3 parallel gsd-doc-writer agents for Wave 1 docs: README, ARCHITECTURE, CONFIGURATION (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze). These are foundational docs with no cross-references needed, making them ideal for parallel generation. @@ -831,7 +831,8 @@ Extract `canonical_queue` (items with `status: "completed"`) and `review_queue` For each doc in `canonical_queue` that was successfully written to disk: -1. Spawn the `gsd-doc-verifier` agent (or invoke sequentially if Task tool is unavailable) with a `` block: +1. Print: `◆ Spawning doc verifier for {doc_path}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + Spawn the `gsd-doc-verifier` agent (or invoke sequentially if Task tool is unavailable) with a `` block: ```xml doc_path: {relative path to the doc file, e.g. README.md} @@ -849,7 +850,8 @@ This is NOT optional. Every doc in `review_queue` MUST be verified. For each doc in `review_queue` from the manifest: -1. Spawn the `gsd-doc-verifier` agent with the same `` block as above. +1. Print: `◆ Spawning doc verifier for {doc_path}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + Spawn the `gsd-doc-verifier` agent with the same `` block as above. 2. Read the result JSON from `.planning/tmp/verify-{doc_filename}.json`. 3. Update the manifest: set `status: "verified"` for each review_queue doc processed. diff --git a/get-shit-done/workflows/eval-review.md b/get-shit-done/workflows/eval-review.md index 19ad488f8..c39705225 100644 --- a/get-shit-done/workflows/eval-review.md +++ b/get-shit-done/workflows/eval-review.md @@ -73,7 +73,7 @@ Build file list for auditor: ## 3. Spawn gsd-eval-auditor ``` -◆ Spawning eval auditor... +◆ Spawning eval auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Build prompt: diff --git a/get-shit-done/workflows/execute-phase.md b/get-shit-done/workflows/execute-phase.md index 213d8a574..67810c036 100644 --- a/get-shit-done/workflows/execute-phase.md +++ b/get-shit-done/workflows/execute-phase.md @@ -491,7 +491,7 @@ increases monotonically across waves. `{status}` is `complete` (success), **{Plan ID}: {Plan Name}** {2-3 sentences: what this builds, technical approach, why it matters} - Spawning {count} agent(s)... + Spawning {count} agent(s)... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) --- ``` diff --git a/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md b/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md index 648ea796d..1aa4957a3 100644 --- a/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md +++ b/get-shit-done/workflows/execute-phase/steps/codebase-drift-gate.md @@ -49,7 +49,7 @@ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-pars AGENT_SKILLS_MAPPER=$(gsd_run query agent-skills gsd-codebase-mapper) ``` -Then spawn `gsd-codebase-mapper` agents with the `--paths` hint: +Then spawn `gsd-codebase-mapper` agents with the `--paths` hint (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ```text Agent( diff --git a/get-shit-done/workflows/execute-plan.md b/get-shit-done/workflows/execute-plan.md index 39a487ced..704d37a72 100644 --- a/get-shit-done/workflows/execute-plan.md +++ b/get-shit-done/workflows/execute-plan.md @@ -92,7 +92,7 @@ Otherwise: Apply checkpoint-based routing below. | 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 → capture `EXPECTED_BASE=$(git rev-parse HEAD)` → spawn Agent(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`). **When using `isolation="worktree"`, include a `` block in the prompt** instructing the executor to: (1) FIRST assert `git symbolic-ref HEAD` resolves to a per-agent branch (NOT a protected ref like `main`/`master`/`develop`/`trunk`/`release/*`) and HALT with a blocker if not — never self-recover via `git update-ref refs/heads/` (#2924); (2) only after that assertion passes, run `git merge-base HEAD {EXPECTED_BASE}` and, if the result differs from `{EXPECTED_BASE}`, hard-reset the branch with `git reset --hard {EXPECTED_BASE}` before starting work, then verify with `[ "$(git rev-parse HEAD)" != "{EXPECTED_BASE}" ] && exit 1`. The HEAD assertion (Step 1) MUST run before any reset/checkout. This corrects a known issue where `EnterWorktree` creates branches from `main` instead of the feature branch HEAD (affects all platforms — #2015) and prevents the destructive HEAD-on-master self-recovery path (#2924). +**Pattern A:** init_agent_tracking → capture `EXPECTED_BASE=$(git rev-parse HEAD)` → print `Spawning executor agent (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` → spawn Agent(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`). **When using `isolation="worktree"`, include a `` block in the prompt** instructing the executor to: (1) FIRST assert `git symbolic-ref HEAD` resolves to a per-agent branch (NOT a protected ref like `main`/`master`/`develop`/`trunk`/`release/*`) and HALT with a blocker if not — never self-recover via `git update-ref refs/heads/` (#2924); (2) only after that assertion passes, run `git merge-base HEAD {EXPECTED_BASE}` and, if the result differs from `{EXPECTED_BASE}`, hard-reset the branch with `git reset --hard {EXPECTED_BASE}` before starting work, then verify with `[ "$(git rev-parse HEAD)" != "{EXPECTED_BASE}" ] && exit 1`. The HEAD assertion (Step 1) MUST run before any reset/checkout. This corrects a known issue where `EnterWorktree` creates branches from `main` instead of the feature branch HEAD (affects all platforms — #2015) and prevents the destructive HEAD-on-master self-recovery path (#2924). **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/explore.md b/get-shit-done/workflows/explore.md index d93635725..a6e73cf17 100644 --- a/get-shit-done/workflows/explore.md +++ b/get-shit-done/workflows/explore.md @@ -59,6 +59,8 @@ This would take ~30 seconds and might surface useful context. ``` If yes, spawn a research agent: + +Print: `◆ Spawning explorer... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` ``` Agent( prompt="Quick research: {specific_question}. Return 3-5 key findings, no more than 200 words.", diff --git a/get-shit-done/workflows/import.md b/get-shit-done/workflows/import.md index dda817932..cc516a159 100644 --- a/get-shit-done/workflows/import.md +++ b/get-shit-done/workflows/import.md @@ -197,6 +197,8 @@ Write the PLAN.md file to the target directory. Delegate validation to gsd-plan-checker: +Print: "Delegating to gsd-plan-checker (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)" + ``` Agent({ subagent_type: "gsd-plan-checker", diff --git a/get-shit-done/workflows/ingest-docs.md b/get-shit-done/workflows/ingest-docs.md index f19cc558b..49757d492 100644 --- a/get-shit-done/workflows/ingest-docs.md +++ b/get-shit-done/workflows/ingest-docs.md @@ -180,7 +180,7 @@ Collect the one-line confirmations from each classifier. If any classifier error -Spawn `gsd-doc-synthesizer` once: +Spawn `gsd-doc-synthesizer` once (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ``` Agent({ @@ -247,7 +247,7 @@ Proceed to routing silently, or optionally display `GSD > No conflicts. Auto-res Audit PROJECT.md field requirements that `gsd-roadmapper` expects. For fields derivable from `.planning/intel/SYNTHESIS.md` (project scope, goals/non-goals, constraints, locked decisions), synthesize from the intel. For fields NOT derivable (project name, developer-facing success metric, target runtime), prompt via `AskUserQuestion` one at a time — minimal question set, no interrogation. -Delegate to `gsd-roadmapper`: +Delegate to `gsd-roadmapper` (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze): ``` Agent({ diff --git a/get-shit-done/workflows/manager.md b/get-shit-done/workflows/manager.md index 032d69c8d..5187d3220 100644 --- a/get-shit-done/workflows/manager.md +++ b/get-shit-done/workflows/manager.md @@ -269,7 +269,7 @@ Important: You are running in the background. Do NOT use AskUserQuestion — mak Display: ``` -◆ Spawning planner for Phase {N}: {phase_name}... +◆ Spawning planner for Phase {N}: {phase_name}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Loop back to dashboard step. @@ -303,7 +303,7 @@ Important: You are running in the background. Do NOT use AskUserQuestion — mak Display: ``` -◆ Spawning executor for Phase {N}: {phase_name}... +◆ Spawning executor for Phase {N}: {phase_name}... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Loop back to dashboard step. diff --git a/get-shit-done/workflows/map-codebase.md b/get-shit-done/workflows/map-codebase.md index 22526067d..b63523bea 100644 --- a/get-shit-done/workflows/map-codebase.md +++ b/get-shit-done/workflows/map-codebase.md @@ -144,6 +144,8 @@ Use Agent tool with `subagent_type="gsd-codebase-mapper"`, `model="{mapper_model **CRITICAL:** Use the dedicated `gsd-codebase-mapper` agent, NOT `Explore` or `browser_subagent`. The mapper agent writes documents directly. +Print: "Spawning 4 parallel codebase mapper agents (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze)" + **Agent 1: Tech Focus** ```text diff --git a/get-shit-done/workflows/new-milestone.md b/get-shit-done/workflows/new-milestone.md index ba66cfe6c..43dc58fce 100644 --- a/get-shit-done/workflows/new-milestone.md +++ b/get-shit-done/workflows/new-milestone.md @@ -287,7 +287,7 @@ AskUserQuestion: "Research the domain ecosystem for new features before defining GSD ► RESEARCHING ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning 4 researchers in parallel... +◆ Spawning 4 researchers in parallel... (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze) → Stack, Features, Architecture, Pitfalls ``` @@ -455,7 +455,7 @@ gsd_run query commit "docs: define milestone v[X.Y] requirements" --files .plann GSD ► CREATING ROADMAP ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning roadmapper... +◆ Spawning roadmapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` **Starting phase number:** diff --git a/get-shit-done/workflows/new-project.md b/get-shit-done/workflows/new-project.md index 1676dbea2..c15226e5d 100644 --- a/get-shit-done/workflows/new-project.md +++ b/get-shit-done/workflows/new-project.md @@ -869,7 +869,7 @@ Check if this is greenfield or subsequent milestone: Display spawning indicator: ``` -◆ Spawning 4 researchers in parallel... +◆ Spawning 4 researchers in parallel... (each runs in a subagent — no output until they return, ~1–5 min; expected, not a freeze) → Stack research → Features research → Architecture research @@ -1265,7 +1265,7 @@ Display stage banner: GSD ► CREATING ROADMAP ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning roadmapper... +◆ Spawning roadmapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` **ROADMAP.md template — mode-aware emit.** When generating the initial ROADMAP.md: diff --git a/get-shit-done/workflows/plan-phase.md b/get-shit-done/workflows/plan-phase.md index 700efb366..ab5d33949 100644 --- a/get-shit-done/workflows/plan-phase.md +++ b/get-shit-done/workflows/plan-phase.md @@ -480,7 +480,7 @@ Display banner: GSD ► RESEARCHING PHASE {X} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning researcher... +◆ Spawning researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` ### Spawn gsd-phase-researcher @@ -815,7 +815,7 @@ Display banner: GSD ► PATTERN MAPPING PHASE {X} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning pattern mapper... +◆ Spawning pattern mapper... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Pattern mapper prompt: @@ -877,7 +877,7 @@ Display banner: GSD ► PLANNING PHASE {X} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning planner... +◆ Spawning planner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Planner prompt: @@ -1040,7 +1040,7 @@ fi Display: ```text -◆ Chunked mode: spawning outline planner... +◆ Chunked mode: spawning outline planner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Spawn the planner in **outline-only** mode — it must write only the outline manifest, not any @@ -1087,7 +1087,7 @@ For each plan entry extracted from `PLAN-OUTLINE.md`: 2. Display: ```text - ◆ Chunked mode: planning {plan_id} ({k}/{N})... + ◆ Chunked mode: planning {plan_id} ({k}/{N})... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` 3. Spawn the planner in **single-plan** mode — it must write exactly one PLAN.md file: @@ -1225,7 +1225,7 @@ Display banner: GSD ► VERIFYING PLANS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning plan checker... +◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Checker prompt: diff --git a/get-shit-done/workflows/plan-review-convergence.md b/get-shit-done/workflows/plan-review-convergence.md index f3a925147..f0b31773a 100644 --- a/get-shit-done/workflows/plan-review-convergence.md +++ b/get-shit-done/workflows/plan-review-convergence.md @@ -98,7 +98,7 @@ Display startup banner: **If `has_plans` is false:** -Display: `◆ No plans found — spawning initial planning agent...` +Display: `◆ No plans found — spawning initial planning agent... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` ```text Agent( @@ -134,7 +134,7 @@ prev_high_count = Infinity Increment `cycle`. -Display: `◆ Cycle {cycle}/{MAX_CYCLES} — spawning review agent...` +Display: `◆ Cycle {cycle}/{MAX_CYCLES} — spawning review agent... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` ```text Agent( @@ -308,7 +308,7 @@ Exit workflow. Update `prev_high_count = HIGH_COUNT`. -Display: `◆ Spawning replan agent with review feedback...` +Display: `◆ Spawning replan agent with review feedback... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` ```text Agent( diff --git a/get-shit-done/workflows/quick.md b/get-shit-done/workflows/quick.md index 6c127e0ad..fae302a55 100644 --- a/get-shit-done/workflows/quick.md +++ b/get-shit-done/workflows/quick.md @@ -392,7 +392,7 @@ Display banner: GSD ► RESEARCHING QUICK TASK ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Investigating approaches for: ${DESCRIPTION} +◆ Investigating approaches for: ${DESCRIPTION} (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Spawn a single focused researcher (not 4 parallel researchers like full phases — quick tasks need targeted research, not broad domain surveys): @@ -455,6 +455,8 @@ If research file not found, warn but continue: "Research agent did not produce o **If NOT `$VALIDATE_MODE`:** Use standard `quick` mode. +Display: `◆ Spawning planner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + ``` Agent( prompt=" @@ -518,7 +520,7 @@ Display banner: GSD ► CHECKING PLAN ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning plan checker... +◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Checker prompt: @@ -855,7 +857,7 @@ Display banner: GSD ► VERIFYING RESULTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning verifier... +◆ Spawning verifier... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` ``` diff --git a/get-shit-done/workflows/scan.md b/get-shit-done/workflows/scan.md index 0b8228cbc..a72f451ec 100644 --- a/get-shit-done/workflows/scan.md +++ b/get-shit-done/workflows/scan.md @@ -72,6 +72,8 @@ mkdir -p .planning/codebase Spawn a single `gsd-codebase-mapper` agent with the selected focus area: +Print: `◆ Spawning scanner... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + ``` Agent( prompt="Scan this codebase with focus: {focus}. Write results to .planning/codebase/. Produce only: {document_list}", diff --git a/get-shit-done/workflows/secure-phase.md b/get-shit-done/workflows/secure-phase.md index c5a9b1dfa..06a55ddac 100644 --- a/get-shit-done/workflows/secure-phase.md +++ b/get-shit-done/workflows/secure-phase.md @@ -93,6 +93,8 @@ Call AskUserQuestion with threat table and options: - `register_authored_at_plan_time: true` — **Verify mitigations exist** — do not scan for new threats. The register is complete; verify each threat's mitigation is present in the implementation. - `register_authored_at_plan_time: false` (retroactive-STRIDE mode) — **Retroactive-STRIDE: build a STRIDE register from implementation files first, then verify mitigations.** The phase was authored before formal threat modelling; the auditor must construct the register from scratch before verifying. +Print: `◆ Spawning security auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + ``` Agent( prompt="Read ~/.claude/agents/gsd-security-auditor.md for instructions.\n\n" + diff --git a/get-shit-done/workflows/ui-phase.md b/get-shit-done/workflows/ui-phase.md index 92e2381b8..7b3e11b1d 100644 --- a/get-shit-done/workflows/ui-phase.md +++ b/get-shit-done/workflows/ui-phase.md @@ -118,7 +118,7 @@ Display: GSD ► UI DESIGN CONTRACT — PHASE {N} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning UI researcher... +◆ Spawning UI researcher... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Build prompt: @@ -183,7 +183,7 @@ Display: GSD ► VERIFYING UI-SPEC ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning UI checker... +◆ Spawning UI checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Build prompt: diff --git a/get-shit-done/workflows/ui-review.md b/get-shit-done/workflows/ui-review.md index a6c328ad8..92fda0302 100644 --- a/get-shit-done/workflows/ui-review.md +++ b/get-shit-done/workflows/ui-review.md @@ -68,7 +68,7 @@ Build file list for auditor: ## 3. Spawn gsd-ui-auditor ``` -◆ Spawning UI auditor... +◆ Spawning UI auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Build prompt: diff --git a/get-shit-done/workflows/validate-phase.md b/get-shit-done/workflows/validate-phase.md index 937d343e6..878280c54 100644 --- a/get-shit-done/workflows/validate-phase.md +++ b/get-shit-done/workflows/validate-phase.md @@ -93,6 +93,8 @@ Call AskUserQuestion with gap table and options: ## 5. Spawn gsd-nyquist-auditor +Print: `◆ Spawning nyquist auditor... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze)` + ``` Agent( prompt="Read ~/.claude/agents/gsd-nyquist-auditor.md for instructions.\n\n" + diff --git a/get-shit-done/workflows/verify-work.md b/get-shit-done/workflows/verify-work.md index 950c65772..65bc517a4 100644 --- a/get-shit-done/workflows/verify-work.md +++ b/get-shit-done/workflows/verify-work.md @@ -552,7 +552,7 @@ Display: GSD ► PLANNING FIXES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning planner for gap closure... +◆ Spawning planner for gap closure... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Spawn gsd-planner in --gaps mode: @@ -602,7 +602,7 @@ Display: GSD ► VERIFYING FIX PLANS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ -◆ Spawning plan checker... +◆ Spawning plan checker... (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze) ``` Initialize: `iteration_count = 1` diff --git a/tests/spawn-liveness-banner.test.cjs b/tests/spawn-liveness-banner.test.cjs new file mode 100644 index 000000000..5c907a848 --- /dev/null +++ b/tests/spawn-liveness-banner.test.cjs @@ -0,0 +1,110 @@ +// allow-test-rule: source-text-is-the-product +// Tests that every GSD workflow that spawns a subagent carries the liveness phrase +// "runs in a subagent" on its spawn announcement lines. +// Canonical phrase defined in get-shit-done/references/ui-brand.md § Spawning Indicators. +// Regression test for https://github.com/open-gsd/gsd-core/issues/558. +// +// TEST STRATEGY: Two complementary assertions. +// +// 1. SPAWN-BANNER CHECK — any ◆ display line that contains the word "spawn" or "spawning" +// (case-insensitive, anywhere on the line) must carry the liveness phrase. This is more +// rigorous than a simple /◆\s+[Ss]pawning/ prefix match and catches all variants: +// "◆ Spawning researcher..." (spawn word right after ◆) +// "◆ Chunked mode: spawning planner..." (spawn word after prefix text) +// It does NOT match ◆ status/error lines that say "planner returned" or "checker wrote" +// (those don't contain the word "spawn"), so it has no false positives. +// +// 2. PRESENCE CHECK (coarser fallback) — if a file contains `subagent_type`, it must +// contain the liveness phrase at least once. Catches workflows that dispatch subagents +// without any ◆ spawn banner (e.g. prose-only "Spawn X" instructions). +// +// Together the two assertions are strictly stronger than the original file-level check: +// a file with 10 spawns where only one ◆ Spawning banner got the note still fails #1. +// A file with subagent_type but no banner at all still fails #2. + +const { test, describe } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows'); +const LIVENESS_PHRASE = 'runs in a subagent'; + +// Matches any ◆ line that contains "spawn" or "spawning" (case-insensitive, word anywhere). +// This covers: +// "◆ Spawning researcher..." → matched +// "◆ Chunked mode: spawning X..." → matched +// But NOT: +// "◆ Planner wrote N plan(s)..." → not matched (no "spawn" word) +// "◆ Research phase enabled" → not matched (no "spawn" word) +const SPAWN_BANNER_RE = /◆[^\n]*\bspawning?\b/i; + +function findMdFiles(dir) { + const entries = fs.readdirSync(dir, { withFileTypes: true }); + const files = []; + for (const entry of entries) { + const full = path.join(dir, entry.name); + if (entry.isDirectory()) { + files.push(...findMdFiles(full)); + } else if (entry.isFile() && entry.name.endsWith('.md')) { + files.push(full); + } + } + return files; +} + +describe('spawn-liveness-banner', () => { + test('every ◆ spawn announcement line carries the liveness phrase "runs in a subagent"', () => { + const mdFiles = findMdFiles(WORKFLOWS_DIR); + const bannerViolations = []; + + for (const filePath of mdFiles) { + const content = fs.readFileSync(filePath, 'utf-8'); + const lines = content.split('\n'); + const rel = path.relative(WORKFLOWS_DIR, filePath); + + for (let i = 0; i < lines.length; i++) { + const line = lines[i]; + if (SPAWN_BANNER_RE.test(line) && !line.includes(LIVENESS_PHRASE)) { + bannerViolations.push(`${rel}:${i + 1}: ${line.trim()}`); + } + } + } + + assert.deepStrictEqual( + bannerViolations, + [], + `The following ◆ spawn announcement lines are missing the liveness phrase "${LIVENESS_PHRASE}":\n` + + bannerViolations.map(v => ` - ${v}`).join('\n') + + '\n\nPer get-shit-done/references/ui-brand.md § "Spawning Indicators":\n' + + 'every ◆ spawn announcement must carry "runs in a subagent" so users know\n' + + 'that silence during a subagent run is expected and do not kill a healthy agent.\n' + + 'See https://github.com/open-gsd/gsd-core/issues/558' + ); + }); + + test('every workflow that dispatches a subagent contains the liveness phrase somewhere', () => { + const mdFiles = findMdFiles(WORKFLOWS_DIR); + const presenceViolations = []; + + for (const filePath of mdFiles) { + const content = fs.readFileSync(filePath, 'utf-8'); + const rel = path.relative(WORKFLOWS_DIR, filePath); + + if (content.includes('subagent_type') && !content.includes(LIVENESS_PHRASE)) { + presenceViolations.push(rel); + } + } + + assert.deepStrictEqual( + presenceViolations, + [], + `The following workflow files contain "subagent_type" but are missing the liveness phrase "${LIVENESS_PHRASE}" anywhere in the file:\n` + + presenceViolations.map(f => ` - ${f}`).join('\n') + + '\n\nPer get-shit-done/references/ui-brand.md § "Spawning Indicators":\n' + + 'every workflow that spawns a subagent must carry "runs in a subagent" so users know\n' + + 'that silence during a subagent run is expected and do not kill a healthy agent.\n' + + 'See https://github.com/open-gsd/gsd-core/issues/558' + ); + }); +});