Compare commits

...

43 Commits

Author SHA1 Message Date
Tom Boucher
bd99d3dcb8 Merge pull request #2934 from gsd-build/add-release-sdk-to-dev
ci(release-sdk): propagate release-sdk.yml to dev branch
2026-04-30 22:00:24 -04:00
Tom Boucher
4d534cf03f ci(release-sdk): make release-sdk.yml dispatchable from the dev branch
The workflow lives on main only, so the GitHub Actions "Use workflow
from" dropdown doesn't list dev — meaning dev → @dev publishes can't be
triggered from the dev branch directly. Add the file to dev so an
operator can dispatch it with branch=dev and tag=dev.

Per project release-stream policy: dev branch publishes canary (@dev).
This is the stream that needs the file most, since main never publishes
@dev itself (main does @next / @latest).

File is byte-identical to main's release-sdk.yml — straight propagation,
no behavioral change. Tracking issues #2925, #2929.
2026-04-30 21:59:29 -04:00
Tom Boucher
b956a596ee fix(ci/canary): publish gate checks dev branch, not main
Four publish-step `if:` conditions in .github/workflows/canary.yml were
checking `github.ref == 'refs/heads/main'`. Those steps (Tag and push,
Publish to npm, Publish SDK to npm, Verify publish) therefore always
skipped on every workflow_dispatch invocation since canary runs from dev,
never main.

The workflow's own header comment is unambiguous: `dev → @canary`. The
gate was a copy-paste from release.yml (which correctly targets main for
the @next/@latest streams) that was never corrected for the canary stream.

This is why the 1.50.0-canary.1 publish hadn't materialized despite three
green workflow runs. With the gate corrected, the next dispatch will
actually publish.
2026-04-30 18:01:36 -04:00
Tom Boucher
4d4f02bd60 docs: add CANARY stream README + v1.50.0-canary.1 release notes
- docs/CANARY.md — explains the dev→@canary stream policy, install/rollback
  paths, and when (not) to install canary builds
