Default-off, BETA, claude-only capability adopting Claude Code's Workflow tool (/effort ultracode, Agent SDK >= v0.3.149) as an optional parallel-execution backend for the GSD loop. Restores the wave parallelism + plan-checker + verifier that #853 forces inline on Claude Code, and folds gsd-ultraplan-phase under one runtime gate. - Pure fail-closed core (src/claude-orchestration.cts): detectWorkflowBackend (gate ladder: enabled -> Claude -> backend != inline -> nested+background host -> valid Agent SDK -> SDK >= floor; every miss degrades to inline) and emitWorkflowScript (waves -> parallel() barriers, plans -> gsd-executor + worktree, files_modified overlap -> separate stages, resumeFromRunId, budget). All interpolated identifiers validated script-safe; briefs JSON-quoted. - claude-orchestration command family (gsd-tools claude-orchestration detect-backend|emit-workflow) for orchestrator invocation. - Two gated loop contributions at wired points (execute:wave:post, plan:post); federated config keys (enabled/execution_backend/min_agent_sdk_version). - ADR-1143 implementation amendment; CONTEXT.md glossary entry; explanation doc. On any runtime lacking the Workflow tool, behaviour is byte-identical to today. closes #1143
3.2 KiB
Claude orchestration — Workflow execution backend (BETA)
Injected at
execute:wave:postinto: executoronly whenclaude_orchestration.enabledis true. Default-off;onError: skip.
When this contribution is active
The Claude orchestration capability is default-off and BETA. It activates only when ALL of the following hold:
claude_orchestration.enabledistruein.planning/config.json, AND- the active runtime is Claude Code (the Workflow tool is Claude / Agent SDK-specific), AND
claude_orchestration.execution_backendresolves toworkflow— either explicitly, or viaauto— and the Agent SDK version is>= claude_orchestration.min_agent_sdk_version(default0.3.149). The SDK floor applies in bothautoandworkflowmodes (fail-closed: a pre-release or older SDK never activates the preview backend).
Detection is fail-closed: any miss degrades to inline, manual, one-agent-per- message dispatch — exactly today's behaviour. On a non-Claude runtime this contribution is a no-op.
What the executor does when the Workflow backend is active
Instead of the orchestrator fanning out one Agent(subagent_type=gsd-executor, isolation=worktree, run_in_background=true) per message (which on Claude Code
cannot nest further subagents — #853 — and so degrades to sequential inline
execution), execute-phase emits a generated Workflow script and lets the main
loop orchestrate it:
- waves →
parallel()barriers — each wave is one barrier; the next wave waits for the previous to complete. - plans →
agent(brief, { agentType: 'gsd-executor', isolation: 'worktree' })— the SAME executor agent and worktree isolation the inline path uses, so the producedSUMMARY.mdand commits are identical. files_modifiedoverlap → separate sequential stages — two plans that touch the same file are placed in different stages within the wave (the same overlap rule execute-phase already applies inline).resumeFromRunId— wired to the phase run id, so an interrupted phase resumes without re-running completed plans.budget(tokens)— a shared token pool across the whole phase when the orchestrator passes abudgetTokensvalue toemitWorkflowScript(it is a function parameter, not a config key; the orchestrator decides the budget).
The emitter is a pure function exposed through the capability command surface:
gsd-tools claude-orchestration emit-workflow --waves <manifest.json> --run-id <id> [--phase-dir <dir>] [--budget <n>] (or require('gsd-core/bin/lib/claude-orchestration.cjs').emitWorkflowScript
directly). It maps the phase's wave/plan manifest to the Workflow script string
and never invokes the Workflow tool itself; the orchestrator runs the emitted
script. Use gsd-tools claude-orchestration detect-backend to resolve whether
the Workflow backend should activate for the current runtime.
Fallback contract
If detection resolves to inline (tool absent, SDK too old, runtime not Claude,
or the capability disabled), execute-phase MUST proceed with the standard inline
wave dispatch. The executor MUST NOT assume parallelism, a shared budget, or
resume-from-run-id semantics in that mode.