GSD Headless Orchestration
Run GSD commands without TUI via gsd headless. Spawns an RPC child process, auto-responds to UI prompts, streams progress.
Command Syntax
gsd headless [flags] [command] [args...]
Flags:
--timeout N— overall timeout in ms;autohas no overall timeout unless this flag is set--json— JSONL event stream ending with the authoritativeheadless_result--model ID— override LLM model--thinking LEVEL— override thinking level (off,minimal,low,medium,high,xhigh,max)--resume <id>— resume a prior headless session by ID--bare— start with minimal context for CI/ecosystem use--verbose— show tool calls in progress output--supervised— forward interactive UI requests to orchestrator via stdout/stdin--response-timeout N— timeout for orchestrator response in supervised mode (default 30000)--max-restarts N— auto-restart on crash with backoff (default 3, 0 to disable)--answers <path>— pre-supply answers and secrets from JSON file--events <types>— filter JSONL output to specific event types (comma-separated, implies--json)
Exit codes: 0=complete, 1=error/timeout, 10=blocked, 11=cancelled
Core Workflows
1. Create + Execute a Milestone (end-to-end)
gsd headless new-milestone --context spec.md --auto
Reads spec, bootstraps .gsd/, creates milestone, then chains into auto-mode executing all phases (discuss → research → plan → execute → summarize → complete).
Extra flags for new-milestone: --context <path> (use - for stdin), --context-text <text>, --auto.
2. Run All Queued Work
gsd headless auto
Default command. Loops through all pending units until milestone complete or blocked.
3. Run One Unit
gsd headless next
Execute exactly one unit (task/slice/milestone step), then exit. Ideal for step-by-step orchestration with external decision logic between steps.
4. Instant State Snapshot (no LLM)
gsd headless query
Returns a single JSON object with the full project snapshot — no LLM session, instant (~50ms). This is the recommended way for orchestrators to inspect state.
{
"state": { "phase": "executing", "activeMilestone": {...}, "activeSlice": {...}, "progress": {...}, "registry": [...] },
"next": { "action": "dispatch", "unitType": "execute-task", "unitId": "M001/S01/T01" },
"cost": { "workers": [{ "milestoneId": "M001", "cost": 1.50, ... }], "total": 1.50 }
}
# What phase is the project in?
gsd headless query | jq '.state.phase'
# What would auto-mode do next?
gsd headless query | jq '.next'
# Total spend across parallel workers
gsd headless query | jq '.cost.total'
5. Dispatch Specific Phase
gsd headless dispatch research|plan|execute|complete|reassess|uat|replan
Force-route to a specific phase, bypassing normal state-machine routing.
Orchestrator Patterns
Poll-and-React Loop
# Instant state check — no LLM cost
PHASE=$(gsd headless query | jq -r '.state.phase')
NEXT_ACTION=$(gsd headless query | jq -r '.next.action')
case "$PHASE" in
complete) echo "Done" ;;
blocked) echo "Needs intervention" ;;
*) [ "$NEXT_ACTION" = "dispatch" ] && gsd headless next ;;
esac
Step-by-Step with Monitoring
while true; do
gsd headless next
EXIT=$?
[ $EXIT -ne 0 ] && break
# Instant progress check between steps
gsd headless query | jq '{phase: .state.phase, progress: .state.progress}'
done
Multi-Session Orchestration
GSD tracks concurrent workers via file-based IPC in .gsd/parallel/. See references/multi-session.md for the full architecture.
Quick overview:
Each worker spawns with GSD_MILESTONE_LOCK=M00X + its own git worktree. Workers write heartbeats to .gsd/parallel/<milestoneId>.status.json. The orchestrator enumerates all status files to get a dashboard of all workers, and sends commands via signal files.
# Spawn a worker for milestone M001 in its worktree
GSD_MILESTONE_LOCK=M001 GSD_PARALLEL_WORKER=1 \
gsd headless --json auto \
--cwd .gsd/worktrees/M001 2>worker-M001.log &
# Monitor all workers: read .gsd/parallel/*.status.json
for f in .gsd/parallel/*.status.json; do
jq '{mid: .milestoneId, state: .state, unit: .currentUnit.id, cost: .cost}' "$f"
done
# Send pause signal to M001
echo '{"signal":"pause","sentAt":'$(date +%s000)',"from":"coordinator"}' \
> .gsd/parallel/M001.signal.json
Status file fields: milestoneId, pid, state (running/paused/stopped/error), currentUnit, completedUnits, cost, lastHeartbeat, startedAt, worktreePath.
Signal commands: pause, resume, stop, rebase.
Liveness detection: PID alive check (kill -0 $pid) + heartbeat freshness (30s timeout). Stale sessions are auto-cleaned.
For multiple projects: each project has its own .gsd/ directory. The orchestrator must track (projectPath, milestoneId) tuples externally.
JSONL Event Stream
Use --json to get real-time events on stdout for downstream processing:
gsd headless --json auto 2>/dev/null | while read -r line; do
TYPE=$(echo "$line" | jq -r '.type')
case "$TYPE" in
tool_execution_start) echo "Tool: $(echo "$line" | jq -r '.toolName')" ;;
extension_ui_request) echo "GSD: $(echo "$line" | jq -r '.message // .title // empty')" ;;
agent_end) echo "Session ended" ;;
esac
done
Event types: agent_start, agent_end, tool_execution_start, tool_execution_end, extension_ui_request, message_update, error.
Filtered Event Stream
Use --events to receive only specific event types — reduces noise for orchestrators:
# Only phase-relevant events
gsd headless --events agent_end,extension_ui_request auto 2>/dev/null
# Only tool execution events
gsd headless --events tool_execution_start,tool_execution_end auto
The filter applies only to stdout output. Internal processing (completion detection, supervised mode, answer injection) is unaffected — all events are still processed internally.
Available event types: agent_start, agent_end, tool_execution_start, tool_execution_end, tool_execution_update, extension_ui_request, message_start, message_end, message_update, turn_start, turn_end.
Answer Injection
Pre-supply answers and secrets for headless runs via --answers:
gsd headless --answers answers.json auto
Answer file schema:
{
"questions": { "question_id": "selected_option" },
"secrets": { "API_KEY": "sk-..." },
"defaults": { "strategy": "first_option" }
}
- questions — question ID → answer (string or string[])
- secrets — env var → value, injected into child process env
- defaults.strategy —
"first_option"(default) or"cancel"for unmatched
See references/answer-injection.md for full details.
GSD Project Structure
All state lives in .gsd/ as markdown files (version-controllable):
.gsd/
phases/01-foundation/
01-CONTEXT.md # Requirements, scope, decisions
01-ROADMAP.md # Slices with tasks, dependencies, checkboxes
01-SUMMARY.md # Completion summary
01-01-PLAN.md # First slice task list
01-01-SUMMARY.md # First slice summary with frontmatter
Runtime state is stored in the project-root database; markdown files are reviewable projections written under .gsd/phases/. Legacy projects may still resolve to .gsd/milestones/<MID>/ until migrated.
All Headless Commands
Quick reference — see references/commands.md for the complete list.
| Command | Purpose |
|---|---|
auto |
Run all queued units (default) |
next |
Run one unit |
query |
Instant JSON snapshot — state, next dispatch, costs (no LLM) |
new-milestone |
Create milestone from spec |
queue |
Queue/reorder milestones |
history |
View execution history |
stop / pause |
Control auto-mode |
dispatch <phase> |
Force specific phase |
skip / undo |
Unit control |
doctor |
Health check + auto-fix |
steer <desc> |
Hard-steer plan mid-execution |
explore / spike / sketch |
Prompt-driven ideation and experiment workflows |
map-codebase / docs-update / graphify |
Codebase documentation and knowledge workflows |
code-review / review / audit-* / verify-work |
Review, audit, and UAT workflows |