- docs/RELEASE-v1.50.0-canary.1.md — release notes for the first 1.50.0
  canary cut: vertical MVP/TDD/UAT slice (#2867 + #2874 + #2878 + #2880 +
  #2883), opening the 1.50.0 train under PRD #2826
- docs/README.md — index entry + quick link for the canary stream
2026-04-30 17:54:43 -04:00
Tom Boucher
6eae18415d chore(release): bump dev to 1.50.0-canary.0 for first 1.50.0 canary
Sets the base version that .github/workflows/canary.yml derives the canary
tag from (strips suffix → base 1.50.0 → next available v1.50.0-canary.N).

This kicks off the 1.50.0 release train, opened by the MVP/TDD/UAT vertical
slice landed across PRs #2867, #2874, #2878, #2880, #2883.
2026-04-30 17:52:16 -04:00
Tom Boucher
ca72c08eb5 Merge pull request #2883 from gsd-build/claude/mvp-phase-4
feat(mvp-discovery): vertical MVP discovery + progress + graphify — PRD Phase 4 (#2882)
2026-04-30 17:48:04 -04:00
Tom Boucher
9548a96a9b Merge pull request #2880 from gsd-build/claude/mvp-phase-3b
feat(verify-work): MVP-mode UAT framing — PRD Phase 3b (#2879)
2026-04-30 17:47:42 -04:00
Tom Boucher
a8007e857f Merge pull request #2878 from gsd-build/claude/mvp-phase-3a
feat(execute-phase): MVP+TDD runtime gate — PRD Phase 3a (#2877)
2026-04-30 17:47:20 -04:00
Tom Boucher
76b587cba2 Merge pull request #2874 from gsd-build/claude/mvp-phase-2
feat(mvp-phase): /gsd mvp-phase command — PRD Phase 2 (#2826)
2026-04-30 17:46:58 -04:00
Tom Boucher
5d4d928250 Merge pull request #2867 from gsd-build/claude/stoic-jones-5f489e
feat(plan-phase): --mvp vertical-slice planning — PRD Phase 1 (#2826)
2026-04-30 17:46:05 -04:00
Tom Boucher
5e808b233a docs(changelog): announce Phase 4 discovery & progress (#2826) 2026-04-30 00:49:21 -04:00
Tom Boucher
38dcf1ec47 feat(graphify): add MVP-mode visual differentiation
MVP-mode phases render with #22c55e fill color AND ' (MVP)' label
suffix — two-channel signaling for color-blind and grayscale renders.
Standard phases unchanged.

Per PRD vertical-mvp-slice Phase 4 (PRD Q5: distinct visual treatment).
2026-04-30 00:49:21 -04:00
Tom Boucher
f2c7f730ce feat(stats): add MVP phase count summary
Reads roadmap.analyze (which surfaces mode per phase from Phase 1) and
emits 'Phases: N total | M MVP | K standard' summary line. Suppressed
when MVP_COUNT == 0 to avoid clutter on non-MVP projects.

Per PRD vertical-mvp-slice Phase 4.
2026-04-30 00:49:21 -04:00
Tom Boucher
37d3c9337a feat(progress): add MVP-mode user-flow display
When phase has **Mode:** mvp, progress renders user-flow status from
PLAN.md task names alongside standard task progress. Tasks that aren't
user-flow-shaped (technical-sounding) are filtered out of the user-flow
sub-block. Falls back to standard display when mode is null/absent.

Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Progress).
2026-04-30 00:49:21 -04:00
Tom Boucher
6a7ecb5344 feat(new-project): add Vertical MVP vs Horizontal Layers mode prompt
Asks user at project init how to structure the project. Vertical MVP
emits **Mode:** mvp on every initial roadmap phase (per-phase mode
preserved per PRD Q1). Horizontal Layers falls back to standard
template — no behavioral change for existing flows.

Per PRD vertical-mvp-slice Phase 4 (decision Phase-4-Persistence).
2026-04-30 00:49:21 -04:00
Tom Boucher
8be1af4f98 docs(changelog): announce MVP-mode UAT framing in verify-work (#2826) 2026-04-30 00:47:23 -04:00
Tom Boucher
87951950fd feat(gsd-verifier): add MVP Mode Verification section
Narrows goal-backward verification to the user-story [outcome] clause
when phase mode is mvp. References verify-mvp-mode.md. Preserves
existing goal-backward methodology for non-MVP phases. User-story-format
guard refuses to verify a mode:mvp phase with a non-user-story goal.
2026-04-30 00:46:51 -04:00
Tom Boucher
f9d892946a feat(verify-work): MVP-mode UAT framing — user flow first
Resolves MVP_MODE from phase mode field. Under MVP mode, generates UAT
in three ordered sections: user-flow walk-through (derived from user
story), technical checks (deferred), coverage check (goal-backward).
Falls back to standard UAT generation when mode is null/absent.
User-story-format guard refuses to verify a mode:mvp phase with a
non-user-story goal.

Also updates docs/INVENTORY.md (56 references) and
docs/INVENTORY-MANIFEST.json to register verify-mvp-mode.md added
in Task 1.

Per PRD vertical-mvp-slice Phase 3b (decisions Phase-3-B,
Phase-3-Verify-Structure).
2026-04-30 00:46:51 -04:00
Tom Boucher
3591c09fa9 docs(verifier): add verify-mvp-mode reference
Defines UAT framing under MVP mode: user-flow walk-through first,
technical checks deferred, coverage check as goal-backward narrowing
to the user story's outcome clause. Loaded conditionally by
verify-work workflow and gsd-verifier agent.
2026-04-30 00:46:15 -04:00
Tom Boucher
c1fcb6075d docs(changelog): announce MVP+TDD runtime gate in execute-phase (#2826) 2026-04-29 21:08:23 -04:00
Tom Boucher
40ecf263b1 chore(inventory): register execute-mvp-tdd reference
Bumps References count 55 -> 56. Registers execute-mvp-tdd.md.
Adds "init" to PROSE_ALLOWLIST in registry integration test so
bare `gsd-sdk query init` prose examples in plan docs don't
trigger the unregistered-handler guard (real commands are all
init.<subcommand>).
2026-04-29 21:08:18 -04:00
Tom Boucher
a370f299f3 test(execute-phase): add MVP+TDD resolution-chain integration cases
Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode
default is unset in fresh projects. Mirrors the Phase 1 plan-phase
resolution-chain integration test.
2026-04-29 21:02:29 -04:00
Tom Boucher
37d207312f feat(gsd-executor): add MVP+TDD Gate section
Mirrors the planner's MVP Mode Detection pattern from Phase 1.
Instructs halt-and-report when the runtime gate trips, references
execute-mvp-tdd.md for full semantics. No agent changes outside the
new section.
2026-04-29 21:01:12 -04:00
Tom Boucher
278d440ca8 feat(execute-phase): MVP+TDD runtime gate + blocking review
Resolves MVP_MODE in Step 1 (CLI flag -> roadmap mode -> config -> false).
Adds per-task gate that halts before behavior-adding tasks run if no
failing-test commit exists for the plan. Escalates end-of-phase TDD
review from advisory to blocking when both MVP_MODE and TDD_MODE active.

Also updates INVENTORY-MANIFEST.json to register execute-mvp-tdd.md
(added by Task 1) so manifest-sync tests pass.

Per PRD vertical-mvp-slice Phase 3a (decisions Phase-3-A, Phase-3-Split).
2026-04-29 20:56:42 -04:00
Tom Boucher
b5385cffb3 docs(executor): add MVP+TDD gate reference
Defines the runtime gate semantics for execute-phase when both
MVP_MODE and TDD_MODE are true: pre-task verification of failing-test
commit, end-of-phase review escalation from advisory to blocking,
behavior-adding task definition. Loaded conditionally by
execute-phase workflow and gsd-executor agent.
2026-04-29 20:44:13 -04:00
Tom Boucher
20e5cbda75 fix(mvp-phase): add TEXT_MODE plain-text fallback for non-Claude runtimes (#2012) 2026-04-29 17:54:56 -04:00
Tom Boucher
0d07c350df docs(changelog): announce /gsd mvp-phase command (#2826) 2026-04-29 17:50:08 -04:00
Tom Boucher
43fdb0334b chore(inventory): register mvp-phase command + 2 new references
Adds /gsd mvp-phase to commands list, mvp-phase workflow to workflows list,
and user-story-template.md + spidr-splitting.md to references. References
count: 53 -> 55.
2026-04-29 17:50:04 -04:00
Tom Boucher
78c60a9cc0 test(mvp-phase): integration smoke test for ROADMAP mutation
Validates roadmap.get-phase output after a workflow-spec'd ROADMAP write:
mode=mvp and goal=full user story. Catches schema drift between workflow
emit and parser expectation. Includes a long-story case (>120 chars) to
confirm SPIDR-rejected stories still parse correctly.
2026-04-29 17:47:58 -04:00
Tom Boucher
a871db4222 feat(gsd-planner): emit user-story header in PLAN.md under MVP mode
Extends the MVP Mode Detection section (added in Phase 1) so the planner
sources the user story from ROADMAP **Goal:** and emits the bolded
**As a** / **I want to** / **so that** form as the first content under
the phase header in PLAN.md. References user-story-template.md.
2026-04-29 17:46:28 -04:00
Tom Boucher
c26d4dc863 feat(mvp-phase): add mvp-phase workflow
Standalone workflow: phase validation -> user story prompts (As a / I want to /
So that) -> SPIDR splitting check -> ROADMAP write (Mode + Goal) -> delegation
to plan-phase. Per PRD Phase 2 (Q3 full SPIDR; Phase-2-A/B/C/D decisions).

Plan-phase auto-detects MVP via Phase 1's resolution chain, so no flags
are needed when delegating.
2026-04-29 17:44:38 -04:00
Tom Boucher
7a812ab812 fix(mvp-phase): trim description to fit 100-char budget 2026-04-29 17:41:23 -04:00
Tom Boucher
8fa4692d46 docs(planner): add SPIDR splitting reference
Defines size signals, the five SPIDR axes (Spike/Paths/Interfaces/Data/Rules),
the interactive workflow, and anti-patterns. Per PRD Q3 decision: full
interactive flow, not lightweight check. Used by mvp-phase workflow.
2026-04-29 17:23:52 -04:00
Tom Boucher
f73945aa73 docs(planner): add user-story-template reference
Defines the canonical 'As a / I want to / So that' format and the
ROADMAP.md / PLAN.md emit rules. Used by mvp-phase workflow and
gsd-planner agent under MVP_MODE.
2026-04-29 17:23:32 -04:00
Tom Boucher
1cc688367a feat(mvp-phase): add /gsd mvp-phase slash command
Standalone command for vertical MVP planning. Frontmatter only;
heavyweight workflow at get-shit-done/workflows/mvp-phase.md follows
in next commit. Mirrors discuss-phase/edit-phase command shape.
2026-04-29 17:21:44 -04:00
Tom Boucher
7db6786ec8 docs(changelog): announce --mvp vertical-slice planning (#2826) 2026-04-29 17:00:12 -04:00
Tom Boucher
ad6e2e81ca test(plan-phase): add --mvp resolution-chain integration cases
Validates roadmap.get-phase --pick mode and confirms workflow.mvp_mode
default is unset in fresh projects.
2026-04-29 17:00:12 -04:00
Tom Boucher
bf401f1a4c feat(gsd-planner): add MVP Mode Detection section
Mode-switched branch in the existing planner agent (per Q4: single agent).
Vertical-slice decomposition rules, Walking Skeleton handling, and
TDD-mode compatibility. Heavy guidance lives in references/planner-mvp-mode.md.
2026-04-29 17:00:12 -04:00
Tom Boucher
d732c4fdb7 chore(inventory): register new planner references
Added planner-mvp-mode.md and skeleton-template.md to INVENTORY.md and
INVENTORY-MANIFEST.json. References now: 53.
2026-04-29 17:00:12 -04:00
Tom Boucher
c055e23e55 docs(planner): add SKELETON.md template
Template emitted by gsd-planner under WALKING_SKELETON=true. Captures
architectural decisions and out-of-scope list for new-project Phase 1.
2026-04-29 17:00:12 -04:00
Tom Boucher
3c4b701afe docs(planner): add vertical-slice planning reference
New reference loaded by gsd-planner when MVP_MODE=true. Defines slice
ordering, Walking Skeleton rules, and anti-patterns. Referenced from
plan-phase workflow MVP_MODE wiring.
2026-04-29 17:00:12 -04:00
Tom Boucher
b1d221a42b feat(plan-phase): parse --mvp flag and resolve MVP_MODE
Resolution order: CLI flag → ROADMAP **Mode:** field → workflow.mvp_mode
config → false. Walking Skeleton gate fires for new-project Phase 1.
Wires MVP_MODE + WALKING_SKELETON into gsd-planner subagent prompt.

Per PRD vertical-mvp-slice Phase 1 (Q1, Q2, Q4).
2026-04-29 17:00:12 -04:00
Tom Boucher
834e57dfe1 feat(roadmap): parse **Mode:** field on phase sections
Adds a 'mode' field to roadmap.get-phase and roadmap.analyze outputs.
Recognizes '**Mode:** mvp' lines in phase sections; lowercased + trimmed.
Forward-compat: unrecognized values preserved verbatim, no enum check.

Foundation for --mvp flag in plan-phase (PRD: vertical-mvp-slice).
2026-04-29 17:00:12 -04:00
46 changed files with 2184 additions and 17 deletions

View File

@@ -80,7 +80,7 @@ jobs:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Tag and push
if: ${{ github.ref == 'refs/heads/main' && !inputs.dry_run }}
if: ${{ github.ref == 'refs/heads/dev' && !inputs.dry_run }}
env:
CANARY_VERSION: ${{ steps.canary.outputs.canary_version }}
run: |
@@ -88,19 +88,19 @@ jobs:
git push origin "v${CANARY_VERSION}"
- name: Publish to npm (canary)
if: ${{ github.ref == 'refs/heads/main' && !inputs.dry_run }}
if: ${{ github.ref == 'refs/heads/dev' && !inputs.dry_run }}
run: npm publish --provenance --access public --tag canary
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Publish SDK to npm (canary)
if: ${{ github.ref == 'refs/heads/main' && !inputs.dry_run }}
if: ${{ github.ref == 'refs/heads/dev' && !inputs.dry_run }}
run: cd sdk && npm publish --provenance --access public --tag canary
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Verify publish
if: ${{ github.ref == 'refs/heads/main' && !inputs.dry_run }}
if: ${{ github.ref == 'refs/heads/dev' && !inputs.dry_run }}
env:
CANARY_VERSION: ${{ steps.canary.outputs.canary_version }}
run: |

344
.github/workflows/release-sdk.yml vendored Normal file
View File

@@ -0,0 +1,344 @@
# Release SDK Bundle
#
# Stopgap workflow_dispatch publish path: builds get-shit-done-cc with the
# compiled SDK and the SDK .tgz bundled inside the CC tarball, then
# publishes the CC package to ONE chosen dist-tag (dev | next | latest)
# per run.
#
# Why this exists: @gsd-build/sdk publishes from canary.yml and release.yml
# fail because the @gsd-build npm token is currently unavailable. CC users
# do not consume @gsd-build/sdk directly — bin/gsd-sdk.js resolves
# sdk/dist/cli.js from inside the installed CC package, so the bundled
# copy is sufficient for full functionality. This workflow ships CC alone
# (no separate @gsd-build/sdk publish attempt) and additionally bakes a
# bundled gsd-sdk-<version>.tgz at sdk-bundle/gsd-sdk.tgz inside the CC
# tarball as a recoverable npm-installable artifact.
#
# Existing canary.yml and release.yml are intentionally untouched. They
# remain the canonical two-package publish path; restore them to primary
# use once @gsd-build/sdk ownership is recovered.
#
# Tracking issues: #2925 (initial workflow), #2929 (CI-gate parity with release.yml)
name: Release SDK Bundle
on:
workflow_dispatch:
inputs:
tag:
description: 'npm dist-tag to publish under'
required: true
type: choice
options:
- dev
- next
- latest
version:
description: 'Explicit version (e.g. 1.50.0-dev.3, 1.50.0-rc.2, 1.50.0). Empty = derive from package.json base + tag-appropriate suffix.'
required: false
type: string
ref:
description: 'Branch or ref to build from (default: the workflow-dispatch ref, typically dev)'
required: false
type: string
dry_run:
description: 'Dry run (skip npm publish, git tag, and push)'
required: false
type: boolean
default: false
# Per dist-tag, no concurrent publishes for the same stream. Different streams
# can publish in parallel because they target different dist-tags.
concurrency:
group: release-sdk-${{ inputs.tag }}
cancel-in-progress: false
env:
NODE_VERSION: 24
jobs:
# Cross-platform install validation gate (parity with release.yml).
# Publish job depends on this — won't proceed if the package fails to
# install cleanly across the supported matrix.
install-smoke:
permissions:
contents: read
uses: ./.github/workflows/install-smoke.yml
with:
ref: ${{ inputs.ref }}
release:
needs: install-smoke
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write # tag + push + GitHub Release
id-token: write # provenance
environment: npm-publish
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
ref: ${{ inputs.ref }}
- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
cache: 'npm'
- name: Determine version
id: ver
env:
INPUT_TAG: ${{ inputs.tag }}
INPUT_OVERRIDE: ${{ inputs.version }}
run: |
set -e
RAW=$(node -p "require('./package.json').version")
BASE=$(echo "$RAW" | sed 's/-.*//')
if [ -n "$INPUT_OVERRIDE" ]; then
VERSION="$INPUT_OVERRIDE"
else
case "$INPUT_TAG" in
dev)
N=1
while git tag -l "v${BASE}-dev.${N}" | grep -q .; do
N=$((N + 1))
done
VERSION="${BASE}-dev.${N}"
;;
next)
N=1
while git tag -l "v${BASE}-rc.${N}" | grep -q .; do
N=$((N + 1))
done
VERSION="${BASE}-rc.${N}"
;;
latest)
VERSION="$BASE"
;;
*)
echo "::error::Unknown tag '$INPUT_TAG' (expected dev|next|latest)"
exit 1
;;
esac
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=$INPUT_TAG" >> "$GITHUB_OUTPUT"
echo "→ Will publish v${VERSION} to dist-tag '${INPUT_TAG}'"
- name: Refuse if version already exists on npm
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
EXISTING=$(npm view get-shit-done-cc@"$VERSION" version 2>/dev/null || true)
if [ -n "$EXISTING" ]; then
echo "::error::get-shit-done-cc@${VERSION} is already published. Bump version or pass an explicit override input."
exit 1
fi
# Tolerant tag-existence check (matches release.yml pattern). An
# operator re-running after a mid-flight publish-step failure should
# not be blocked just because the tag step succeeded last time. Only
# error if the existing tag points at a different commit than HEAD.
- name: Check git tag (skip if matches HEAD, error if mismatched)
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
EXISTING_SHA=$(git rev-parse "refs/tags/v${VERSION}")
HEAD_SHA=$(git rev-parse HEAD)
if [ "$EXISTING_SHA" != "$HEAD_SHA" ]; then
echo "::error::git tag v${VERSION} already exists pointing at ${EXISTING_SHA}, but HEAD is ${HEAD_SHA}"
exit 1
fi
echo "::notice::tag v${VERSION} already exists at HEAD; tag step will skip"
fi
- name: Configure git identity
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- name: Bump in-tree version (not committed)
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
npm version "$VERSION" --no-git-tag-version
cd sdk && npm version "$VERSION" --no-git-tag-version
- name: Install dependencies
run: npm ci
- name: Run full test suite with coverage (parity with release.yml)
run: npm run test:coverage
- name: Build SDK dist for tarball
run: npm run build:sdk
- name: Verify CC tarball ships sdk/dist/cli.js (bug #2647 guard)
run: bash scripts/verify-tarball-sdk-dist.sh
- name: Pack SDK as tarball and bundle into CC source tree
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
set -e
cd sdk
npm pack
# npm pack emits gsd-build-sdk-<version>.tgz in the cwd
TARBALL="gsd-build-sdk-${VERSION}.tgz"
if [ ! -f "$TARBALL" ]; then
echo "::error::Expected $TARBALL but npm pack did not produce it. Listing sdk/:"
ls -la
exit 1
fi
mkdir -p ../sdk-bundle
mv "$TARBALL" ../sdk-bundle/gsd-sdk.tgz
cd ..
ls -la sdk-bundle/
- name: Add sdk-bundle to CC files whitelist (in-tree, not committed)
run: |
node <<'NODE'
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
if (!Array.isArray(pkg.files)) {
console.error('::error::package.json files is not an array');
process.exit(1);
}
if (!pkg.files.includes('sdk-bundle')) {
pkg.files.push('sdk-bundle');
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
console.log('Added sdk-bundle/ to package.json files whitelist');
} else {
console.log('sdk-bundle/ already in files whitelist');
}
NODE
- name: Verify CC tarball will contain sdk-bundle/gsd-sdk.tgz
run: |
set -e
TARBALL=$(npm pack --ignore-scripts 2>/dev/null | tail -1)
if [ -z "$TARBALL" ] || [ ! -f "$TARBALL" ]; then
echo "::error::npm pack produced no tarball"
exit 1
fi
echo "Inspecting $TARBALL for sdk-bundle/gsd-sdk.tgz:"
if ! tar -tzf "$TARBALL" | grep -q "package/sdk-bundle/gsd-sdk.tgz"; then
echo "::error::CC tarball is missing package/sdk-bundle/gsd-sdk.tgz"
tar -tzf "$TARBALL" | grep -E "sdk-bundle|sdk/dist" | head -20
exit 1
fi
echo "✅ CC tarball contains sdk-bundle/gsd-sdk.tgz"
rm -f "$TARBALL"
- name: Dry-run publish validation
env:
TAG: ${{ steps.ver.outputs.tag }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --dry-run --tag "$TAG"
- name: Tag and push
if: ${{ !inputs.dry_run }}
env:
VERSION: ${{ steps.ver.outputs.version }}
run: |
if git rev-parse -q --verify "refs/tags/v${VERSION}" >/dev/null; then
echo "Tag v${VERSION} already exists at HEAD (per pre-flight check); skipping git tag step"
else
git tag "v${VERSION}"
fi
git push origin "v${VERSION}"
- name: Publish to npm (CC bundle, SDK included as both loose tree and .tgz)
if: ${{ !inputs.dry_run }}
env:
TAG: ${{ steps.ver.outputs.tag }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npm publish --provenance --access public --tag "$TAG"
# Keep `next` from going stale relative to `latest`. When publishing a
# stable release, also point `next` at it so users on `@next` don't
# get stuck on an older pre-release than what's now stable. Parity
# with release.yml#finalize "Clean up next dist-tag" step.
- name: Re-point next dist-tag at the new latest (only when tag=latest)
if: ${{ !inputs.dry_run && steps.ver.outputs.tag == 'latest' }}
env:
VERSION: ${{ steps.ver.outputs.version }}
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
npm dist-tag add "get-shit-done-cc@${VERSION}" next
echo "✅ next dist-tag re-pointed to v${VERSION} (matches latest)"
- name: Create GitHub Release
if: ${{ !inputs.dry_run }}
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.ver.outputs.version }}
TAG: ${{ steps.ver.outputs.tag }}
run: |
# Per-tag release flags:
# dev, next → --prerelease (won't be highlighted as the latest release on the repo page)
# latest → --latest (becomes the highlighted release)
if [ "$TAG" = "latest" ]; then
gh release create "v${VERSION}" \
--title "v${VERSION}" \
--generate-notes \
--latest
else
gh release create "v${VERSION}" \
--title "v${VERSION}" \
--generate-notes \
--prerelease
fi
echo "✅ GitHub Release v${VERSION} created"
- name: Verify publish landed on registry
if: ${{ !inputs.dry_run }}
env:
VERSION: ${{ steps.ver.outputs.version }}
TAG: ${{ steps.ver.outputs.tag }}
run: |
PUBLISHED="NOT_FOUND"
for delay in 5 10 20 30 45; do
PUBLISHED=$(npm view get-shit-done-cc@"$VERSION" version 2>/dev/null || echo "NOT_FOUND")
if [ "$PUBLISHED" = "$VERSION" ]; then
break
fi
echo "Waiting ${delay}s for registry to catch up (saw: $PUBLISHED)..."
sleep "$delay"
done
if [ "$PUBLISHED" != "$VERSION" ]; then
echo "::error::Version $VERSION did not appear on the registry within timeout"
exit 1
fi
TAG_VERSION=$(npm view get-shit-done-cc dist-tags."$TAG" 2>/dev/null || echo "NOT_FOUND")
if [ "$TAG_VERSION" != "$VERSION" ]; then
echo "::error::dist-tag '$TAG' resolves to '$TAG_VERSION', expected '$VERSION'"
exit 1
fi
echo "✅ get-shit-done-cc@${VERSION} live on dist-tag '${TAG}'"
- name: Summary
env:
VERSION: ${{ steps.ver.outputs.version }}
TAG: ${{ steps.ver.outputs.tag }}
DRY_RUN: ${{ inputs.dry_run }}
run: |
echo "## Release SDK Bundle: v${VERSION} → @${TAG}" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
if [ "$DRY_RUN" = "true" ]; then
echo "**DRY RUN** — npm publish, git tag, push, and GitHub Release were skipped." >> "$GITHUB_STEP_SUMMARY"
else
echo "- Published \`get-shit-done-cc@${VERSION}\` to dist-tag \`${TAG}\`" >> "$GITHUB_STEP_SUMMARY"
echo "- SDK bundled inside the CC tarball at:" >> "$GITHUB_STEP_SUMMARY"
echo " - \`sdk/dist/cli.js\` (loose tree, consumed by \`bin/gsd-sdk.js\` shim)" >> "$GITHUB_STEP_SUMMARY"
echo " - \`sdk-bundle/gsd-sdk.tgz\` (npm-installable artifact)" >> "$GITHUB_STEP_SUMMARY"
echo "- Git tag \`v${VERSION}\` pushed" >> "$GITHUB_STEP_SUMMARY"
echo "- GitHub Release \`v${VERSION}\` created" >> "$GITHUB_STEP_SUMMARY"
if [ "$TAG" = "latest" ]; then
echo "- \`next\` dist-tag re-pointed at \`v${VERSION}\` (kept current with \`latest\`)" >> "$GITHUB_STEP_SUMMARY"
fi
echo "- Install: \`npm install -g get-shit-done-cc@${TAG}\`" >> "$GITHUB_STEP_SUMMARY"
fi

View File

@@ -7,6 +7,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
## [Unreleased](https://github.com/gsd-build/get-shit-done/compare/v1.38.5...HEAD)
### Added
- **Vertical MVP discovery & progress surfaces.** `/gsd new-project` now prompts the user to choose between **Vertical MVP** (each phase delivers an end-to-end user capability — recommended for new products) and **Horizontal Layers** (build complete technical layers, assemble at the end). Picking Vertical MVP writes `**Mode:** mvp` on every initial roadmap phase. `/gsd progress` adds a user-flow status sub-block sourced from PLAN.md task names when a phase has `**Mode:** mvp`. `/gsd stats` adds a 'Phases: N total | M MVP | K standard' summary line when at least one MVP phase exists. `/gsd graphify` renders MVP-mode phase nodes with a distinct green fill (`#22c55e`) and a ` (MVP)` label suffix — two-channel signaling for color-blind and grayscale renders. Closes the umbrella PRD #2826. (#2826)
- **MVP-mode UAT framing in `/gsd verify-work`** — when the phase under verification has `**Mode:** mvp` in ROADMAP.md, the generated UAT script asks "can a real user complete the feature?" before any technical checks. User-flow steps (open, fill, click, observe) run first; technical checks (endpoint schemas, error states) only run AFTER the user flow passes. Adds a goal-backward "User Flow Coverage" section to VERIFICATION.md that maps user-story steps to evidence in the codebase. User-story-format guard refuses to verify a `mode: mvp` phase whose `**Goal:**` line is not in user-story format. (#2826)
- **MVP+TDD runtime gate in `/gsd execute-phase`** — when both `MVP_MODE` and `TDD_MODE` are active for a phase, the executor refuses to advance a behavior-adding task until a failing-test commit exists for it. The existing end-of-phase TDD review (advisory by default) escalates to **blocking** under the same condition: phases with missing RED→GREEN commits cannot be marked complete without `--force-mvp-gate`. Pure doc-only / config-only / test-only tasks are exempt. (#2826)
- **`/gsd mvp-phase <N>` command** — guided MVP planning entry point. Prompts for an "As a / I want to / So that" user story (three structured fields), runs SPIDR splitting check (full interactive flow per PRD Q3) if the story is too large, writes `**Mode:** mvp` and the formatted goal to ROADMAP.md, then delegates to `/gsd plan-phase <N>` (which auto-detects MVP via the roadmap mode field shipped in PRD Phase 1). The `gsd-planner` agent now emits a `## Phase Goal` section with bolded **As a** / **I want to** / **so that** keywords as the first content under the phase header in PLAN.md when MVP_MODE is active. (#2826)
- **`--mvp` flag on `/gsd plan-phase`** — opt-in vertical-slice planning. Plans are
organized as feature slices (UI→API→DB) instead of horizontal layers, so each task
moves a real user-visible capability forward. Persistable per-phase via `**Mode:** mvp`
in ROADMAP.md. New-project Phase 1 + `--mvp` triggers Walking Skeleton output
(`SKELETON.md`) capturing architectural decisions for subsequent phases. Single
planner agent, mode-switched (no new agent surface). PRD Phases 24 (`mvp-phase`
command, TDD wiring, discovery/UX) deferred to follow-up plans. (#2826)
- `--minimal` install flag (alias `--core-only`) writes only the main-loop core skills
(`new-project`, `discuss-phase`, `plan-phase`, `execute-phase`, `help`, `update`) and
zero `gsd-*` subagents. Cuts cold-start system-prompt overhead from ~12k tokens to

View File

@@ -355,6 +355,21 @@ When the plan frontmatter has `type: tdd`, the entire plan follows the RED/GREEN
If RED or GREEN gate commits are missing, add a warning to SUMMARY.md under a `## TDD Gate Compliance` section.
</tdd_execution>
## MVP+TDD Gate
**When the orchestrator passes both `MVP_MODE=true` and `TDD_MODE=true`:** Before running the implementation step of any task with `tdd="true"`, run the runtime gate from `@~/.claude/get-shit-done/references/execute-mvp-tdd.md`. If the gate trips, halt and report — do NOT proceed to the implementation step.
**Halt-and-report protocol:**
1. Stop. Do not run the task's implementation step.
2. Emit the structured halt report defined in `references/execute-mvp-tdd.md` (header line, reason code, expected behavior, required next step).
3. Update `STATE.md` with `last_gate_trip: {plan_id}/{task_id}`.
4. Exit the current execution wave cleanly. Prior commits in the same wave stay — do not roll back.
**Behavior-adding task detection** (the gate only fires for behavior-adding tasks): see `references/execute-mvp-tdd.md` for the precise definition. Pure doc-only / config-only / test-only tasks are exempt.
**Mode is all-or-nothing per phase** (PRD decision Q1, inherited from Phase 1). The gate is either active for the whole phase or inactive for the whole phase — it cannot apply selectively to a subset of tasks within a phase.
<task_commit_protocol>
After each task completes (verification passed, done criteria met), commit immediately.

View File

@@ -302,6 +302,35 @@ This prevents the "scavenger hunt" anti-pattern where executors explore the code
Exceptions where `tdd="true"` is not needed: `type="checkpoint:*"` tasks, configuration-only files, documentation, migration scripts, glue code wiring existing tested components, styling-only changes.
## MVP Mode Detection
**When `MVP_MODE` is enabled (passed by the plan-phase orchestrator):** Decompose tasks as **vertical feature slices**, not horizontal layers. Required reading: `@~/.claude/get-shit-done/references/planner-mvp-mode.md` (loaded conditionally by the orchestrator).
**Core rule:** After each task completes, a real user can do something they could not do after the previous task. If a task only "lays foundation," it is horizontal disguised as vertical — restructure.
**Plan structure under MVP_MODE:**
1. Frame the phase goal as a user story at the top of `PLAN.md`. The user story is sourced from the `**Goal:**` line in ROADMAP.md (set by `mvp-phase`). Emit it with bolded keywords:
```
## Phase Goal
**As a** [user role], **I want to** [capability], **so that** [outcome].
```
Format rules from `@~/.claude/get-shit-done/references/user-story-template.md`:
- All three slots required. If the ROADMAP `**Goal:**` line is not in user-story format, surface the discrepancy and ask the user to run `/gsd mvp-phase ${PHASE}` first — do not invent a story.
- Bold the three keywords (`**As a**`, `**I want to**`, `**so that**`) when emitting to PLAN.md. The ROADMAP form does not use bolded keywords; the PLAN form does.
2. First task: failing end-to-end test for the happy path.
3. Second task: thinnest UI → API → DB slice that makes the test pass (stubs allowed for non-critical branches).
4. Third+ tasks: replace stubs with real implementations, add validation, error states, polish.
**Mode is all-or-nothing per phase** (PRD decision Q1). Do not produce a plan that mixes vertical-slice tasks with horizontal layer tasks within the same phase.
**Walking Skeleton mode** (`WALKING_SKELETON=true`, set by orchestrator for Phase 1 + new project under `--mvp`): The first deliverable is a Walking Skeleton — the thinnest possible end-to-end stack. In addition to `PLAN.md`, produce `SKELETON.md` using the template at `@~/.claude/get-shit-done/references/skeleton-template.md`. `SKELETON.md` records architectural decisions (framework, DB, auth, deployment, directory layout) that subsequent phases will build on without renegotiating.
**Compatibility with TDD detection:** When both `MVP_MODE=true` and `workflow.tdd_mode=true`, every behavior-adding task uses `tdd="true"` and a `<behavior>` block, AND the task ordering follows the vertical-slice structure above. The first task is always a failing end-to-end test.
## User Setup Detection
For tasks involving external services, identify human-required configuration:

View File

@@ -585,6 +585,27 @@ Deferred items are informational only — they do not require closure plans.
</verification_process>
<mvp_mode_verification>
## MVP Mode Verification
**When the phase under verification has `mode: mvp` in ROADMAP.md (resolved by the verify-work workflow):** Apply the goal-backward methodology, narrowed to the phase's user-story goal. Required reading: `@~/.claude/get-shit-done/references/verify-mvp-mode.md`.
**Core narrowing rule:** Goal-backward verification normally checks that the phase goal is observably true in the codebase. Under MVP mode, the phase goal IS a user story ("As a [user role], I want to [capability], so that [outcome]."). Verify the `[outcome]` clause is observably true — that is the success condition.
**VERIFICATION.md output structure under MVP mode:**
1. Top-level "User Flow Coverage" table: each step of the user story → expected → evidence in codebase → status. (Format defined in `references/verify-mvp-mode.md`.)
2. Standard technical-check sections (API verification, error handling, etc.) follow below — only if the user flow coverage is complete.
**User-story-format guard:** If the phase has `mode: mvp` but the `**Goal:**` line is not in user-story format, refuse to verify. Surface the discrepancy and ask the user to run `/gsd mvp-phase ${PHASE}` to set a proper user-story goal. Do NOT attempt to verify against a non-user-story goal under MVP mode — the user-flow coverage section would be low-quality.
**Mode is all-or-nothing per phase** (PRD decision Q1, inherited from Phase 1). The MVP Mode Verification rules apply to the whole phase or not at all.
**Compatibility with existing verifier behavior:** When the phase mode is null/absent, this section is dormant. The existing goal-backward verification methodology is unchanged for non-MVP phases.
</mvp_mode_verification>
<output>
## Create VERIFICATION.md

View File

@@ -193,6 +193,19 @@ Wait for the agent to complete.
---
## MVP-Mode Node Rendering
**MVP-mode rendering.** When a phase has `**Mode:** mvp` in ROADMAP.md (resolved via `gsd-sdk query roadmap.get-phase --pick mode`), render its graph node with two distinct visual signals:
1. **Distinct fill color.** Use `#22c55e` (green) for MVP-mode phase nodes. Standard phases keep the default fill color. Two-channel signaling (color + label) handles color-blind and grayscale renders.
2. **`MVP` label suffix.** Append ` (MVP)` to the node's label text. Example: a phase originally labeled `Phase 1: User Auth` renders as `Phase 1: User Auth (MVP)`.
Both signals fire together — never just one. Per PRD Q5 decision, the goal is unambiguous visual distinction in any render context.
When the phase mode is null/absent, render with the standard color and label — no behavioral change for non-MVP phases.
---
## Anti-Patterns
1. DO NOT spawn an agent for query/status/diff operations -- these are inline CLI calls

45
commands/gsd/mvp-phase.md Normal file
View File

@@ -0,0 +1,45 @@
---
name: gsd:mvp-phase
description: Plan a phase as a vertical MVP slice — user story, SPIDR splitting, then plan-phase
argument-hint: "<phase-number>"
agent: gsd-planner
allowed-tools:
- Read
- Write
- Bash
- Glob
- Grep
- Task
- AskUserQuestion
---
<objective>
Guide the user through MVP-mode planning for a phase. The command:
1. Prompts for an "As a / I want to / So that" user story (three structured questions)
2. Runs SPIDR splitting check — if the story is too large, walks through Spike/Paths/Interfaces/Data/Rules and offers to split into multiple phases
3. Writes `**Mode:** mvp` and the reformatted `**Goal:**` to the phase's ROADMAP.md section
4. Delegates to `/gsd plan-phase <N>` which auto-detects MVP mode via the roadmap field
Phase 1 of the vertical-mvp-slice PRD shipped the planner-side machinery; this command is the user entry point for it.
</objective>
<execution_context>
@~/.claude/get-shit-done/workflows/mvp-phase.md
@~/.claude/get-shit-done/references/spidr-splitting.md
@~/.claude/get-shit-done/references/user-story-template.md
</execution_context>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`. Equivalent API.
</runtime_note>
<context>
Phase number: $ARGUMENTS (required — integer or decimal like `2.1`)
The phase must already exist in ROADMAP.md (created via `/gsd new-project`, `/gsd add-phase`, or `/gsd insert-phase`). This command does not create new phases — it converts an existing phase to MVP mode.
</context>
<process>
Execute the mvp-phase workflow from @~/.claude/get-shit-done/workflows/mvp-phase.md end-to-end.
Preserve all gates: phase existence, status guard (refuse in_progress/completed), user-story format validation, SPIDR splitting check, ROADMAP write confirmation, plan-phase delegation.
</process>

66
docs/CANARY.md Normal file
View File

@@ -0,0 +1,66 @@
# Canary Stream
The **canary** dist-tag is GSD's earliest preview channel. It exists so contributors and willing early adopters can exercise in-flight features against the long-lived `dev` integration branch before they have any expectation of stability.
## Stream policy
GSD ships through three npm dist-tags, each fed by exactly one git branch. **Streams do not mix.**
| Branch | dist-tag | Audience | Stability |
|---|---|---|---|
| `dev` | `canary` | Contributors, willing early adopters | Best-effort. May regress between cuts. Roll-forward only. |
| `main` | `next` | Maintainers, RC testers | Release-candidate quality. Bug-bar enforced. |
| `main` | `latest` | Everyone else | Production stable. The default `npm install` target. |
`dev` is the integration branch for in-flight feature work (typically multi-PR vertical slices like the MVP/TDD/UAT track in 1.50.0). When the dev work stabilizes, it promotes to `main` as an RC train (`vX.Y.Z-rc.N` published to `next`), and after the RC train bakes, the same train promotes again to `latest`.
A canary build NEVER becomes a `next` build directly, and a `next` build NEVER becomes a `latest` build directly — every promotion goes through a fresh tag and a fresh release.
## Installing canary
```bash
# One-off invocation (npx)
npx get-shit-done-cc@canary
# Pin to the canary dist-tag globally
npm install -g get-shit-done-cc@canary
# Pin to an exact canary version
npm install -g get-shit-done-cc@1.50.0-canary.1
```
The CC installer's defensive purge rewrites stale config blocks left by older GSD versions, so reinstalling on top of an existing project is safe.
## When to install canary
**Do** install canary when you want to:
- Exercise in-flight planning/execution/verification features early and report findings
- Validate a fix you've contributed to `dev` is reachable end-to-end
- Help shake out canary-bake items (rough edges that won't ship to `next` until resolved)
**Do NOT** install canary on:
- Production projects you depend on for delivery
- A machine where rolling back means recreating GSD state (use a profile or a workspace instead)
- A demo or onboarding setup — pin to `@latest` so audiences see the stable surface
## Rolling back from canary
```bash
# Back to the current stable
npm install -g get-shit-done-cc@latest
# Or to the next/RC train
npm install -g get-shit-done-cc@next
```
If you have a local project that interacted with canary-only features (for instance, an MVP-mode phase planned by 1.50.0-canary), the planner artifacts in `.planning/` remain valid — older GSD versions will just ignore the `**Mode:** mvp` field on phases.
## Reporting issues against canary
File against the [issue tracker](https://github.com/gsd-build/get-shit-done/issues) with the `bug` template. Include the exact canary version (`get-shit-done-cc --version` reports it) so triage can route the report back into the `dev` stream rather than the stable stream.
## Where to look next
- Active canary release notes: [`docs/RELEASE-v1.50.0-canary.1.md`](RELEASE-v1.50.0-canary.1.md)
- Stable release notes: [`CHANGELOG.md`](../CHANGELOG.md)
- Stream architecture rationale: discussed across [#2727](https://github.com/gsd-build/get-shit-done/issues/2727), [#2773](https://github.com/gsd-build/get-shit-done/issues/2773) (codex schema-break and the resulting promotion bottleneck that motivated explicit stream isolation)

View File

@@ -1,5 +1,5 @@
{
"generated": "2026-04-27",
"generated": "2026-04-30",
"families": {
"agents": [
"gsd-advisor-researcher",
@@ -78,6 +78,7 @@
"/gsd-manager",
"/gsd-map-codebase",
"/gsd-milestone-summary",
"/gsd-mvp-phase",
"/gsd-new-milestone",
"/gsd-new-project",
"/gsd-new-workspace",
@@ -166,6 +167,7 @@
"manager.md",
"map-codebase.md",
"milestone-summary.md",
"mvp-phase.md",
"new-milestone.md",
"new-project.md",
"new-workspace.md",
@@ -224,6 +226,7 @@
"decimal-phase-calculation.md",
"doc-conflict-engine.md",
"domain-probes.md",
"execute-mvp-tdd.md",
"executor-examples.md",
"gate-prompts.md",
"gates.md",
@@ -237,6 +240,7 @@
"planner-antipatterns.md",
"planner-chunked.md",
"planner-gap-closure.md",
"planner-mvp-mode.md",
"planner-reviews.md",
"planner-revision.md",
"planner-source-audit.md",
@@ -245,10 +249,12 @@
"questioning.md",
"revision-loop.md",
"scout-codebase.md",
"skeleton-template.md",
"sketch-interactivity.md",
"sketch-theme-system.md",
"sketch-tooling.md",
"sketch-variant-patterns.md",
"spidr-splitting.md",
"tdd.md",
"thinking-models-debug.md",
"thinking-models-execution.md",
@@ -259,8 +265,10 @@
"ui-brand.md",
"universal-anti-patterns.md",
"user-profiling.md",
"user-story-template.md",
"verification-overrides.md",
"verification-patterns.md",
"verify-mvp-mode.md",
"workstream-flag.md"
],
"cli_modules": [

View File

@@ -54,7 +54,7 @@ Full roster at `agents/gsd-*.md`. The "Primary doc" column flags whether [`docs/
---
## Commands (86 shipped)
## Commands (87 shipped)
Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md` section order; each row carries the command name, a one-line role derived from the command's frontmatter `description:`, and a link to the source file. `tests/command-count-sync.test.cjs` locks the count against the filesystem.
@@ -67,6 +67,7 @@ Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md
| `/gsd-list-workspaces` | List active GSD workspaces and their status. | [commands/gsd/list-workspaces.md](../commands/gsd/list-workspaces.md) |
| `/gsd-remove-workspace` | Remove a GSD workspace and clean up worktrees. | [commands/gsd/remove-workspace.md](../commands/gsd/remove-workspace.md) |
| `/gsd-discuss-phase` | Gather phase context through adaptive questioning before planning. | [commands/gsd/discuss-phase.md](../commands/gsd/discuss-phase.md) |
| `/gsd-mvp-phase` | Plan a phase as a vertical MVP slice — user story, SPIDR splitting, then plan-phase. | [commands/gsd/mvp-phase.md](../commands/gsd/mvp-phase.md) |
| `/gsd-spec-phase` | Socratic spec refinement producing a SPEC.md with falsifiable requirements. | [commands/gsd/spec-phase.md](../commands/gsd/spec-phase.md) |
| `/gsd-ui-phase` | Generate UI design contract (UI-SPEC.md) for frontend phases. | [commands/gsd/ui-phase.md](../commands/gsd/ui-phase.md) |
| `/gsd-ai-integration-phase` | Generate AI design contract (AI-SPEC.md) via framework selection, research, and eval planning. | [commands/gsd/ai-integration-phase.md](../commands/gsd/ai-integration-phase.md) |
@@ -176,7 +177,7 @@ Full roster at `commands/gsd/*.md`. The groupings below mirror `docs/COMMANDS.md
---
## Workflows (84 shipped)
## Workflows (85 shipped)
Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators that commands reference internally; most are not read directly by end users. Rows below map each workflow file to its role (derived from the `<purpose>` block) and, where applicable, to the command that invokes it.
@@ -201,6 +202,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators
| `discuss-phase-assumptions.md` | Assumptions-mode discuss — extract implementation decisions via codebase-first analysis. | `/gsd-discuss-phase` (when `discuss_mode=assumptions`) |
| `discuss-phase-power.md` | Power-user discuss — pre-generate all questions into a JSON state file + HTML UI. | `/gsd-discuss-phase --power` |
| `discuss-phase.md` | Extract implementation decisions through iterative gray-area discussion. | `/gsd-discuss-phase` |
| `mvp-phase.md` | Plan a phase as a vertical MVP slice — user story, SPIDR splitting, then plan-phase. | `/gsd-mvp-phase` |
| `do.md` | Route freeform text from the user to the best matching GSD command. | `/gsd-do` |
| `docs-update.md` | Generate, update, and verify canonical and hand-written project documentation. | `/gsd-docs-update` |
| `edit-phase.md` | Edit any field of an existing phase in ROADMAP.md in place, preserving number and position. | `/gsd-edit-phase` |
@@ -271,7 +273,7 @@ Full roster at `get-shit-done/workflows/*.md`. Workflows are thin orchestrators
---
## References (51 shipped)
## References (57 shipped)
Full roster at `get-shit-done/references/*.md`. References are shared knowledge documents that workflows and agents `@-reference`. The groupings below match [`docs/ARCHITECTURE.md`](ARCHITECTURE.md#references-get-shit-donereferencesmd) — core, workflow, thinking-model clusters, and the modular planner decomposition.
@@ -320,6 +322,8 @@ Full roster at `get-shit-done/references/*.md`. References are shared knowledge
| `ai-frameworks.md` | AI framework decision-matrix reference for `gsd-framework-selector`. |
| `executor-examples.md` | Worked examples for the gsd-executor agent. |
| `doc-conflict-engine.md` | Shared conflict-detection contract for ingest/import workflows. |
| `execute-mvp-tdd.md` | Runtime gate semantics for execute-phase under MVP+TDD — pre-task failing-test verification, end-of-phase blocking review. |
| `verify-mvp-mode.md` | UAT framing rules for MVP-mode phases — user-flow-first ordering, deferred technical checks, user-story-format guard. |
### Sketch References
@@ -356,8 +360,12 @@ The `gsd-planner` agent is decomposed into a core agent plus reference modules t
| `planner-reviews.md` | Cross-AI review integration (reads REVIEWS.md from `/gsd-review`). |
| `planner-revision.md` | Plan revision patterns for iterative refinement. |
| `planner-source-audit.md` | Planner source-audit and authority-limit rules. |
| `planner-mvp-mode.md` | Vertical-slice planning rules for MVP mode. |
| `skeleton-template.md` | SKELETON.md template emitted for new-project Walking Skeleton (Phase 1 + `--mvp`). |
| `user-story-template.md` | User story format for MVP planning — "As a / I want to / So that" structured fields. |
| `spidr-splitting.md` | SPIDR splitting decomposition rules for handling large user stories in MVP mode. |
> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 51 top-level references.
> **Subdirectory:** `get-shit-done/references/few-shot-examples/` contains additional few-shot examples (`plan-checker.md`, `verifier.md`) that are referenced from specific agents. These are not counted in the 53 top-level references.
---

View File

@@ -17,10 +17,12 @@ Language versions: [English](README.md) · [Português (pt-BR)](pt-BR/README.md)
| [User Guide](USER-GUIDE.md) | All users | Workflow walkthroughs, troubleshooting, and recovery |
| [Context Monitor](context-monitor.md) | All users | Context window monitoring hook architecture |
| [Discuss Mode](workflow-discuss-mode.md) | All users | Assumptions vs interview mode for discuss-phase |
| [Canary Stream](CANARY.md) | Contributors, early adopters | `dev``@canary` dist-tag policy, when to install, rollback path |
## Quick Links
- **What's new:** see [CHANGELOG](../CHANGELOG.md) for current release notes, and upstream [README](../README.md) for release highlights
- **Canary preview:** [`docs/CANARY.md`](CANARY.md) — opt into the early-preview stream from `dev`. Active cut: [`v1.50.0-canary.1`](RELEASE-v1.50.0-canary.1.md)
- **Getting started:** [README](../README.md) → install → `/gsd-new-project`
- **Full workflow walkthrough:** [User Guide](USER-GUIDE.md)
- **All commands at a glance:** [Command Reference](COMMANDS.md)

View File

@@ -0,0 +1,94 @@
# v1.50.0-canary.1 Release Notes
First canary cut for the **1.50.0** train. Published to npm under the `canary` dist-tag.
```bash
npx get-shit-done-cc@canary
# or pin exact:
npm install -g get-shit-done-cc@1.50.0-canary.1
```
> **Canary stream caveat.** Canary builds come from the long-lived `dev` integration branch and may carry rough edges that the `next` (RC) and `latest` (stable) channels never see. Use canary when you want to exercise in-flight features early and report findings; do NOT pin production projects to it. See [CANARY.md](CANARY.md) for the stream policy and rollback path.
---
## Headline: Vertical MVP / TDD / UAT planning track
The 1.50.0 train opens with a four-phase vertical slice that adds an end-to-end "MVP mode" to the GSD planning pipeline — from project kickoff, through phase planning, through execution, through verification. Issue [#2826](https://github.com/gsd-build/get-shit-done/issues/2826) is the umbrella PRD.
### What's new
#### `/gsd plan-phase --mvp` — vertical-slice planning ([#2867](https://github.com/gsd-build/get-shit-done/pull/2867))
`/gsd plan-phase` learns a `--mvp` flag that flips the planner into vertical-slice mode. The planner reads `**Mode:** mvp` from a phase's ROADMAP entry, an explicit `--mvp` CLI override, or `workflow.mvp_mode` in `.planning/config.json` (precedence in that order, with the CLI flag winning). Under MVP mode the planner:
- Surfaces a "Walking Skeleton" template for the very first phase of a new project — a thin end-to-end vertical slice that proves the wiring before any horizontal layer is built
- Suppresses horizontal-layer language ("data layer first, then business logic, then UI") in favor of user-flow-driven decomposition
- Emits the user story as a header at the top of `PLAN.md`
New required-reading injection: `references/planner-mvp-mode.md`. New parser surface: `roadmap.cjs` extracts a `mode` field on every phase lookup.
#### `/gsd mvp-phase <N>` — guided user-story phase framing ([#2874](https://github.com/gsd-build/get-shit-done/pull/2874))
A new top-level command that walks the user through framing a phase as a vertical MVP slice before planning. Three structured prompts capture an "As a / I want to / So that" user story. If the story is too large, an interactive SPIDR (Spike / Path / Interface / Data / Rule) splitting flow surfaces a list of `/gsd add-phase` invocations to break the work apart. The command then:
- Mutates the ROADMAP entry to set `**Mode:** mvp` and replaces `**Goal:**` with the assembled user story
- Delegates to `/gsd plan-phase --mvp <N>` to produce the plan
Two new references: [`spidr-splitting.md`](../get-shit-done/references/spidr-splitting.md), [`user-story-template.md`](../get-shit-done/references/user-story-template.md).
#### Execute-phase MVP+TDD runtime gate ([#2878](https://github.com/gsd-build/get-shit-done/pull/2878))
When `MVP_MODE` and `TDD_MODE` are both true at execution time, `execute-phase` adds a per-task gate that requires a `test(<phase>-<plan>):` commit to exist before the corresponding `feat(...)` commit. The reference [`execute-mvp-tdd.md`](../get-shit-done/references/execute-mvp-tdd.md) documents the contract; the executor agent (`agents/gsd-executor.md`) gains an MVP+TDD Gate section that explains when the gate trips, what evidence it expects, and how to escalate via the documented escape hatch.
> **Known canary-bake item.** The current bash gate snippet uses some workflow variables that aren't fully wired (`${PLAN_ID}`, `${TASK_TDD}`) and the documented `--force-mvp-gate` escape hatch is referenced in the user-facing error message but not yet implemented in the argument parser. These are tracked as canary-bake follow-ups; the gate itself is functional for the dominant code path.
#### Verify-work MVP-mode UAT framing ([#2880](https://github.com/gsd-build/get-shit-done/pull/2880))
Under MVP mode, `verify-work` flips the UAT script's framing so user-flow steps come **before** technical correctness checks — the inverse of the default order. The verifier agent gains a `mvp_mode_verification` section. New reference: [`verify-mvp-mode.md`](../get-shit-done/references/verify-mvp-mode.md).
A user-story format guard at the top of `extract_tests` will halt verification if a phase claims `**Mode:** mvp` but its `**Goal:**` doesn't parse as `As a … I want to … so that …` — pointing the user at `/gsd mvp-phase <N>` to repair.
#### Discovery & progress surfaces ([#2883](https://github.com/gsd-build/get-shit-done/pull/2883))
The MVP slice closes out with read-side surfaces:
- **`/gsd new-project`** prompts up front for **Vertical MVP** vs **Horizontal Layers** mode and seeds the milestone accordingly
- **`/gsd-progress`** emits a "User-flow next up" panel for MVP-mode phases, surfacing user-visible task names ahead of internal scaffolding
- **`/gsd-stats`** adds an "MVP phases: N" summary line when the roadmap contains any
- **`/gsd-graphify`** visually differentiates MVP-mode phase nodes from horizontal-layer phases in the rendered graph
---
## Bonus fixes also in this canary
- **`/gsd-progress` no longer cites stale CLAUDE.md project blocks** as the source for the "Next Up" section ([#2912](https://github.com/gsd-build/get-shit-done/issues/2912)) — explicit context-authority directive added to the report step.
(Other recent main-stream fixes — agent-skills CLI JSON wrap, audit-open ReferenceError, execute-phase branching, Hermes runtime — target the `next` stream and will arrive in the canary when they land in `dev`.)
---
## Install / upgrade
```bash
# Try the canary
npx get-shit-done-cc@canary
# Or pin exact
npm install -g get-shit-done-cc@1.50.0-canary.1
```
The installer's defensive purge will rewrite stale config blocks left by older GSD versions on first run. No manual cleanup needed.
## Reporting issues
If something breaks on canary, file against [the issue tracker](https://github.com/gsd-build/get-shit-done/issues) with the `bug` template and mention `1.50.0-canary.1` so it gets routed back into the dev stream rather than the stable stream.
## What ships next in this train
Pending dev-stream merges that should land before promotion to `next`:
- Resolve canary-bake items in the MVP+TDD gate (variable wiring + `--force-mvp-gate` parser)
- Sync recent main-stream fixes (`#2918`, `#2919`, `#2921`, `#2917`, `#2920`) into dev
- Ride a few canary cycles for real-user MVP/TDD/UAT feedback
When the dev stream stabilizes, the train promotes to `main` as `v1.50.0-rc.1` (the `next` channel).

View File

@@ -85,6 +85,11 @@ function searchPhaseInContent(content, escapedPhase, phaseNum) {
const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
const goal = goalMatch ? goalMatch[1].trim() : null;
// Mode: vertical-MVP slice mode flag. Lowercased + trimmed for canonical
// comparison; unrecognized values are preserved verbatim for forward-compat.
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
const mode = modeMatch ? modeMatch[1].trim().toLowerCase() : null;
// Extract success criteria as structured array
const criteriaMatch = section.match(/\*\*Success Criteria\*\*[^\n]*:\s*\n((?:\s*\d+\.\s*[^\n]+\n?)+)/i);
const success_criteria = criteriaMatch
@@ -96,6 +101,7 @@ function searchPhaseInContent(content, escapedPhase, phaseNum) {
phase_number: phaseNum,
phase_name: phaseName,
goal,
mode,
success_criteria,
section,
};
@@ -181,6 +187,9 @@ function cmdRoadmapAnalyze(cwd, raw) {
const goalMatch = section.match(/\*\*Goal(?::\*\*|\*\*:)\s*([^\n]+)/i);
const goal = goalMatch ? goalMatch[1].trim() : null;
const modeMatch = section.match(/\*\*Mode(?::\*\*|\*\*:)\s*([^\n]+)/i);
const mode = modeMatch ? modeMatch[1].trim().toLowerCase() : null;
const dependsMatch = section.match(/\*\*Depends on(?::\*\*|\*\*:)\s*([^\n]+)/i);
const depends_on = dependsMatch ? dependsMatch[1].trim() : null;
@@ -227,6 +236,7 @@ function cmdRoadmapAnalyze(cwd, raw) {
number: phaseNum,
name: phaseName,
goal,
mode,
depends_on,
plan_count: planCount,
summary_count: summaryCount,

View File

@@ -0,0 +1,81 @@
# Execute-Phase — MVP+TDD Gate (Runtime Enforcement)
> Loaded by `execute-phase` workflow and `gsd-executor` agent only when **both** `MVP_MODE=true` AND `TDD_MODE=true` for the phase. Defines the runtime gate that blocks behavior-adding tasks until a failing-test commit exists.
## When this gate fires
- `MVP_MODE` is `true` (resolved from CLI flag → ROADMAP `**Mode:**` field → config; see `references/planner-mvp-mode.md`).
- `TDD_MODE` is `true` (resolved from `--tdd` flag → `workflow.tdd_mode` config).
- The current task being executed has `tdd="true"` in its `<task>` frontmatter (set by the planner per Phase 1).
- The task's `<behavior>` block lists at least one expected behavior.
If any of these is false, the gate is inactive — execution proceeds normally.
## What the gate checks
For each task gated by MVP+TDD, the executor MUST verify (before running the implementation step):
1. **A failing-test commit exists.** Search git log on the current branch for a commit matching `test({phase}-{plan})` whose subject mentions the same plan as the current task. The commit must touch a test file (`*.test.*`, `*.spec.*`, `tests/**`).
2. **The test was actually red.** The commit message body or the executor's recent shell history must show the test failed when first run. Acceptable evidence:
- Commit message contains `RED:` prefix or `(RED)` tag
- Recent terminal output shows `FAIL` or non-zero exit on the new test before any implementation commit
3. **No implementation commit yet.** No `feat({phase}-{plan})` commit may exist for the same plan ID before the failing-test commit.
If any check fails, the gate trips.
## What "behavior-adding task" means
A task is behavior-adding when:
- Its frontmatter has `tdd="true"` AND
- Its `<behavior>` block names at least one user-visible outcome (not a config-only or doc-only task) AND
- Its `<files>` list includes at least one source file (not exclusively `*.md`, `*.json`, or `*.test.*`)
Pure documentation, configuration, or test-only tasks are skipped by this gate even when both modes are active.
## What happens when the gate trips
The executor MUST:
1. Halt before running the task's implementation step.
2. Emit a structured halt report:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
MVP+TDD GATE TRIPPED — Plan {plan_id}, Task {task_id}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Reason: {missing_red_commit | red_commit_not_failing | feat_before_test}
Behavior expected to be tested:
- {first behavior bullet}
Required next step:
1. Write a failing test for the behavior above.
2. Commit it as: test({phase}-{plan}): {short description}
3. Re-run /gsd execute-phase
```
3. Exit the current execution wave cleanly. Do NOT roll back any prior commits in the same wave.
4. Update `STATE.md` with `last_gate_trip: {plan_id}/{task_id}` so the user can resume after writing the test.
## Escalation: end-of-phase TDD review under MVP+TDD
The existing end-of-phase TDD review (in `workflows/execute-phase.md`'s `tdd_review_checkpoint` step) is normally **advisory** — it surfaces gate violations but does not block phase completion.
Under MVP+TDD, escalate this to **blocking**:
- If any TDD plan is missing a RED or GREEN commit, the executor MUST refuse to mark the phase complete.
- The user is shown the same review table, but the verdict line reads:
> "Phase blocked: {N} TDD plan(s) violate the RED→GREEN gate sequence under MVP+TDD. Resolve and re-run /gsd execute-phase, or override with `/gsd execute-phase {phase} --force-mvp-gate` to ship anyway."
The `--force-mvp-gate` flag is documented but not introduced by this plan — it is the escape hatch the spec mentions; if the user later builds it, the workflow already references the contract.
## What this gate does NOT do
- It does not enforce REFACTOR commits. REFACTOR remains optional (per `references/tdd.md`).
- It does not check test quality (the test could be trivially passing). That's the planner's job.
- It does not run tests. The executor only inspects git log + file system. Running tests is the implementation step's job.
- It does not gate config-only or doc-only tasks (see "behavior-adding task" definition).
## Compatibility with existing TDD discipline
This gate is additive to `references/tdd.md`. Tasks not under MVP+TDD continue to use the existing advisory TDD discipline (RED/GREEN/REFACTOR commits with end-of-phase review checkpoint). Only the runtime gate and the blocking escalation are new.

View File

@@ -0,0 +1,53 @@
# Planner — MVP Mode (Vertical Slice Strategy)
> Loaded by `gsd-planner` only when `MVP_MODE=true`. Standard horizontal-layer planning rules continue to apply for all other phases.
## Core Rule
**Decompose by feature slice, not by technical layer.** Every task must move the user-facing capability forward. After each task, a real user can click through more of the feature than they could before.
**Forbidden** in MVP mode:
- "Create the database schema" as a standalone task
- "Build the API layer" as a standalone task
- "Wire up the UI" as a final integration task
**Required** in MVP mode:
- The first non-test task produces a working end-to-end path. Stubs are allowed for non-critical branches; the happy path must be real.
- Each subsequent task either adds a new slice OR refines an existing slice (validation, error states, edge cases).
- The phase goal is framed as a user story: "**As a** [user], **I want to** [do X], **so that** [Y]."
## Task Order Pattern
For a feature `F`:
1. **Failing end-to-end test** for the happy path of `F`.
2. **Thinnest viable slice** — UI form → API endpoint → DB read/write — that makes the test pass. Hard-coded values, missing validation, no error states are fine here.
3. **Real data layer** — replace any stubs from Task 2 with real queries.
4. **Validation + error states** — invalid input, network failure, empty states.
5. **Production polish** — loading indicators, edge cases, accessibility checks.
Tasks 3-5 are not always all needed; gate by the phase's acceptance criteria.
## Walking Skeleton Mode (`WALKING_SKELETON=true`)
When the orchestrator sets `WALKING_SKELETON=true` (Phase 1 of a new project under `--mvp`), the plan changes shape:
- The "feature" is the application itself. Pick the smallest meaningful capability that proves the full stack works (e.g., "user can sign up and see their name on a dashboard").
- The plan **must include**:
- Project scaffold (framework init, routing, build, lint)
- One real DB read/write
- One real UI interaction wired to the API
- Deployment to a dev environment (or a documented local-run command that exercises the full stack)
- The plan **must produce** `SKELETON.md` in the phase directory alongside `PLAN.md`. Use the template at `@~/.claude/get-shit-done/references/skeleton-template.md`. `SKELETON.md` records the architectural decisions that subsequent phases will build on (chosen framework, DB, deployment target, auth approach, directory layout).
`SKELETON.md` is the architectural backbone for every later vertical slice; treat it as a contract, not a scratchpad.
## Anti-Patterns to Reject
- **Layer cake disguised as slices.** Three "vertical" tasks where Task 1 is "all the schemas", Task 2 is "all the endpoints", Task 3 is "all the UI" — that is horizontal planning with new labels. Reject.
- **Skeleton bloat.** Walking Skeleton is the *thinnest* working stack, not "Phase 1 of a normal app." If Skeleton has more than ~5 tasks, you are not skeletonizing.
- **Premature SPIDR splitting.** SPIDR splitting is the `mvp-phase` command's job (Phase 2 of the PRD), not the planner's. If the phase scope feels too large, surface it via the verification loop, do not split silently.
## Acceptance Test for Your Plan
Before emitting the plan, ask: **after Task N completes, can a real user *do* something they could not do after Task N-1?** If the answer is "no, but the foundation is laid", you have a horizontal task disguised as a slice. Restructure.

View File

@@ -0,0 +1,48 @@
# SKELETON.md Template
> Emitted by `gsd-planner` when `WALKING_SKELETON=true` (Phase 1 + `--mvp` + new project). Records the architectural decisions the rest of the project will build on.
```markdown
# Walking Skeleton — [Project Name]
**Phase:** 1
**Generated:** {ISO date}
## Capability Proven End-to-End
> One sentence: the smallest user-visible capability that exercises the full stack.
Example: "A signed-in user can view their email on a dashboard page served by the deployed app."
## Architectural Decisions
| Decision | Choice | Rationale |
|---|---|---|
| Framework | (e.g., Next.js 15 App Router) | Why this fits the project |
| Data layer | (e.g., Postgres + Drizzle) | Why |
| Auth | (e.g., session cookies + bcrypt) | Why |
| Deployment target | (e.g., Vercel preview) | Why |
| Directory layout | (e.g., feature-folders under src/features/*) | Why |
## Stack Touched in Phase 1
- [ ] Project scaffold (framework, build, lint, test runner)
- [ ] Routing — at least one real route
- [ ] Database — at least one real read AND one real write
- [ ] UI — at least one interactive element wired to the API
- [ ] Deployment — running on dev environment OR documented local full-stack run command
## Out of Scope (Deferred to Later Slices)
> Anything that is *not* in the skeleton. Be explicit — this list prevents future phases from re-litigating Phase 1's minimalism.
- (e.g., password reset, email verification, multi-tenancy)
## Subsequent Slice Plan
Each later phase adds one vertical slice on top of this skeleton without altering its architectural decisions:
- Phase 2: [next user capability]
- Phase 3: [next user capability]
- ...
```

View File

@@ -0,0 +1,69 @@
# SPIDR Story Splitting Rules
> Used by `mvp-phase` workflow when the user-supplied story is too large for a single phase. Per PRD decision Q3, SPIDR runs as a **full interactive flow** — not a lightweight check.
## When SPIDR triggers
Trigger SPIDR splitting if **any** of these size signals fire on the user story:
1. **Compound capabilities.** The story names two or more independent user actions joined by "and" (e.g., "register **and** log in **and** reset their password"). Each "and" is a candidate split point.
2. **Multi-actor.** The story names more than one `[user role]` (e.g., "As a user or admin..."). Each role is a candidate split.
3. **Length.** The assembled story exceeds ~120 chars on a single line.
4. **Vague capability.** The capability is a noun phrase, not a verb-noun pair (e.g., "I want to use the dashboard" — needs to specify *which interaction* with the dashboard).
If none of these fire, skip SPIDR entirely and proceed to ROADMAP write.
## The five SPIDR axes
For each axis, ask one targeted question. The user picks the axis that best fits their story; only one axis is applied per split.
### Spike
> "Is there an unknown that needs research before this can be implemented? If so, the spike is its own phase."
If yes: split out a research phase (no acceptance criteria except "we know enough to plan the rest"). The remaining story becomes a follow-up phase.
### Paths
> "Does this feature have a happy path and one or more error/edge paths?"
If yes: split happy path into the first phase, edge paths into follow-ups. Order: happy path first (it proves the slice works), then progressively edge cases.
### Interfaces
> "Does this feature need to work on more than one interface (web, mobile, API, CLI)?"
If yes: split by interface. Web first if user-facing; API first if integration-driven; mobile last unless it's the primary platform.
### Data
> "Does this feature touch multiple data scopes (one user vs. many, single team vs. multi-tenant, small CSV vs. large dataset)?"
If yes: split by scope. Smallest scope first (one user, single team, small data), then expand.
### Rules
> "Does this feature have multiple business rules that could be added incrementally (basic validation first, then complex policy)?"
If yes: split by rule complexity. Minimum viable rules first; complex policy in follow-ups.
## Workflow
When SPIDR triggers, the workflow:
1. Restates the user-supplied story.
2. Asks "Which SPIDR axis fits best?" with the five options above.
3. Walks through the chosen axis interactively (one focused question), produces a split proposal: "Phase N (this one): X. Phase N+1: Y. Phase N+2: Z."
4. Confirms the split with the user.
5. On accept: writes the FIRST phase's story to the current ROADMAP entry; defers creating new phases for the splits to a follow-up step (the workflow surfaces a list of `/gsd add-phase` invocations the user can run after `mvp-phase` completes — but does not run them automatically, to preserve user control over phase numbering).
6. On reject: proceeds with the original story unchanged.
## Anti-patterns to reject
- **Splitting by technical layer.** "Phase 1: schema. Phase 2: API. Phase 3: UI." That's horizontal planning. Reject.
- **Pre-splitting before the user even sees the original.** Always show the user-supplied story first; only offer split if it triggers a size signal.
- **Splitting more than one axis at once.** SPIDR is one axis per split. If a story needs splitting on two axes (e.g., paths AND data), do paths first, then re-evaluate the resulting smaller stories.
## Reference
See [Mike Cohn — Five Simple But Powerful Ways to Split User Stories](https://www.mountaingoatsoftware.com/blog/five-simple-but-powerful-ways-to-split-user-stories).

View File

@@ -0,0 +1,58 @@
# User Story Template (MVP Mode)
> Used by `mvp-phase` workflow and `gsd-planner` agent when `MVP_MODE=true`. Defines the canonical "As a / I want to / So that" format and the rules for converting it into the `**Goal:**` line in ROADMAP.md.
## Canonical format
```
As a [user role], I want to [capability], so that [outcome].
```
Three required components:
| Slot | Question | Examples |
|---|---|---|
| `[user role]` | Who is the actor? | "new user", "admin", "signed-in customer", "API consumer" |
| `[capability]` | What can they do? | "register and log in", "upload a CSV", "see my dashboard" |
| `[outcome]` | Why does it matter? | "I can access my account", "I can bulk-import contacts", "I can see at a glance what needs attention" |
All three must be present. Refuse to assemble a partial story.
## How it lands in ROADMAP.md
The full user story replaces the existing `**Goal:**` line in the phase section:
**Before:**
```
### Phase 1: User Auth MVP
**Goal:** Users can register and log in
```
**After:**
```
### Phase 1: User Auth MVP
**Goal:** As a new user, I want to register and log in, so that I can access my dashboard.
**Mode:** mvp
```
Two structural rules:
1. The `**Goal:**` line stays on a single line (no line breaks inside the story). If the story is longer than ~120 chars, it should be split into multiple phases via SPIDR (see `spidr-splitting.md`).
2. The `**Mode:** mvp` line is added immediately below `**Goal:**`. If `**Mode:**` already exists, it is replaced (not duplicated).
## How it lands in PLAN.md
The `gsd-planner` agent (with MVP_MODE=true) emits the user story as the first content under the phase header in `PLAN.md`:
```markdown
## Phase Goal
**As a** new user, **I want to** register and log in, **so that** I can access my dashboard.
## Acceptance Criteria
- [ ] ...
## MVP Slice Tasks
...
```
Note the bold-keyword formatting (`**As a**`, `**I want to**`, `**so that**`) is for the PLAN.md emit only. The ROADMAP.md `**Goal:**` line uses prose form (the keywords are not bolded inside the goal line, since the goal is itself a single bolded label).

View File

@@ -0,0 +1,85 @@
# Verify-Work — MVP Mode UAT Framing
> Loaded by `verify-work` workflow and `gsd-verifier` agent only when the phase under verification has `mode: mvp` in ROADMAP.md. Reframes UAT generation from technical checks to user-flow walk-throughs.
## Core rule
**Show expected, ask if reality matches** — same philosophy as standard verify-work (from `workflows/verify-work.md`). The MVP-mode change is WHAT gets shown:
- **Standard verify-work:** "The API endpoint at /users/register returns 201 with the new user's ID." → user confirms.
- **MVP verify-work:** "Open the registration page. Fill in 'name', 'email', 'password'. Click Submit. You should see your dashboard with your name in the header." → user confirms.
The user-flow form mirrors what a real user does: open, fill, click, see. No HTTP verbs, no JSON shapes, no error codes.
## When this framing applies
The framing fires when:
- The phase under verification has `**Mode:** mvp` in ROADMAP.md (parsed via `gsd-sdk query roadmap.get-phase --pick mode`).
- AND the phase has a user-story-formatted goal (set by `/gsd mvp-phase` per Phase 2): "As a [user role], I want to [capability], so that [outcome]."
If the phase has `mode: mvp` but the goal is NOT in user-story format, the verifier surfaces this as a discrepancy and asks the user to run `/gsd mvp-phase` to reformat the goal — same pattern as the planner agent under MVP_MODE (per `references/planner-mvp-mode.md`).
## Generated UAT script structure under MVP mode
The UAT script generated by `verify-work` under MVP mode has THREE sections, in this exact order:
### 1. User-flow walk-through (always first, always required)
Derive ordered steps from the phase's user-story goal:
1. The first step opens the entry point ("Open the app", "Navigate to /register", "Run `gsd mvp-phase 1`").
2. Each subsequent step is one user action: fill, click, type, observe.
3. The final step asserts the user-visible outcome from the `[outcome]` clause of the user story.
Format each step as: "**Step N: [action]** — Expected: [what the user should see]". The user responds with one of:
- `yes` / `y` / `next` / empty → step passes
- Anything else → step is logged as an issue, and the script halts (do not proceed to step N+1 with a broken N).
If ALL user-flow steps pass, advance to section 2. If any step fails, the verdict is FAIL — do not run technical checks.
### 2. Technical checks (only if section 1 passes)
After the user flow passes, run the technical checks that would normally run in non-MVP mode:
- API endpoint schema verification (if the phase shipped APIs)
- Error state behavior (4xx, 5xx codes; invalid input handling)
- Edge cases (empty data, large data, concurrent requests if applicable)
- Cross-browser / cross-runtime checks (if applicable)
These are the same checks `verify-work` would run without MVP mode — just deferred until the user flow proves the slice actually works for a user.
### 3. Coverage check (always last, always required)
Verify that the user-story `[outcome]` clause is observably true in the codebase:
- If the outcome is "I can access my dashboard", verify a dashboard route exists and renders for an authenticated user.
- If the outcome is "I can bulk-import contacts", verify the import path produces persisted records.
Coverage is a goal-backward check: "did this phase deliver what its user story promised?" — sourced from the existing `gsd-verifier` agent's goal-backward methodology, narrowed to the user story.
## Anti-patterns to reject under MVP mode
- **Lead with technical checks.** "Step 1: GET /api/users/me returns 200." Reject. The user does not see API endpoints. Reorder so a user action comes first.
- **Schema-as-feature.** "User has a `name` field on the User model." Reject. The user does not see database fields. Express the same check as a user-visible outcome ("the user's name appears in the dashboard header").
- **Skip user flow because the test passed.** The unit test passing in CI is not evidence that the user flow works. The user-flow walk-through is mandatory under MVP mode even when all unit tests are green.
## Compatibility with existing verify-work philosophy
The "show expected, ask if reality matches" model is preserved. The user still types `yes` / `next` / empty to advance. The UAT.md state file format is unchanged. Only the WHAT changes — under MVP mode, the "expected" is a user-visible outcome rather than a technical assertion.
## Output: VERIFICATION.md changes under MVP mode
The `gsd-verifier` agent produces `VERIFICATION.md`. Under MVP mode, the report adds a top-level "User Flow Coverage" section that maps each step of the user story to evidence in the codebase:
```markdown
## User Flow Coverage
User story: «As a new user, I want to register and log in, so that I can access my dashboard.»
| Step | Expected | Evidence | Status |
|------|----------|----------|--------|
| Register | Form at /register accepts name/email/password | src/app/register/page.tsx:12 (form component) | ✓ |
| Submit | Persists user, redirects to /dashboard | src/api/register/route.ts:34 (db.insert + redirect) | ✓ |
| See dashboard | Dashboard page renders, shows user's name | src/app/dashboard/page.tsx:8 (greeting line) | ✓ |
| Outcome | "Access my dashboard" — user lands on a populated page | dashboard route + greeting both verified above | ✓ |
```
Standard technical-check sections of VERIFICATION.md remain (API verification, error handling, etc.) but are appended below "User Flow Coverage", not above.

View File

@@ -138,6 +138,23 @@ if [[ ! "$ARGUMENTS" =~ --auto ]]; then
gsd-sdk query config-set workflow._auto_chain_active false || true
fi
```
Resolve MVP_MODE (CLI flag → roadmap phase mode → config → false):
```bash
MVP_MODE_CFG=$(gsd-sdk query config-get workflow.mvp_mode 2>/dev/null || echo "false")
PHASE_MODE=$(gsd-sdk query roadmap.get-phase "${PHASE_NUMBER}" --pick mode 2>/dev/null || echo "")
MVP_MODE=false
if [[ "$ARGUMENTS" =~ --mvp ]] || [ "$PHASE_MODE" = "mvp" ] || [ "$MVP_MODE_CFG" = "true" ]; then MVP_MODE=true; fi
```
**MVP+TDD gate.** When `MVP_MODE=true` AND `TDD_MODE=true` AND `TASK_TDD=true`, require a failing-test commit before the implementation step. Doc-only / config-only tasks are exempt. See `execute-mvp-tdd.md` for full halt report format and "behavior-adding task" definition.
```bash
if [ "$MVP_MODE" = "true" ] && [ "$TDD_MODE" = "true" ] && [ "$TASK_TDD" = "true" ]; then
RED_COMMIT=$(git log --oneline --grep="^test(${PHASE_NUMBER}-${PLAN_ID}):" -- "**/*.test.*" "**/*.spec.*" "tests/" | head -1)
if [ -z "$RED_COMMIT" ]; then echo "MVP+TDD GATE TRIPPED: missing RED commit for ${PLAN_ID}/${TASK_ID}"; exit 1; fi
fi
```
On trip, updates `STATE.md` with `last_gate_trip: {plan_id}/{task_id}`.
</step>
<step name="check_blocking_antipatterns" priority="first">
@@ -1053,6 +1070,14 @@ TDD_PLANS=$(grep -rl "^type: tdd" "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l |
| {id} | ✓ | ✗ | — | FAIL |
```
**Escalation under MVP+TDD.** When `MVP_MODE=true` AND `TDD_MODE=true`, the review verdict escalates from advisory to **blocking**: missing RED or GREEN gate commits prevent marking the phase complete.
```
Phase blocked: {N} TDD plan(s) violate the RED→GREEN gate sequence under MVP+TDD.
Resolve and re-run /gsd execute-phase, or override with
/gsd execute-phase {phase} --force-mvp-gate to ship anyway.
```
`--force-mvp-gate` is the escape hatch (documented, not yet implemented). Without both modes active, the existing advisory behavior is preserved.
**Gate violations are advisory** — they do not block execution but are surfaced to the user for review. The verifier agent (step `verify_phase_goal`) will also check TDD discipline as part of its quality assessment.
</step>

View File

@@ -0,0 +1,195 @@
<purpose>
Guide the user through MVP-mode planning for a phase. Prompts for an "As a / I want to / So that" user story, runs SPIDR splitting check on the story, writes the result to ROADMAP.md, and delegates to `/gsd plan-phase` (which auto-detects MVP via the roadmap mode field shipped in PRD Phase 1).
</purpose>
<required_reading>
@~/.claude/get-shit-done/references/user-story-template.md
@~/.claude/get-shit-done/references/spidr-splitting.md
@~/.claude/get-shit-done/references/planner-mvp-mode.md
</required_reading>
<runtime_note>
**Copilot (VS Code):** Use `vscode_askquestions` wherever this workflow calls `AskUserQuestion`. They are equivalent.
**TEXT_MODE fallback:** Set TEXT_MODE=true if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is true. When TEXT_MODE is active, replace every AskUserQuestion call with a plain-text numbered list and ask the user to type their choice number.
</runtime_note>
<process>
## 1. Parse and validate phase argument
Extract the phase number from `$ARGUMENTS` (integer or decimal like `2.1`). Optional flag: `--force` (allow operating on `in_progress` / `completed` phases).
If no argument:
```
ERROR: Phase number required
Usage: /gsd mvp-phase <phase-number>
Example: /gsd mvp-phase 1
Example: /gsd mvp-phase 2.1
```
Exit.
Normalize per `@~/.claude/get-shit-done/references/phase-argument-parsing.md` (zero-pad integer phases to two digits).
## 2. Validate phase exists and check status
```bash
PHASE_INFO=$(gsd-sdk query roadmap.get-phase "${PHASE}")
PHASE_FOUND=$(echo "$PHASE_INFO" | jq -r '.found')
PHASE_NAME=$(echo "$PHASE_INFO" | jq -r '.phase_name')
PHASE_GOAL=$(echo "$PHASE_INFO" | jq -r '.goal')
PHASE_MODE=$(echo "$PHASE_INFO" | jq -r '.mode // ""')
PHASE_COMPLETE=$(echo "$PHASE_INFO" | jq -r '.roadmap_complete // false')
```
If `PHASE_FOUND` is `false`: error and exit. Suggest `/gsd add-phase` or `/gsd insert-phase` to create the phase first.
**Status guard.** If the phase is `in_progress` (has plans but not complete) or `completed`, refuse unless `--force` is in `$ARGUMENTS`:
```
ERROR: Phase ${PHASE} is currently ${STATUS}.
Converting an active or completed phase to MVP mode mid-flight will
invalidate any existing plans and summaries.
To proceed anyway: /gsd mvp-phase ${PHASE} --force
```
**Already-MVP guard.** If `PHASE_MODE` is already `mvp`, surface this and ask whether to re-prompt the user story or abort:
> "Phase ${PHASE} is already in MVP mode with goal: «${PHASE_GOAL}». Re-run user-story prompts and SPIDR check?"
Use `AskUserQuestion` with options [Re-prompt / Abort]. On Abort, exit cleanly. On Re-prompt, proceed.
## 3. User story prompts
Run three sequential `AskUserQuestion` calls. Each is free-text. After all three, assemble into the canonical sentence per `@~/.claude/get-shit-done/references/user-story-template.md`:
**Prompt 1 — As a:**
> "As a [user role]?"
> (Examples: "new user", "admin", "signed-in customer", "API consumer")
**Prompt 2 — I want to:**
> "I want to [capability]?"
> (Examples: "register and log in", "upload a CSV", "see my dashboard")
**Prompt 3 — So that:**
> "So that [outcome]?"
> (Examples: "I can access my account", "I can bulk-import contacts", "I can see at a glance what needs attention")
Assemble:
```
USER_STORY="As a ${ROLE}, I want to ${CAPABILITY}, so that ${OUTCOME}."
```
If any of the three answers is empty or whitespace-only, error and re-prompt that single field. Do NOT proceed with a partial story.
## 4. SPIDR splitting check
Run the SPIDR rules from `@~/.claude/get-shit-done/references/spidr-splitting.md`. Briefly:
**Trigger evaluation.** Check the assembled `USER_STORY` against the four size signals from the reference (compound capabilities, multi-actor, length > 120 chars, vague capability). If none fire, **skip SPIDR** entirely — go to step 5.
**If SPIDR triggers.**
a) Restate the story to the user:
> "Your story: «${USER_STORY}»
>
> This story has [signal description, e.g., 'two compound capabilities joined by and']. Splitting it into multiple phases will produce a cleaner Walking Skeleton and reduce the risk of mid-phase scope creep.
>
> Want to walk through SPIDR splitting?"
Use `AskUserQuestion` with options [Yes, walk through SPIDR / No, proceed with the story as-is].
If "No": skip SPIDR, go to step 5.
If "Yes": continue to (b).
b) Ask which SPIDR axis fits best:
> "Which axis best fits how to split this story?"
Use `AskUserQuestion` with the five options from `spidr-splitting.md` (Spike / Paths / Interfaces / Data / Rules). Each option includes its targeted question as the description so the user can pick by understanding what each axis means.
c) Walk through the chosen axis with **one** targeted question (not all five). For example, if the user picked "Paths":
> "Does this feature have a happy path and one or more error/edge paths?"
Free-text response. Workflow parses to identify the split.
d) Produce a split proposal. Example:
> "Proposed split (Paths axis):
> - **Phase ${PHASE} (this one):** Happy path — ${HAPPY_STORY}
> - **Phase ${PHASE+1} (new):** Edge case — ${EDGE_STORY}
>
> Accept this split?"
Use `AskUserQuestion` [Accept / Modify / Reject].
- **Accept**: `USER_STORY` becomes the first split's story (`${HAPPY_STORY}` in the example). Surface the remaining splits as a list of `/gsd add-phase` invocations the user can run after this command completes — do NOT auto-create the new phases (preserve user control over numbering).
- **Modify**: re-prompt the splits one more time, then accept or reject.
- **Reject**: revert `USER_STORY` to the original, proceed without splitting.
## 5. Update ROADMAP.md
Read `ROADMAP.md`. Find the section for `Phase ${PHASE}`. Apply two edits:
**Edit 1 — Update Goal line.**
Find: `**Goal:** ${OLD_GOAL_TEXT}`
Replace with: `**Goal:** ${USER_STORY}`
**Edit 2 — Insert Mode line.**
If `**Mode:**` already exists in the section (replacing or re-running), update it to `**Mode:** mvp`.
If `**Mode:**` does not exist, insert `**Mode:** mvp` on the line immediately after `**Goal:**`.
Show the user a unified diff (lines being changed) and ask:
> "Apply these changes to ROADMAP.md?"
Use `AskUserQuestion` [Apply / Cancel]. On Cancel, exit without writing.
On Apply, write the updated `ROADMAP.md` atomically (read-edit-write).
## 6. Verify the write
```bash
NEW_MODE=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick mode)
NEW_GOAL=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick goal)
```
Assert:
- `NEW_MODE` equals `mvp`
- `NEW_GOAL` equals the assembled user story
If either assertion fails, surface the discrepancy to the user and exit. Do not proceed to plan-phase delegation with a half-applied write.
## 7. Delegate to /gsd plan-phase
Invoke `/gsd plan-phase ${PHASE}` (no flags). Phase 1's MVP_MODE resolution chain (CLI flag → roadmap mode → config → false) will detect the new `**Mode:** mvp` line and run plan-phase in vertical-slice mode automatically.
The Walking Skeleton gate (also from Phase 1) will fire automatically if `${PHASE} == "01"` and there are zero prior phase summaries.
## 8. Surface deferred phase splits (if any)
If SPIDR produced a split in step 4, append a final user-facing message:
> "**SPIDR split deferred phases.**
>
> Your original story was split. The first slice is now planned via plan-phase.
> To create the remaining slice(s) as new phases, run:
>
> - `/gsd add-phase` — for the next slice: «${SPLIT_2_STORY}»
> - `/gsd add-phase` — for the next slice: «${SPLIT_3_STORY}»
>
> Each will be added to the end of the current milestone. You can then run
> `/gsd mvp-phase <new-phase-number>` on each to plan them as MVP slices."
## 9. Exit
Workflow ends. The phase is now in MVP mode with a planned PLAN.md, optionally with deferred follow-up phases surfaced for the user.
</process>

View File

@@ -1117,6 +1117,21 @@ If "adjust": Return to scoping.
gsd-sdk query commit "docs: define v1 requirements" --files .planning/REQUIREMENTS.md
```
## 7.5. Project Structure Mode
**If auto mode:** Set `PROJECT_MODE=mvp` and skip this prompt.
**Mode prompt: Vertical MVP vs Horizontal Layers.**
Ask the user how they want to structure the project. Use `AskUserQuestion` with two options:
- **Vertical MVP** — get a working app fast, add features slice by slice. Each phase delivers an end-to-end user capability. *(Recommended for new products and rapid-iteration MVPs.)*
- **Horizontal Layers** — build complete technical layers (DB → API → UI → wiring) and assemble at the end. *(Better for infrastructure-heavy projects with multiple developers.)*
Set `PROJECT_MODE=mvp` if the user picks Vertical MVP, otherwise `PROJECT_MODE=standard`.
When `TEXT_MODE=true` (per the workflow's existing TEXT_MODE handling for non-Claude runtimes), present the same two options as a plain-text numbered list and ask the user to type their choice number.
## 8. Create Roadmap
Display stage banner:
@@ -1129,6 +1144,23 @@ Display stage banner:
◆ Spawning roadmapper...
```
**ROADMAP.md template — mode-aware emit.** When generating the initial ROADMAP.md:
- If `PROJECT_MODE=mvp`: under each `### Phase N:` header, emit `**Mode:** mvp` on the line immediately following `**Goal:**`. This sets every initial phase to MVP mode (per Phase-4-Persistence decision: per-phase mode, not project-wide config).
- If `PROJECT_MODE=standard`: emit the standard ROADMAP.md template with no `**Mode:**` lines (Horizontal Layers standard template — no behavioral change for users who pick Horizontal Layers).
Example MVP-mode emit for Phase 1:
```markdown
### Phase 1: [Name]
**Goal:** [Goal]
**Mode:** mvp
**Success Criteria**:
1. [Criterion]
```
Pass `PROJECT_MODE` to the roadmapper so it applies the correct template.
Spawn gsd-roadmapper agent with path references:
```

View File

@@ -38,6 +38,7 @@ AGENT_SKILLS_PLANNER=$(gsd-sdk query agent-skills gsd-planner)
AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
CONTEXT_WINDOW=$(gsd-sdk query config-get context_window 2>/dev/null || echo "200000")
TDD_MODE=$(gsd-sdk query config-get workflow.tdd_mode 2>/dev/null || echo "false")
MVP_MODE_CFG=$(gsd-sdk query config-get workflow.mvp_mode 2>/dev/null || echo "false")
```
When `TDD_MODE` is `true`, the planner agent is instructed to apply `type: tdd` to eligible tasks using heuristics from `references/tdd.md`. The planner's `<required_reading>` is extended to include `@~/.claude/get-shit-done/references/tdd.md` so gate enforcement rules are available during planning.
@@ -54,10 +55,37 @@ Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_
## 2. Parse and Normalize Arguments
Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--skip-ui`, `--prd <filepath>`, `--reviews`, `--text`, `--bounce`, `--skip-bounce`, `--chunked`).
Extract from $ARGUMENTS: phase number (integer or decimal like `2.1`), flags (`--research`, `--skip-research`, `--gaps`, `--skip-verify`, `--skip-ui`, `--prd <filepath>`, `--reviews`, `--text`, `--bounce`, `--skip-bounce`, `--chunked`, `--mvp`).
Set `TEXT_MODE=true` if `--text` is present in $ARGUMENTS OR `text_mode` from init JSON is `true`. When `TEXT_MODE` is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for Claude Code remote sessions (`/rc` mode) where TUI menus don't work through the Claude App.
**MVP_MODE resolution.** Resolve `MVP_MODE` once and reuse for the rest of the workflow. Order (first hit wins):
1. **CLI flag.** If `$ARGUMENTS` contains `--mvp`, set `MVP_MODE=true`.
2. **Roadmap phase mode.** Otherwise, query the phase's mode field:
```bash
PHASE_MODE=$(gsd-sdk query roadmap.get-phase "${PHASE}" --pick mode)
if [ "$PHASE_MODE" = "mvp" ]; then MVP_MODE=true; fi
```
3. **Config default.** Otherwise, `MVP_MODE="$MVP_MODE_CFG"` (resolved in Step 1).
4. **Fallback.** `MVP_MODE=false`.
The mode is **all-or-nothing per phase** (per PRD decision Q1). Do not allow `--mvp` to apply selectively to a subset of tasks within a phase.
**Walking Skeleton gate.** When `MVP_MODE=true` AND `phase_number == "01"` AND there are zero prior phase summaries (new project), the planner runs in **Walking Skeleton mode** (per PRD decision Q2 — new projects only). Detect with:
```bash
WALKING_SKELETON=false
if [ "$MVP_MODE" = "true" ] && [ "$padded_phase" = "01" ]; then
PRIOR_SUMMARIES=$(gsd-sdk query phases.list --pick summaries_total 2>/dev/null || echo "0")
if [ "$PRIOR_SUMMARIES" = "0" ]; then WALKING_SKELETON=true; fi
fi
```
When `WALKING_SKELETON=true`:
- Planner is instructed to produce `SKELETON.md` in the phase directory alongside `PLAN.md`. The template lives at `@~/.claude/get-shit-done/references/skeleton-template.md`.
- The plan must scaffold project + routing + one real DB read/write + one real UI interaction + dev deployment — the thinnest possible end-to-end working slice.
Extract `--prd <filepath>` from $ARGUMENTS. If present, set PRD_FILE to the filepath.
**If no phase number:** Detect next unplanned phase from roadmap.
@@ -756,6 +784,15 @@ ${TDD_MODE === 'true' ? `
Each TDD plan gets one feature with RED/GREEN/REFACTOR gate sequence.
</tdd_mode_active>
` : ''}
**MVP_MODE:** ${MVP_MODE} (when true, follow vertical-slice rules from `@~/.claude/get-shit-done/references/planner-mvp-mode.md`; when false, ignore MVP guidance entirely.)
**WALKING_SKELETON:** ${WALKING_SKELETON} (when true, the first deliverable must be a Walking Skeleton — produce SKELETON.md alongside PLAN.md.)
${MVP_MODE === 'true' ? `
<mvp_mode_active>
**MVP Mode is ENABLED.** Follow vertical-slice planning rules from @~/.claude/get-shit-done/references/planner-mvp-mode.md. Each plan must deliver a complete vertical slice — thin end-to-end functionality rather than horizontal layers.
</mvp_mode_active>
` : ''}
</planning_context>
<downstream_consumer>

View File

@@ -135,6 +135,35 @@ CONTEXT: [✓ if has_context | - if not]
</step>
<step name="mvp_display">
**MVP-mode display (when phase has `**Mode:** mvp` in ROADMAP.md).**
Resolve `MVP_MODE` per phase:
```bash
PHASE_MODE=$(gsd-sdk query roadmap.get-phase "${PHASE_NUMBER}" --pick mode 2>/dev/null || echo "")
MVP_MODE=false
if [ "$PHASE_MODE" = "mvp" ]; then
MVP_MODE=true
fi
```
When `MVP_MODE=true`, the per-phase progress block adds a **user-flow status** sub-block sourced from the phase's PLAN.md task names. Each task whose name reads like a user-visible capability (e.g., "Register flow", "Login flow", "Password reset") is rendered as a status line:
```
Phase 1 — User Auth MVP
✅ Walking Skeleton complete ← from SKELETON.md existence
✅ Register flow working ← from PLAN.md task with summary
✅ Login flow working ← from PLAN.md task with summary
🔄 Password reset (in progress) ← from PLAN.md task without summary
⬜ Email verification ← from PLAN.md task not yet started
```
**User-flow filter:** Tasks whose names are technical-sounding ("Wire DB schema", "Create migration", "Bump deps") are NOT rendered as user-flow status lines. Heuristic: a task name is user-flow-shaped if it ends in "flow", "page", "screen", or starts with a verb the user would recognize ("Register", "Login", "Upload", "View"). Tasks that fail the heuristic still count toward the standard task progress total but don't appear in the user-flow sub-block.
When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line), fall back to the standard display path — no behavioral change.
</step>
<step name="route">
**Determine next action based on verified counts.**

View File

@@ -51,6 +51,24 @@ X/Y plans complete (Z%)
If no `.planning/` directory exists, inform the user to run `/gsd-new-project` first.
</step>
<step name="mvp_summary">
**MVP phase summary.** Read all phases via `gsd-sdk query roadmap.analyze` (Phase 1's `cmdRoadmapAnalyze` surfaces a `mode` field per phase). Count phases by mode:
```bash
ANALYZE=$(gsd-sdk query roadmap.analyze)
MVP_COUNT=$(echo "$ANALYZE" | jq '[.phases[] | select(.mode == "mvp")] | length')
TOTAL_COUNT=$(echo "$ANALYZE" | jq '.phases | length')
```
Emit a summary line in the stats output:
```
Phases: ${TOTAL_COUNT} total | ${MVP_COUNT} MVP | $((TOTAL_COUNT - MVP_COUNT)) standard
```
If `MVP_COUNT == 0`, the project has no MVP-mode phases — omit the line (no clutter for non-MVP projects).
</step>
</process>
<success_criteria>

View File

@@ -37,6 +37,15 @@ AGENT_SKILLS_CHECKER=$(gsd-sdk query agent-skills gsd-plan-checker)
```
Parse JSON for: `planner_model`, `checker_model`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `has_verification`, `uat_path`.
```bash
# MVP mode detection — read the phase's mode field from ROADMAP.md
PHASE_MODE=$(gsd-sdk query roadmap.get-phase "${phase_number}" --pick mode 2>/dev/null || echo "")
MVP_MODE=false
if [ "$PHASE_MODE" = "mvp" ]; then
MVP_MODE=true
fi
```
</step>
<step name="check_active_session">
@@ -135,6 +144,21 @@ Read each SUMMARY.md to extract testable deliverables.
</step>
<step name="extract_tests">
**MVP-mode UAT framing.** When `MVP_MODE=true`, follow the rules in `@~/.claude/get-shit-done/references/verify-mvp-mode.md`. Briefly:
1. Generate the UAT script in three ordered sections: (a) user-flow walk-through derived from the phase's user-story goal, (b) technical checks (deferred — only run after user flow passes), (c) coverage check (goal-backward, narrowed to the user story's outcome clause).
2. **User-flow steps run first.** Each step is one user action: open, fill, click, type, observe. No HTTP verbs, no JSON shapes, no error codes in user-flow steps.
3. **Technical checks are deferred.** They run AFTER the user flow passes — same checks as non-MVP mode (endpoint schemas, error states, edge cases), just reordered.
4. **If user-flow step N fails, do not advance.** The verdict is FAIL; technical checks do not run. The user can re-run after fixing the underlying flow.
When `MVP_MODE=false` (mode is null, absent, or the phase has no `**Mode:**` line in ROADMAP.md), fall back to the standard UAT generation path — no behavioral change.
**User-story format guard.** When `MVP_MODE=true`, also verify the phase's goal is in user-story format (matches `/^As a .+, I want to .+, so that .+\.$/`). If the goal is `mode: mvp` but NOT in user-story format, surface the discrepancy:
> "Phase ${PHASE} has `**Mode:** mvp` in ROADMAP.md but the **Goal:** is not in user-story format. Run `/gsd mvp-phase ${PHASE}` to set a user-story goal before verifying."
Halt UAT generation and exit cleanly. Do not attempt to derive user-flow steps from a non-user-story goal — that would produce a low-quality UAT.
**Extract testable deliverables from SUMMARY.md:**
Parse for:

7
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "get-shit-done-cc",
"version": "1.39.0-rc.4",
"version": "1.50.0-canary.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "get-shit-done-cc",
"version": "1.39.0-rc.4",
"version": "1.50.0-canary.0",
"license": "MIT",
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.84",
@@ -14,7 +14,8 @@
},
"bin": {
"get-shit-done-cc": "bin/install.js",
"gsd-sdk": "bin/gsd-sdk.js"
"gsd-sdk": "bin/gsd-sdk.js",
"gsd-tools": "bin/gsd-sdk.js"
},
"devDependencies": {
"c8": "^11.0.0"

View File

@@ -1,6 +1,6 @@
{
"name": "get-shit-done-cc",
"version": "1.39.0-rc.4",
"version": "1.50.0-canary.0",
"description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by TÂCHES.",
"bin": {
"get-shit-done-cc": "bin/install.js",

4
sdk/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@gsd-build/sdk",
"version": "0.1.0",
"version": "1.50.0-canary.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@gsd-build/sdk",
"version": "0.1.0",
"version": "1.50.0-canary.0",
"license": "MIT",
"dependencies": {
"@anthropic-ai/claude-agent-sdk": "^0.2.84",

View File

@@ -1,6 +1,6 @@
{
"name": "@gsd-build/sdk",
"version": "1.39.0-rc.4",
"version": "1.50.0-canary.0",
"description": "GSD SDK — programmatic interface for running GSD plans via the Agent SDK",
"type": "module",
"main": "dist/index.js",

View File

@@ -0,0 +1,85 @@
/**
* execute-phase MVP+TDD gate — contract test
* Verifies the workflow markdown documents the gate's resolution chain,
* per-task firing condition, and end-of-phase review escalation.
*/
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
describe('execute-phase — MVP+TDD gate', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('Step 1 resolves MVP_MODE from roadmap mode field', () => {
assert.match(content, /MVP_MODE/, 'workflow must declare MVP_MODE');
assert.match(
content,
/roadmap[^\n]*mode|phase[^\n]*\.mode|\.mode\s*=/i,
'must consult phase mode from roadmap'
);
});
test('gate fires when both MVP_MODE and TDD_MODE are true', () => {
assert.match(
content,
/MVP_MODE[^\n]*TDD_MODE|TDD_MODE[^\n]*MVP_MODE/,
'workflow must combine MVP_MODE and TDD_MODE for the gate'
);
});
test('per-task gate is documented before behavior-adding task execution', () => {
assert.match(content, /MVP\+TDD\s*gate|mvp[\s-]?tdd[\s-]?gate/i, 'must label the gate');
assert.match(content, /failing[\s-]?test\s*commit|test\(.+\):.*RED/i, 'must reference failing-test commit check');
});
test('end-of-phase TDD review escalates to blocking under MVP+TDD', () => {
assert.match(
content,
/blocking[^\n]*MVP|MVP[^\n]*blocking|escalat\w+\s*to\s*blocking/i,
'must escalate end-of-phase review to blocking'
);
});
test('workflow references execute-mvp-tdd.md', () => {
assert.match(content, /execute-mvp-tdd\.md/, 'must reference the gate semantics file');
});
});
describe('execute-phase MVP+TDD — resolution chain integration', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject(); });
afterEach(() => { cleanup(tmpDir); });
test('roadmap.get-phase --pick mode returns mvp when **Mode:** mvp set', () => {
fs.writeFileSync(
path.join(tmpDir, '.planning', 'ROADMAP.md'),
`# Roadmap\n\n## v1.0.0\n\n### Phase 1: User Auth\n**Goal:** As a user, I want to log in, so that I can access.\n**Mode:** mvp\n`
);
const result = runGsdTools('roadmap get-phase 1 --pick mode', tmpDir);
assert.ok(result.success);
assert.strictEqual(result.output.trim(), 'mvp');
});
test('roadmap.get-phase --pick mode returns null/empty when no Mode line', () => {
fs.writeFileSync(
path.join(tmpDir, '.planning', 'ROADMAP.md'),
`# Roadmap\n\n## v1.0.0\n\n### Phase 1: User Auth\n**Goal:** Users can log in.\n`
);
const result = runGsdTools('roadmap get-phase 1 --pick mode', tmpDir);
if (result.success) {
assert.ok(result.output.trim() === '' || result.output.trim() === 'null');
}
});
test('config-get workflow.mvp_mode default is unset in fresh project', () => {
const result = runGsdTools('config-get workflow.mvp_mode', tmpDir);
if (result.success) {
assert.notStrictEqual(result.output.trim(), 'true');
}
});
});

View File

@@ -0,0 +1,33 @@
/**
* gsd-executor agent — MVP+TDD gate section contract
* Verifies the agent definition contains a section instructing the executor
* to halt and report when the runtime gate trips.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT = path.join(__dirname, '..', 'agents', 'gsd-executor.md');
const REF = path.join(__dirname, '..', 'get-shit-done', 'references', 'execute-mvp-tdd.md');
describe('gsd-executor — MVP+TDD gate section', () => {
const content = fs.readFileSync(AGENT, 'utf-8');
test('agent defines an MVP+TDD Gate section', () => {
assert.match(content, /MVP\+TDD\s*Gate|MVP[\s-]?TDD[\s-]?gate/i, 'must label the gate');
});
test('agent instructs halt-and-report when gate trips', () => {
assert.match(content, /halt|stop[^\n]*gate|gate[^\n]*halt/i, 'must instruct halt');
assert.match(content, /report|surface|emit/i, 'must instruct report');
});
test('agent references execute-mvp-tdd.md', () => {
assert.match(content, /execute-mvp-tdd\.md/, 'must reference the gate semantics file');
});
test('referenced file exists on disk', () => {
assert.ok(fs.existsSync(REF), `${REF} must exist`);
});
});

View File

@@ -0,0 +1,39 @@
/**
* graphify — MVP visual differentiation contract test
* Per PRD Q5: distinct node color + 'MVP' label suffix.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const CMD = path.join(__dirname, '..', 'commands', 'gsd', 'graphify.md');
describe('graphify — MVP visualization', () => {
const content = fs.readFileSync(CMD, 'utf-8');
test('command documents distinct color for MVP-mode phases', () => {
assert.match(content, /MVP/, 'must mention MVP in color rule');
assert.match(
content,
/color|fill|hex|#[0-9a-f]{3,6}/i,
'must reference a color/fill rule for MVP nodes'
);
});
test('command documents MVP label suffix on node text', () => {
assert.match(
content,
/MVP[^\n]*label|label[^\n]*MVP|MVP\s*suffix|suffix[^\n]*MVP/i,
'must add an MVP label/suffix to node text'
);
});
test('falls back to standard rendering when phase mode is null', () => {
assert.match(
content,
/mode[^\n]*null|absent|not.*mvp|standard.*render/i,
'must specify fallback when mode is not mvp'
);
});
});

View File

@@ -22,6 +22,7 @@ const PROSE_ALLOWLIST = new Set([
'intel',
'into',
'or',
'init', // bare "init" appears in prose examples; real commands are init.<subcommand>
'init.',
]);

View File

@@ -0,0 +1,39 @@
/**
* /gsd mvp-phase command — frontmatter contract test
* Verifies the command exists, has required frontmatter fields, and
* points to the workflow file.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const CMD = path.join(__dirname, '..', 'commands', 'gsd', 'mvp-phase.md');
describe('/gsd mvp-phase command frontmatter', () => {
test('command file exists', () => {
assert.ok(fs.existsSync(CMD), `${CMD} must exist`);
});
test('frontmatter declares correct command name', () => {
const content = fs.readFileSync(CMD, 'utf-8');
assert.match(content, /^name:\s*gsd:mvp-phase\b/m);
});
test('argument-hint mentions phase number', () => {
const content = fs.readFileSync(CMD, 'utf-8');
assert.match(content, /argument-hint:[^\n]*phase/i);
});
test('allowed-tools includes Read, Write, Bash, Task, AskUserQuestion', () => {
const content = fs.readFileSync(CMD, 'utf-8');
for (const tool of ['Read', 'Write', 'Bash', 'Task', 'AskUserQuestion']) {
assert.match(content, new RegExp(`-\\s*${tool}\\b`), `allowed-tools must include ${tool}`);
}
});
test('execution_context points to the workflow file', () => {
const content = fs.readFileSync(CMD, 'utf-8');
assert.match(content, /workflows\/mvp-phase\.md/);
});
});

View File

@@ -0,0 +1,81 @@
/**
* mvp-phase ROADMAP mutation — integration smoke test
* Simulates the workflow's step 5 (Update ROADMAP.md) and verifies that
* roadmap.get-phase returns the expected mode and user-story goal afterward.
*/
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const ROADMAP_BEFORE = `# Roadmap
## v1.0.0
### Phase 1: User Auth
**Goal:** Users can register and log in
**Success Criteria**:
1. Registration works
2. Login works
`;
const ROADMAP_AFTER_MVP = `# Roadmap
## v1.0.0
### Phase 1: User Auth
**Goal:** As a new user, I want to register and log in, so that I can access my dashboard.
**Mode:** mvp
**Success Criteria**:
1. Registration works
2. Login works
`;
describe('mvp-phase — ROADMAP mutation result', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject(); });
afterEach(() => { cleanup(tmpDir); });
test('after spec mutation, roadmap.get-phase reports mode=mvp', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_AFTER_MVP);
const result = runGsdTools('roadmap get-phase 1 --pick mode', tmpDir);
assert.ok(result.success);
assert.strictEqual(result.output.trim(), 'mvp');
});
test('after spec mutation, roadmap.get-phase reports the full user story as goal', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_AFTER_MVP);
const result = runGsdTools('roadmap get-phase 1 --pick goal', tmpDir);
assert.ok(result.success);
assert.strictEqual(
result.output.trim(),
'As a new user, I want to register and log in, so that I can access my dashboard.'
);
});
test('before mutation, mode is null and goal is the original short text', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_BEFORE);
const modeResult = runGsdTools('roadmap get-phase 1 --pick mode', tmpDir);
const goalResult = runGsdTools('roadmap get-phase 1 --pick goal', tmpDir);
assert.ok(modeResult.success && goalResult.success);
// mode field absent → empty/null per Phase 1 parser contract
assert.ok(modeResult.output.trim() === '' || modeResult.output.trim() === 'null');
assert.strictEqual(goalResult.output.trim(), 'Users can register and log in');
});
test('user story longer than 120 chars (SPIDR trigger boundary)', () => {
// This story is >120 chars — the workflow should have split it via SPIDR
// before writing. This test confirms the parser still handles it correctly
// if the user chose "Reject split" and proceeded with the long story.
const longStory = 'As a registered customer with an active account and verified email, I want to reset my password and update my profile, so that I can recover access.';
const variant = ROADMAP_AFTER_MVP.replace(
'As a new user, I want to register and log in, so that I can access my dashboard.',
longStory
);
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), variant);
const result = runGsdTools('roadmap get-phase 1 --pick goal', tmpDir);
assert.ok(result.success);
assert.strictEqual(result.output.trim(), longStory);
});
});

