* 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). * 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). * 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. * 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. * 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. * 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. * 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. * docs(changelog): announce --mvp vertical-slice planning (#2826) * 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. * 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. * 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. * fix(mvp-phase): trim description to fit 100-char budget * 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. * 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. * 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. * 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. * docs(changelog): announce /gsd mvp-phase command (#2826) * fix(mvp-phase): add TEXT_MODE plain-text fallback for non-Claude runtimes (#2012) * 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. * 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). * 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. * 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. * 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>). * docs(changelog): announce MVP+TDD runtime gate in execute-phase (#2826) * 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. * 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). * 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. * docs(changelog): announce MVP-mode UAT framing in verify-work (#2826) * 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). * 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). * 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. * 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). * docs(changelog): announce Phase 4 discovery & progress (#2826) * 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. * 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 * 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. * 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. * docs(mvp): canary-prep concept cleanup — CONTEXT.md, mvp-concepts index, --prd interaction (#3176) * chore(mvp): concept cleanup + cross-ref index for v1.50.0-canary.2 prep - CONTEXT.md gains 7 MVP domain terms (MVP Mode, User Story, Walking Skeleton, Vertical Slice, Behavior-Adding Task, MVP+TDD Gate, SPIDR Splitting) so the project glossary matches the shipped surface. - New get-shit-done/references/mvp-concepts.md indexes the six MVP reference files and concept-to-file map so agents and contributors can find the right canonical doc without grepping. - plan-phase.md Walking Skeleton block now documents that --mvp and --prd compose orthogonally on Phase 1; no precedence needed. - INVENTORY/INVENTORY-MANIFEST refreshed for the new reference (58 -> 59). No behavior change. Canary-prep cleanup ahead of v1.50.0-canary.2. Surfaced for follow-up (not in this PR): - MVP_MODE resolution shell block duplicated across plan-phase, execute-phase, verify-work workflows (needs a shared workflow-include mechanism; structural change). - Behavior-Adding Task predicate is prose-only; no shared utility. - User Story regex hardcoded in verify-work; would benefit from a central definition consumed by the verifier and the mvp-phase command. * chore(changeset): set PR number for mvp concept cleanup * feat(mvp): centralize resolution surfaces + fix SDK roadmap mode parity (#3178) Three new SDK query verbs replace the architectural duplication surfaced by the v1.50.0-canary.2 review against dev tip 12c4e565: phase.mvp-mode <N> [--cli-flag] Single canonical precedence resolver (CLI flag -> ROADMAP **Mode:** mvp -> workflow.mvp_mode config -> false). Replaces 4-8 lines of bash that were duplicated across plan-phase.md, execute-phase.md, verify-work.md, and progress.md. Returns {active, source, roadmap_mode, config_mvp_mode, cli_flag_present}. task.is-behavior-adding <plan-file> | --task-content <xml> Behavior-Adding Task predicate (tdd="true" + <behavior> block + non-test source files in <files>). Replaces prose-only specification in references/execute-mvp-tdd.md; gsd-executor agent now invokes the verb instead of re-inlining the three checks. Returns {is_behavior_adding, checks, reason}. user-story.validate <text> | --story <text> Owns the canonical User Story regex /^As a .+, I want to .+, so that .+\.$/ previously hardcoded in verify-work.md prose. Consumed by gsd-verifier (phase-goal guard) and /gsd-mvp-phase (interactive-prompt validation). Returns {valid, slots: {role, capability, outcome}, errors[]}. Bug fix bundled: sdk/src/query/roadmap.ts searchPhaseInContent now extracts the mode field from **Mode:**, restoring parity with roadmap.cjs:120-123. Without this, roadmap.get-phase --pick mode returned null on the native dispatch path even when the phase had **Mode:** mvp set, causing MVP_MODE to silently fall through to the config/false branch in every consuming workflow. The original PRs Phase 1 (#2885) shipped the CJS parser but the SDK port omitted the field; this fix brings them back to parity. Workflows + agents updated to call the verbs: - plan-phase.md, execute-phase.md, verify-work.md, progress.md call phase.mvp-mode (one line replaces the duplicated bash chains). - execute-phase.md MVP+TDD gate calls task.is-behavior-adding. - verify-work.md goal guard calls user-story.validate. - mvp-phase.md interactive prompt validates via user-story.validate. - gsd-executor agent references task.is-behavior-adding instead of prose. - gsd-verifier agent references user-story.validate instead of inlined regex. Tests: 24 new vitest tests in sdk/src/query/mvp.test.ts cover all three verbs + the regression. Two existing contract tests (progress, verify) updated to assert on the new verb shape. All 60 existing MVP contract tests pass; golden integration suite (38 + 42 tests) passes. Closes #3177 * fix(canary.2): unblock release gates for v1.50.0-canary.2 Run 25451329660 (Release SDK Bundle on dev, 2026-05-06T17:41) failed at the test-suite step with 3 deterministic content/structure gate failures, all attributable to the MVP umbrella integration in #3178 and the docs sweep in #3180. Failure 1: /gsd-mvp-phase undocumented in workflows/help.md - tests/bug-2954-help-md-slash-command-stubs.test.cjs requires every shipped commands/gsd/<X>.md to have a /gsd-<X> mention in help.md - PR #3180 updated docs/COMMANDS.md but missed help.md (which the AI agents load in-product) - Fix: add a /gsd-mvp-phase entry to help.md right before /gsd-plan-phase Failures 2 + 3: execute-phase.md (1727) and plan-phase.md (1714) over XL budget (1700) - PR #3178 added MVP-mode verb calls (phase.mvp-mode, task.is-behavior-adding, user-story.validate) to both workflow files, pushing them past 1700 lines - Fix: bump XL_BUDGET 1700 -> 1800 with inline comment pointing at the structural follow-up (extract MVP bodies to <workflow>/modes/mvp.md per the discuss-phase/modes/ precedent) - The structural extract is the right long-term fix but is bigger than canary unblock scope; will land in a follow-up after canary cycles Local verification: $ node --test tests/bug-2954-help-md-slash-command-stubs.test.cjs tests/workflow-size-budget.test.cjs tests 111 pass 111 fail 0 After this lands, re-trigger Release SDK Bundle on dev for v1.50.0-canary.2. * chore(changeset): set PR number for canary.2 unblock * fix(codex): generate-claude-md writes to AGENTS.md on Codex runtime When config.runtime === 'codex' or GSD_RUNTIME=codex, override the output target to AGENTS.md regardless of claude_md_path, so Codex projects no longer have GSD sections written to CLAUDE.md by mistake. Fixes both the CJS (gsd-tools) and SDK (profile-output.ts) paths. Explicit --output flags are still honoured in both paths. Closes #3163 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(plan-phase): remove agent: directive that caused OpenCode subagent dispatch On OpenCode, any command with `agent: <name>` in its frontmatter is auto-dispatched to a subagent context where the Agent tool is unavailable. plan-phase.md and mvp-phase.md both carried `agent: gsd-planner`, causing them to run inside gsd-planner's subagent context with no ability to spawn researcher/planner/checker subagents — the orchestrator fell back to inline execution for all three phases. Fix: remove `agent: gsd-planner` from both command files so they run in the main agent context. Also replace the stale `Task` tool in allowed-tools with `Agent` (the correct dispatcher tool name post-#3168 rename). Adds a structural regression test that parses YAML frontmatter of every commands/gsd/*.md file and asserts no command carries an `agent:` directive. Closes #3156 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(mvp): address CodeRabbit workflow and contract findings * fix(execute-phase): use registered state.update query command --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
18 KiB
Context
Domain terms
Dispatch Policy Module
Module owning dispatch error mapping, fallback policy, timeout classification, and CLI exit mapping contract.
Canonical error kind set:
unknown_commandnative_failurenative_timeoutfallback_failurevalidation_errorinternal_error
Command Definition Module
Canonical command metadata Interface powering alias, catalog, and semantics generation.
Query Runtime Context Module
Module owning query-time context resolution for projectDir and ws, including precedence and validation policy used by query adapters.
Native Dispatch Adapter Module
Adapter Module that satisfies native query dispatch at the Dispatch Policy seam, so policy modules consume a focused dispatch Interface instead of closure-wired call sites.
Query CLI Output Module
Module owning projection from dispatch results/errors to CLI { exitCode, stdoutChunks, stderrLines } output contract.
Query Execution Policy Module
Module owning query transport routing policy projection (preferNative, fallback policy, workstream subprocess forcing) at execution seam.
Query Subprocess Adapter Module
Adapter Module owning subprocess execution contract for query commands (JSON/raw invocation, @file: indirection parsing, timeout/exit error projection).
Query Command Resolution Module
Canonical command normalization and resolution Interface (query-command-resolution-strategy) used by internal query/transport paths after dead-wrapper convergence.
Command Topology Module
Module owning command resolution, policy projection (mutation, output_mode), unknown-command diagnosis, and handler Adapter binding at one seam for query dispatch.
Query Pre-Project Config Policy Module
Module policy that defines query-time behavior when .planning/config.json is absent: use built-in defaults for parity-sensitive query Interfaces, and emit parity-aligned empty model ids for pre-project model resolution surfaces.
MVP Mode
Phase-level planning mode that frames work as a vertical slice (UI → API → DB) of one user-visible capability instead of horizontal layers. Resolved at workflow init via the precedence chain: --mvp CLI flag → ROADMAP.md **Mode:** mvp field → workflow.mvp_mode config → false. All-or-nothing per phase (PRD #2826 Q1). Surfaced as MVP_MODE=true|false to the planner, executor, verifier, and discovery surfaces (progress, stats, graphify). Canonical parser: roadmap.cjs **Mode:** field; canonical resolution chain documented in workflows/plan-phase.md. Concept index: references/mvp-concepts.md.
User Story
Phase-goal format under MVP Mode: As a [role], I want to [capability], so that [outcome]. Required regex shape: /^As a .+, I want to .+, so that .+\.$/. Used as the framing input by gsd-planner (emits as bolded ## Phase Goal header in PLAN.md) and as the verification target by gsd-verifier (the [outcome] clause is the goal-backward verification anchor). Authored interactively by /gsd-mvp-phase, validated by SPIDR Splitting when too large.
Walking Skeleton
Phase 1 deliverable under --mvp on a new project: the thinnest end-to-end stack proving every layer (framework, DB, routing, deployment) works together. Emitted as SKELETON.md capturing the architectural decisions subsequent vertical slices inherit. Gate fires when phase_number == "01" AND prior_summaries == 0 AND MVP_MODE=true. Scope intentionally narrow (PRD #2826 Q2) — does not retrofit existing projects.
Vertical Slice
Single-feature task that moves one user capability from open-to-close (happy path) end-to-end. Contrast with the horizontal layer (all models, then all APIs, then all UI). The MVP Mode planning unit; SPIDR Splitting axes (Spike, Paths, Interfaces, Data, Rules) are the canonical decomposition tools when a slice is too large for one phase.
Behavior-Adding Task
Predicate over a PLAN.md task: tdd="true" frontmatter AND <behavior> block names a user-visible outcome AND <files> includes at least one non-*.md / non-*.json / non-*.test.* source file. Pure doc/config/test-only tasks are exempt. The MVP+TDD Gate (in references/execute-mvp-tdd.md) only halts execution on this predicate; the gsd-executor agent applies all three checks at runtime. Currently a prose-only specification — no shared utility.
MVP+TDD Gate
Per-task runtime gate in /gsd-execute-phase that, when both MVP_MODE and TDD_MODE are true, refuses to advance a Behavior-Adding Task until a failing-test commit (test({phase}-{plan})) exists for it. The tdd_review_checkpoint end-of-phase review escalates from advisory to blocking under the same condition. Documented contract: references/execute-mvp-tdd.md. Reserved escape hatch --force-mvp-gate is documented but not implemented.
SPIDR Splitting
Five-axis story decomposition discipline (Spike, Paths, Interfaces, Data, Rules) used by /gsd-mvp-phase when a User Story is too large for one phase. Full interactive flow per PRD #2826 Q3 (not a lightweight filter). Reference: get-shit-done/references/spidr-splitting.md.
Recurring PR mistakes (distilled from CodeRabbit reviews, 2026-05-05)
Tests — no source-grep
- Rule: never bind
readFileSyncresult to a var then call.includes()/.match()/.startsWith()on it. CI runsscripts/lint-no-source-grep.cjsand exits 1. - Escape: add
// allow-test-rule: <reason>anywhere in the file to exempt the whole file. Use when reading product markdown or runtime output (not.cjssource). - Pattern to reach for instead: call the exported function, capture stdout/JSON, assert on typed fields.
Tests — no unescaped RegExp interpolation
new RegExp(\prefix${someVar}`)— ifsomeVarcan contain.or other metacharacters (e.g. phase id5.1), the pattern is wrong. AlwaysescapeRegex(someVar). TheescapeRegexutility is incore.cjs` and already imported in most modules.
Tests — no dead regex branches in .includes()
src.includes('foo.*bar')is always false —.*is a regex metacharacter, not a wildcard inincludes. Either usenew RegExp('foo.*bar').test(src)or delete the branch.
Tests — guard top-level readFileSync against ENOENT
- Module-level
const src = fs.readFileSync(...)throws before anytest()registers, aborting the runner with an unhandled exception instead of a named failure. Wrap in try/catch and rethrow with a helpful message.
Changesets — pr: field must be the PR number, not the issue number
- The
pr:key in.changeset/*.mdfrontmatter must reference the PR introducing the fix (e.g.3142), not the issue it closes (e.g.3120). Changelog tooling links to GitHub PRs by this value.
Shell hooks — never interpolate $VAR into single-quoted JS strings
node -e "require('$HOOK_DIR/lib/foo.js')"breaks silently if$HOOK_DIRcontains a single quote (POSIX-legal). Pass paths via env vars:GIT_CMD_LIB="$HOOK_DIR/lib/foo.js" node -e "require(process.env.GIT_CMD_LIB)".
Shell guards — [ -f .git ] does not detect worktrees from main repo
- In the main repo
.gitis a directory, so[ -f .git ]is false and the entire guard is skipped. Usegit rev-parse --git-dirand match*.git/worktrees/*in acasestatement instead.
Shell guards — absolute-path containment must use root/ prefix, not glob
[[ "$PATH" != "$ROOT"* ]]matches sibling prefixes (/repo-extrapasses whenROOT=/repo). Use[[ "$P" != "$ROOT" && "$P" != "$ROOT/"* ]]. Also: check[ -z "$ROOT" ]and exit 1 before the containment test. Warn → fail-closed for security-relevant path checks.
Docs — keep internal reference counts consistent
- When a heading says
(N shipped)and a footnote saysN-1 top-level references, update the footnote. CodeRabbit catches this every time.
Workflow learnings (distilled from triage + PR cycle, 2026-05-05)
Skill consolidation gap class — missing workflow files
- When a command absorbs a micro-skill as a flag (e.g.
capture --backlog), the old command's process steps must be ported to aget-shit-done/workflows/<name>.mdfile. The routing wrapper incommands/gsd/*.mddeclares anexecution_context@-reference to that workflow — if the file doesn't exist the agent loads nothing and has no steps to follow. - Detection:
tests/bug-3135-capture-backlog-workflow.test.cjsadds a broad regression — everyexecution_context@-reference in anycommands/gsd/*.mdmust resolve to an existing file on disk. This test will catch all future gaps of this class immediately. - Prior art:
reapply-patches.mdwas the first gap found and fixed in PR #2824 itself.add-backlog.mdwas missed in the same PR and caught later in #3135. Run the regression test after every consolidation PR.
CodeRabbit thread resolution — stale threads after allow-test-rule fixes
- After adding
// allow-test-rule:to silence lint, CodeRabbit's existing inline threads remain open even though the acknowledged fix is in place. Resolve them viaresolveReviewThreadGraphQL mutation before merging — open threads block clean merge history and mislead future reviewers. - Pattern:
gh api graphql -f query='mutation { resolveReviewThread(input:{threadId:"PRRT_..."}) { thread { isResolved } } }'
PR discipline — split unrelated changes into separate PRs
- A bug fix and a docs rewrite committed to the same branch produce a noisy diff and a PR that reviewers can't cleanly approve. Cherry-pick doc changes to a dedicated branch (
docs/) immediately, then force-push the original branch to remove the commit. One concern per PR.
INVENTORY.md must be updated alongside every workflow file addition/removal
docs/INVENTORY.mdtracks the shipped workflow count (## Workflows (N shipped)) and has one row per file. Adding or removing a workflow without updating INVENTORY produces an internally inconsistent doc.- Also update
docs/INVENTORY-MANIFEST.json— it is the machine-readable manifest and must stay in sync with the filesystem. - When a flag absorbs a micro-skill, the old skill's
Invoked byattribution in INVENTORY must move to the new parent (e.g.add-todo.mdincorrectly claimed/gsd-capture --backloguntil #3135 corrected it).
README — keep root README as storyline only; all detail lives in docs/
- Root
README.mdshould be ≤300 lines: hero, author note, 6-step loop, install, core command table, why-it-works bullets, config key dials, docs index, minimal troubleshooting. - Every removed detail section needs a link to the canonical doc that covers it. All doc links must resolve before committing.
- Markdownlint rules to watch: MD001 (heading level skip — don't use
###directly inside admonitions; use bold instead), MD040 (fenced code blocks must declare a language identifier).
Issue triage — always check for existing work before filing as new
- Before writing an agent brief for a confirmed bug, check: (1) local branches (
git branch -a | grep <issue>), (2) untracked/modified files on that branch, (3) stash, (4) open PRs with matching head branch. A crash may have left work 90% done — recover and commit rather than re-implementing.
SDK-only verbs — golden-policy exemption required
- Any
gsd-sdk queryverb implemented only in the SDK native registry (nogsd-tools.cjsmirror) must be added toNO_CJS_SUBPROCESS_REASONinsdk/src/golden/golden-policy.ts. Without this entry the golden-policy test fails, treating the verb as a missing implementation rather than an intentional SDK-only path.
Recurring findings from ADR-0002 PR review (2026-05-05)
allowed-tools must include every tool the workflow uses
When a command delegates to a workflow via execution_context, the command's allowed-tools must cover every tool the workflow calls — including Write for file creation. The thin wrapper pattern makes this easy to miss: the process steps live in the workflow, but the tool grant lives in the command frontmatter. Missing a tool silently fails at runtime.
User-supplied slug/path args always need sanitization before file path construction
Any workflow step that takes user input (subcommand argument, $ARGUMENTS, or parsed remainder) and constructs a .planning/…/{SLUG}.md path must sanitize first: strip non-[a-z0-9-] chars, reject ..///\, enforce max length. Document the sanitization inline at the step, not just in <security_notes>. Steps that say "(already sanitized)" must trace back to an explicit sanitization guard — not just a preceding describe block.
RESUME/fallback modes bypass sanitization guards written for primary modes
CLOSE and STATUS modes that document "(already sanitized)" do not automatically cover RESUME or default modes. Each mode that constructs a file path from user input needs its own guard — don't assume sibling modes share state.
Shared helpers prevent lint/test disagreement
When a lint script and a test suite both implement the same constant (CANONICAL_TOOLS) or parser (parseFrontmatter, executionContextRefs), they will silently diverge. Extract to a scripts/*-helpers.cjs module required by both. A tool added to the lint's allowlist but not the test's (or vice versa) causes one layer to pass while the other fails.
readFileSync outside test() crashes the runner before any test registers
Module-level or suite-registration-time readFileSync throws as an unhandled exception if the file is absent, aborting the runner with no test output. Move reads inside test() callbacks so failures surface as named test failures.
Global regex with g flag carries lastIndex state between calls
A const RE = /pattern/g shared across functions retains lastIndex after .test() or .exec(). Use a non-global pattern for boolean checks (/pattern/.test(s)) and create a new RegExp(pattern, 'g') per iteration when you need exec() loops. Forgetting lastIndex = 0 resets causes intermittent false negatives.
ADR files need Status + Date headers
Every docs/adr/NNNN-*.md file must open with - **Status:** Accepted (or Proposed/Deprecated) and - **Date:** YYYY-MM-DD immediately after the title. Without them the ADR is undatable and untriageable when the list grows.
Step names in workflow XML must use hyphens, not underscores
All workflow file names use hyphens; <step name="..."> attributes inside those files must match: extract-learnings not extract_learnings. Tests asserting content.includes('<step name=') should tighten to the exact hyphenated name so renames are caught.
INVENTORY-MANIFEST.json has two workflow lists — only families.workflows is canonical
docs/INVENTORY-MANIFEST.json has families.workflows (canonical, read by tooling) and a stale top-level workflows key (introduced by a node update script that wrote to the wrong key). Always update families.workflows. Delete any top-level workflows key if it appears.
"Follow the X workflow" prose fragments are non-standard — use "Execute end-to-end."
After stripping prose @-refs, some command <process> blocks retained bolded "Follow the X workflow" fragments. ADR-0002 standard is Execute end-to-end. for single-workflow commands. Routing commands with flag dispatch use execute the X workflow end-to-end. in routing bullets (no bold, no redundant path).
Recurring CodeRabbit review patterns (2026-05-05, PRs #3152/#3154/#3155)
Changeset metadata drift (pr: points at issue instead of PR)
- In
.changeset/*.md, reviewers repeatedly flagpr:values that accidentally reference issue ids. - Rule:
pr:must equal the GitHub PR number carrying the change. - Pre-flight check: before push, verify each new changeset file against current branch PR number.
Test diagnostics quality for command-output parsing
- Even when behavior is correct, CR requests clearer failure surfaces before
.map()on parsed output. - Rule: after
JSON.parse, assert output object shape (e.g.,Array.isArray(output.phases)) with raw-output-prefix diagnostics. - This prevents opaque
TypeErrorfailures and shortens triage loops when CLI output shape changes.
Merge gate discipline: CodeRabbit pass is necessary but not sufficient
- CI/checks can be green while unresolved review threads still block clean merge policy.
- Rule: always gate on all three together: required checks green, CodeRabbit pass, unresolved thread count = 0.
- Keep using GraphQL
reviewThreadsas authoritative unresolved state, not summary comments/check badge alone.
SDK Runtime Bridge review synthesis (PR #3158, 2026-05-05)
What we fixed
- Deepened one SDK Runtime Bridge Module seam (
sdk/src/query-runtime-bridge.ts) for dispatch routing and observability. - Replaced orphan event typing with a canonical union (
RuntimeBridgeEvent). - Made bridge observability non-intrusive:
onDispatchEventnow runs behind a safe emitter so callback failures cannot alter dispatch outcomes. - Corrected strict-mode event semantics: strict native-adapter rejection now reports
dispatchMode: 'native'(no fake subprocess attempt). - Preserved execution policy defaults by passing
allowFallbackToSubprocessthrough asundefinedwhen unset (no forced override inGSDTools). - Fixed transport decision ordering: fallback-disabled guard now throws before emitting subprocess decision events.
- Added explicit invariant in
subprocessReasonfor impossible states (fail loud on contract drift). - Updated user-facing docs (
README.md,docs/CLI-TOOLS.md,docs/ARCHITECTURE.md) and ADR narrative consistency.
What we should not do again
- Do not let observability callbacks sit on the critical path without isolation.
- Do not emit structured events that claim a transport mode that never happened.
- Do not force option defaults at call sites when policy Modules already define defaults.
- Do not keep duplicate/inert exported types; expose one canonical union Interface.
- Do not emit decision events before guard checks that may reject the path.
- Do not leave architectural docs with ambiguous seam ownership between CLI and SDK paths.