* fix(#373): replace unquoted $GSD_SDK with space-safe gsd_run launcher Workflow bash blocks resolved the runtime as GSD_SDK="node $GSD_TOOLS" and invoked it unquoted ($GSD_SDK query ...). On install paths containing spaces (e.g. /Volumes/Mini Me/...) the unquoted expansion word-split into `node /Volumes/Mini gsd-tools.cjs ...`, failing with "Cannot find module '/Volumes/Mini'" and getting masked by `2>/dev/null || echo "{}"` into a silent empty state. Replace the string variable with a single-line shell launcher that defines a gsd_run function, invokes the runtime with a fully-quoted path and "$@", and preserves the local-cjs / installed-gsd-tools-on-PATH fallback (#3668) plus the loud not-found error and install hint. The launcher uses _GSD_SHIM_NAME indirection so no workflow emits the /gsd-tools substring that the do.md dispatcher-parity scanner would misread, and is single-line to stay within the per-file progressive-disclosure line budgets (#2551). The canonical launcher lives in get-shit-done/workflows/_runtime-launcher.snippet.sh, is propagated once per file by scripts/sync-runtime-launcher.cjs, and is locked by tests/runtime-launcher-parity.test.cjs (fails CI on drift, on a reappearing $GSD_SDK token, or on a /gsd-tools substring). Dependent workflow-assertion tests are updated from $GSD_SDK to gsd_run, and the runtime launcher is registered in CONTEXT.md. Fixes #373 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(#373): add changeset fragment and allow-test-rule for parity guard The runtime-launcher parity test is a structural drift guard that reads workflow markdown to assert the canonical launcher is present and the retired $GSD_SDK / /gsd-tools tokens are absent; annotate it with allow-test-rule per the no-source-grep lint escape hatch. Add the required .changeset fragment for this user-facing fix. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(#373): make parity PATH-fallback assertion cross-platform (Windows) Subtest (E) compared GSD_TOOLS against the Node-side absolute temp path, but the value originates from git-bash which reports the POSIX form, so the prefix comparison failed on windows-latest while the launcher itself worked (the installed stub was invoked). Assert the resolved binary by normalized suffix (/bin/gsd-tools, not .cjs) instead of the absolute prefix; the behavioral stub-invocation assertion is unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
200 lines
6.3 KiB
Markdown
200 lines
6.3 KiB
Markdown
# Ultraplan Phase Workflow [BETA]
|
|
|
|
Offload GSD's plan phase to Claude Code's ultraplan cloud infrastructure.
|
|
|
|
⚠ **BETA feature.** Ultraplan is in research preview and may change. This workflow is
|
|
intentionally isolated from /gsd:plan-phase so upstream changes to ultraplan cannot
|
|
affect the core planning pipeline.
|
|
|
|
---
|
|
|
|
<step name="banner">
|
|
|
|
Display the stage banner:
|
|
|
|
```text
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
GSD ► ULTRAPLAN PHASE ⚠ BETA
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Ultraplan is in research preview (Claude Code v2.1.91+).
|
|
Use /gsd:plan-phase for stable local planning.
|
|
```
|
|
|
|
</step>
|
|
|
|
---
|
|
|
|
<step name="runtime_gate">
|
|
|
|
Check that the session is running inside Claude Code:
|
|
|
|
```bash
|
|
if [ "$CLAUDECODE" = "1" ] || [ -n "$CLAUDE_CODE_ENTRYPOINT" ]; then
|
|
CC_VERSION="$(claude --version 2>/dev/null | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)"
|
|
if [ -n "$CC_VERSION" ] && [ "$(printf '%s\n' "2.1.91" "$CC_VERSION" | sort -V | head -n1)" = "2.1.91" ]; then
|
|
echo "claude-code:${CC_VERSION}"
|
|
else
|
|
echo ""
|
|
fi
|
|
else
|
|
echo ""
|
|
fi
|
|
```
|
|
|
|
If the output is empty or unset, display the following error and exit:
|
|
|
|
```text
|
|
╔══════════════════════════════════════════════════════════════╗
|
|
║ RUNTIME ERROR ║
|
|
╚══════════════════════════════════════════════════════════════╝
|
|
|
|
/gsd:ultraplan-phase requires Claude Code.
|
|
ultraplan is not available in this runtime.
|
|
|
|
Use /gsd:plan-phase for local planning instead.
|
|
```
|
|
|
|
</step>
|
|
|
|
---
|
|
|
|
<step name="initialize">
|
|
|
|
Parse phase number from `$ARGUMENTS`. If no phase number is provided, detect the next
|
|
unplanned phase from the roadmap (same logic as /gsd:plan-phase).
|
|
|
|
Load GSD phase context:
|
|
|
|
```bash
|
|
_GSD_SHIM_NAME="gsd-tools.cjs"; GSD_TOOLS="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}/get-shit-done/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/get-shit-done-redux@latest --claude --local" >&2; exit 1; fi
|
|
INIT=$(gsd_run query init.plan-phase "$PHASE")
|
|
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
|
```
|
|
|
|
Parse JSON for: `phase_found`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`,
|
|
`phase_dir`, `roadmap_path`, `requirements_path`, `research_path`, `planning_exists`.
|
|
|
|
**If `planning_exists` is false:** Error and exit:
|
|
|
|
```text
|
|
No .planning directory found. Initialize the project first:
|
|
|
|
/gsd:new-project
|
|
```
|
|
|
|
**If `phase_found` is false:** Error with the phase number provided and exit.
|
|
|
|
Display detected phase:
|
|
|
|
```text
|
|
Phase {N}: {phase name}
|
|
```
|
|
|
|
</step>
|
|
|
|
---
|
|
|
|
<step name="build_prompt">
|
|
|
|
Build the ultraplan prompt from GSD context.
|
|
|
|
1. Read the phase scope from ROADMAP.md — extract the goal, deliverables, and scope for
|
|
the target phase.
|
|
|
|
2. Read REQUIREMENTS.md if it exists (`requirements_path` is not null) — extract a
|
|
concise summary (key requirements relevant to this phase, not the full document).
|
|
|
|
3. Read RESEARCH.md if it exists (`research_path` is not null) — extract a concise
|
|
summary of technical findings. Including this reduces redundant cloud research.
|
|
|
|
Construct the prompt:
|
|
|
|
```text
|
|
Plan phase {phase_number}: {phase_name}
|
|
|
|
## Phase Scope (from ROADMAP.md)
|
|
|
|
{phase scope block extracted from ROADMAP.md}
|
|
|
|
## Requirements Context
|
|
|
|
{requirements summary, or "No REQUIREMENTS.md found — infer from phase scope."}
|
|
|
|
## Existing Research
|
|
|
|
{research summary, or "No RESEARCH.md found — research from scratch."}
|
|
|
|
## Output Format
|
|
|
|
Produce a GSD PLAN.md with the following YAML frontmatter:
|
|
|
|
---
|
|
phase: "{padded_phase}-{phase_slug}"
|
|
plan: "{padded_phase}-01"
|
|
type: "feature"
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified: []
|
|
autonomous: true
|
|
must_haves:
|
|
truths: []
|
|
artifacts: []
|
|
---
|
|
|
|
Then a ## Plan section with numbered tasks. Each task should have:
|
|
- A clear imperative title
|
|
- Files to create or modify
|
|
- Specific implementation steps
|
|
|
|
Keep the plan focused and executable.
|
|
```
|
|
|
|
</step>
|
|
|
|
---
|
|
|
|
<step name="return_path_card">
|
|
|
|
Display the return-path instructions **before** triggering ultraplan so they are visible
|
|
in the terminal scroll-back after ultraplan launches:
|
|
|
|
```text
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
WHEN THE PLAN IS READY — WHAT TO DO
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
When ◆ ultraplan ready appears in your terminal:
|
|
|
|
1. Open the session link in your browser
|
|
2. Review the plan — use inline comments and emoji reactions to give feedback
|
|
3. Ask Claude to revise until you're satisfied
|
|
4. Click "Approve plan and teleport back to terminal"
|
|
5. At the terminal dialog, choose Cancel ← saves the plan to a file
|
|
6. Note the file path Claude prints
|
|
7. Run: /gsd:import --from <the file path>
|
|
|
|
/gsd:import will run conflict detection, convert to GSD format,
|
|
validate via plan-checker, update ROADMAP.md, and commit.
|
|
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
Launching ultraplan for Phase {N}: {phase_name}...
|
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
```
|
|
|
|
</step>
|
|
|
|
---
|
|
|
|
<step name="trigger">
|
|
|
|
Trigger ultraplan with the constructed prompt:
|
|
|
|
```text
|
|
/ultraplan {constructed prompt from build_prompt step}
|
|
```
|
|
|
|
Your terminal will show a `◇ ultraplan` status indicator while the remote session works.
|
|
Use `/tasks` to open the detail view with the session link, agent activity, and a stop action.
|
|
|
|
</step>
|