View File

@@ -0,0 +1,57 @@
/**
* mvp-phase workflow — contract test
* Verifies the workflow markdown contains the four agreed gates:
* 1. Phase existence + status guard (refuse in_progress/completed)
* 2. User-story prompt (three AskUserQuestion calls, As a / I want to / So that)
* 3. SPIDR splitting check
* 4. ROADMAP write (Mode + Goal)
* 5. Delegation to plan-phase
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'mvp-phase.md');
describe('mvp-phase workflow', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('declares phase status guard (refuse in_progress/completed unless --force)', () => {
assert.match(content, /in_progress|completed/i, 'workflow must reference status guard');
assert.match(content, /--force|status\s*guard/i, 'workflow must mention force override or status guard');
});
test('runs three structured user-story prompts', () => {
assert.match(content, /As a/i);
assert.match(content, /I want to/i);
assert.match(content, /[Ss]o that/);
// The three prompts should each use AskUserQuestion (or vscode_askquestions for Copilot)
const askCount = (content.match(/AskUserQuestion|vscode_askquestions/g) || []).length;
assert.ok(askCount >= 3, `workflow must invoke AskUserQuestion at least 3 times for the story prompts (got ${askCount})`);
});
test('runs SPIDR splitting check after user story', () => {
assert.match(content, /SPIDR|spidr-splitting/i);
assert.match(content, /spidr-splitting\.md/, 'workflow must reference the SPIDR rules file');
});
test('writes Mode: mvp + Goal: line to ROADMAP.md', () => {
assert.match(content, /\*\*Mode:\*\*\s*mvp/i, 'workflow must specify the **Mode:** mvp line');
assert.match(content, /ROADMAP\.md/);
assert.match(content, /\*\*Goal:\*\*/, 'workflow must update the **Goal:** line');
});
test('delegates to /gsd plan-phase after ROADMAP write', () => {
assert.match(content, /plan-phase/);
// Order: SPIDR ... then plan-phase
const spidrIdx = content.search(/SPIDR|spidr-splitting/i);
const planPhaseIdx = content.search(/\/gsd[\s-]?plan-phase|plan-phase\s+(?:command|workflow|delegation)/i);
assert.ok(spidrIdx > 0 && planPhaseIdx > 0, 'both SPIDR and plan-phase delegation must be present');
assert.ok(planPhaseIdx > spidrIdx, 'plan-phase delegation must come AFTER SPIDR check');
});
test('references user-story-template.md', () => {
assert.match(content, /user-story-template\.md/);
});
});

View File

@@ -0,0 +1,39 @@
/**
* new-project workflow — MVP mode prompt contract test
* Verifies the workflow markdown documents the Vertical MVP / Horizontal Layers
* prompt and the ROADMAP.md template branch under MVP mode.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'new-project.md');
describe('new-project — MVP mode prompt', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('workflow includes Vertical MVP option in mode prompt', () => {
assert.match(content, /Vertical\s*MVP/i, 'must mention Vertical MVP option');
});
test('workflow includes Horizontal Layers option in mode prompt', () => {
assert.match(content, /Horizontal\s*Layers/i, 'must mention Horizontal Layers option');
});
test('ROADMAP template emits **Mode:** mvp under Vertical MVP path', () => {
assert.match(
content,
/\*\*Mode:\*\*\s*mvp/,
'must emit **Mode:** mvp on initial roadmap phases under Vertical MVP'
);
});
test('workflow falls back to standard template when Horizontal Layers picked', () => {
assert.match(
content,
/Horizontal[^\n]*standard|standard[^\n]*Horizontal|no.*Mode.*line/i,
'must specify fallback to standard template'
);
});
});

View File

@@ -0,0 +1,75 @@
/**
* plan-phase workflow — --mvp flag parsing and MVP_MODE resolution
* Contract test: verifies the workflow markdown documents the agreed
* resolution order (CLI flag → roadmap mode → config → default false).
*/
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md');
describe('plan-phase workflow — --mvp flag', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('argument list documents --mvp flag', () => {
const argsLine = content.match(/Extract from \$ARGUMENTS:[^\n]*/);
assert.ok(argsLine, 'Step 2 arg-extraction line not found');
assert.match(argsLine[0], /--mvp/, 'argument list must mention --mvp');
});
test('workflow defines MVP_MODE resolution block', () => {
assert.match(content, /MVP_MODE/, 'workflow must declare MVP_MODE');
assert.match(content, /workflow\.mvp_mode/, 'must read workflow.mvp_mode config');
assert.match(
content,
/roadmap[^\n]*mode|phase[^\n]*\.mode/i,
'must consult phase mode from roadmap'
);
});
test('Walking Skeleton gate references new-project + Phase 1', () => {
assert.match(content, /SKELETON\.md/, 'workflow must mention SKELETON.md');
assert.match(
content,
/Walking Skeleton|walking_skeleton/i,
'workflow must label the gate as Walking Skeleton'
);
});
test('planner spawn passes MVP_MODE to gsd-planner', () => {
assert.match(
content,
/MVP_MODE[^\n]*planner|planner[^\n]*MVP_MODE/i,
'workflow must wire MVP_MODE into the planner subagent prompt'
);
});
});
describe('plan-phase --mvp — resolution chain integration', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject(); });
afterEach(() => { cleanup(tmpDir); });
test('roadmap.get-phase reports mode=mvp when set in roadmap', () => {
fs.writeFileSync(
path.join(tmpDir, '.planning', 'ROADMAP.md'),
`# Roadmap\n\n## v1.0.0\n\n### Phase 1: Auth\n**Goal:** Users can log in\n**Mode:** mvp\n`
);
const result = runGsdTools('roadmap get-phase 1 --pick mode', tmpDir);
assert.ok(result.success);
assert.strictEqual(result.output.trim(), 'mvp');
});
test('config-get workflow.mvp_mode default is empty/unset', () => {
const result = runGsdTools('config-get workflow.mvp_mode', tmpDir);
// Either success with empty output OR a non-zero exit; both are fine.
// Real assertion: the key isn't accidentally set to "true" in tmp project.
if (result.success) {
assert.notStrictEqual(result.output.trim(), 'true');
}
});
});

View File

@@ -0,0 +1,66 @@
/**
* gsd-planner agent — MVP-mode branch contract
* Verifies the agent definition contains the MVP-mode planning section,
* conditional reference loading, and Walking Skeleton handling.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT = path.join(__dirname, '..', 'agents', 'gsd-planner.md');
const REF_MVP = path.join(__dirname, '..', 'get-shit-done', 'references', 'planner-mvp-mode.md');
const REF_SKEL = path.join(__dirname, '..', 'get-shit-done', 'references', 'skeleton-template.md');
describe('gsd-planner — MVP-mode branch', () => {
const content = fs.readFileSync(AGENT, 'utf-8');
test('agent defines an MVP Mode Detection section', () => {
assert.match(content, /MVP\s*Mode|MVP_MODE/i, 'must reference MVP mode');
assert.match(content, /vertical[\s-]?slice/i, 'must use vertical-slice terminology');
});
test('agent describes Walking Skeleton handling', () => {
assert.match(content, /Walking\s*Skeleton/i, 'must mention Walking Skeleton');
assert.match(content, /SKELETON\.md/, 'must mention SKELETON.md output');
});
test('agent references planner-mvp-mode.md conditionally', () => {
assert.match(
content,
/references\/planner-mvp-mode\.md/,
'must reference the MVP-mode rules file'
);
});
test('referenced files exist on disk', () => {
assert.ok(fs.existsSync(REF_MVP), `${REF_MVP} must exist`);
assert.ok(fs.existsSync(REF_SKEL), `${REF_SKEL} must exist`);
});
test('agent does not introduce horizontal/MVP mixing language', () => {
// Q1: all-or-nothing per phase. Reject phrasing that would imply mixing.
assert.doesNotMatch(
content,
/mix[a-z\s]*horizontal[a-z\s]*MVP|MVP[a-z\s]*and[a-z\s]*horizontal[a-z\s]*tasks/i,
'agent must enforce all-or-nothing per phase'
);
});
test('agent requires PLAN.md to start with user-story header in MVP mode', () => {
// The MVP Mode Detection section must instruct the planner to emit
// a "## Phase Goal" section with **As a** / **I want to** / **so that**
// bolded keywords as the first content under the phase header in PLAN.md.
assert.match(content, /Phase\s*Goal/i, 'must mention "Phase Goal" header');
assert.match(
content,
/\*\*As a\*\*[^\n]*\*\*I want to\*\*[^\n]*\*\*so that\*\*/i,
'must specify the bolded user-story format for PLAN.md emit'
);
assert.match(
content,
/user-story-template\.md/,
'must reference the user-story-template reference file'
);
});
});

View File

@@ -0,0 +1,43 @@
/**
* progress workflow — MVP mode display contract test
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'progress.md');
describe('progress — MVP mode display', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('workflow declares MVP_MODE branch', () => {
assert.match(content, /MVP_MODE/, 'must declare MVP_MODE');
assert.match(
content,
/roadmap[^\n]*mode|phase[^\n]*\.mode/i,
'must consult phase mode from roadmap'
);
});
test('MVP display sources user-flow status from PLAN.md task names', () => {
assert.match(
content,
/PLAN\.md[^\n]*task|task[^\n]*PLAN\.md/i,
'must source user-flow status from PLAN.md tasks'
);
assert.match(
content,
/user[\s-]?flow|user-visible/i,
'must use user-flow framing'
);
});
test('falls back to standard display when mode null', () => {
assert.match(
content,
/mode[^\n]*null|absent|not.*mvp|standard\s*display/i,
'must specify fallback when mode is not mvp'
);
});
});

View File

@@ -0,0 +1,77 @@
/**
* Roadmap parser — `**Mode:**` field extraction
* Covers PRD: vertical-mvp-slice Phase 1 (Q1: all-or-nothing per phase).
*/
const { test, describe, beforeEach, afterEach } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
const ROADMAP_WITH_MODE = `# Roadmap
## v1.0.0
### Phase 1: User Auth MVP
**Goal:** A user can register and log in
**Mode:** mvp
**Success Criteria**:
1. Registration works
2. Login works
### Phase 2: Bulk Import
**Goal:** Admin can upload CSV
**Success Criteria**:
1. CSV parses
`;
describe('roadmap parser — mode field', () => {
let tmpDir;
beforeEach(() => { tmpDir = createTempProject(); });
afterEach(() => { cleanup(tmpDir); });
test('roadmap.get-phase returns mode="mvp" when **Mode:** mvp present', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_WITH_MODE);
const result = runGsdTools('roadmap get-phase 1', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const out = JSON.parse(result.output);
assert.strictEqual(out.found, true);
assert.strictEqual(out.mode, 'mvp');
});
test('roadmap.get-phase returns mode=null when **Mode:** absent', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_WITH_MODE);
const result = runGsdTools('roadmap get-phase 2', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const out = JSON.parse(result.output);
assert.strictEqual(out.found, true);
assert.strictEqual(out.mode, null);
});
test('roadmap.analyze surfaces mode per phase', () => {
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), ROADMAP_WITH_MODE);
const result = runGsdTools('roadmap analyze', tmpDir);
assert.ok(result.success, `Command failed: ${result.error}`);
const out = JSON.parse(result.output);
const p1 = out.phases.find(p => p.number === '1');
const p2 = out.phases.find(p => p.number === '2');
assert.strictEqual(p1.mode, 'mvp');
assert.strictEqual(p2.mode, null);
});
test('mode field is case-insensitive and trimmed', () => {
const variant = ROADMAP_WITH_MODE.replace('**Mode:** mvp', '**mode**: MVP ');
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), variant);
const result = runGsdTools('roadmap get-phase 1', tmpDir);
const out = JSON.parse(result.output);
assert.strictEqual(out.mode, 'mvp');
});
test('unrecognized mode value is preserved verbatim (forward-compat)', () => {
const variant = ROADMAP_WITH_MODE.replace('**Mode:** mvp', '**Mode:** experimental');
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), variant);
const result = runGsdTools('roadmap get-phase 1', tmpDir);
const out = JSON.parse(result.output);
assert.strictEqual(out.mode, 'experimental');
});
});

View File

@@ -0,0 +1,26 @@
/**
* stats workflow — MVP mode summary contract test
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'stats.md');
describe('stats — MVP mode summary', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('workflow includes MVP phase count summary', () => {
assert.match(content, /MVP/, 'must mention MVP in summary');
assert.match(content, /mode/i, 'must reference mode field');
});
test('uses roadmap.analyze to count MVP phases', () => {
assert.match(
content,
/roadmap[^\n]*analyze|analyze[^\n]*mode/i,
'must consult roadmap.analyze (which surfaces mode per phase from Phase 1)'
);
});
});

View File

@@ -0,0 +1,32 @@
/**
* gsd-verifier agent — MVP Mode Verification section contract
* Verifies the agent definition contains a section instructing the verifier
* to emphasize user-visible outcomes under MVP mode.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const AGENT = path.join(__dirname, '..', 'agents', 'gsd-verifier.md');
const REF = path.join(__dirname, '..', 'get-shit-done', 'references', 'verify-mvp-mode.md');
describe('gsd-verifier — MVP Mode Verification section', () => {
const content = fs.readFileSync(AGENT, 'utf-8');
test('agent defines an MVP Mode Verification section', () => {
assert.match(content, /MVP\s*Mode\s*Verification|MVP[\s-]?mode[\s-]?verif/i);
});
test('agent references verify-mvp-mode.md', () => {
assert.match(content, /verify-mvp-mode\.md/);
});
test('agent preserves goal-backward terminology', () => {
assert.match(content, /goal[\s-]?backward/i);
});
test('referenced file exists on disk', () => {
assert.ok(fs.existsSync(REF));
});
});

View File

@@ -0,0 +1,53 @@
/**
* verify-work workflow — MVP mode UAT contract test
* Verifies the workflow markdown documents MVP_MODE resolution,
* conditional reference injection, user-flow-first UAT ordering,
* and the deferred-technical-checks clause.
*/
const { test, describe } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('fs');
const path = require('path');
const WORKFLOW = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'verify-work.md');
describe('verify-work — MVP mode UAT framing', () => {
const content = fs.readFileSync(WORKFLOW, 'utf-8');
test('Step 1 resolves MVP_MODE from phase mode field', () => {
assert.match(content, /MVP_MODE/, 'workflow must declare MVP_MODE');
assert.match(
content,
/roadmap[^\n]*mode|phase[^\n]*\.mode|\.mode\s*=/i,
'must consult phase mode from roadmap'
);
});
test('workflow references verify-mvp-mode.md', () => {
assert.match(content, /verify-mvp-mode\.md/, 'must reference the UAT framing file');
});
test('UAT generation under MVP mode runs user-flow steps first', () => {
assert.match(
content,
/user[\s-]?flow[^\n]{0,80}(first|before|precede)/i,
'must specify user-flow-first ordering'
);
});
test('technical checks deferred under MVP mode', () => {
assert.match(
content,
/technical[\s-]?checks[^\n]{0,80}(after|defer|second)/i,
'must defer technical checks under MVP mode'
);
});
test('mode null falls back to standard UAT generation', () => {
assert.match(
content,
/mode[^\n]*null|absent|not.*mvp|standard\s*UAT/i,
'must specify fallback when mode is not mvp'
);
});
});