Files
msd-core/gsd-core/references/planning-config.md
Tom Boucher 9a69a86f42 enhance(#2971): strict planning filter mode for /gsd-pr-branch (#3720)
* test(#2971): failing-first suite for the pr-branch planning-path filter

Binds the not-yet-built planning.pr_strict mode and the corrected filter recipe
for /gsd-pr-branch across six layers: pure classification and forbidden-path
predicates, real-git fixtures that run the cherry-pick filter loop end to end,
config-key registration through the real CLI and both manifests, the executed
worktree-materialization claim the issue's triage asked to establish, fast-check
properties over arbitrary path sets, and a drift guard over the shipped workflow.

Two live defects in today's shipped recipe are pinned as regressions, both
reproduced empirically first: `git rm -r --cached` stages a deletion of any
.planning/ path the target branch already tracks, so the generated PR removes the
base branch's planning files; and the same command leaves the cherry-picked file
untracked on disk, so a second commit touching that path aborts the pick with
"untracked working tree files would be overwritten" and every remaining commit is
silently dropped.

The test helper parses the canonical path lists out of gsd-core/workflows/pr-branch.md
rather than restating them, so the workflow stays the single source of truth and the
suite cannot drift from what ships.

Refs #2971

* feat(#2971): strict planning filter mode for /gsd-pr-branch

Adds planning.pr_strict — a boolean, default false, that selects what
/gsd-pr-branch means by "filtered". Default mode is unchanged: structural
planning state survives into the PR branch and the nine transient
subdirectories do not. Strict mode drops every .planning/ path, structural
files included, and carries a commit over only when it touches at least one
file outside .planning/.

Strict mode is what makes planning.commit_docs: true safe for a project that
versions its planning tree locally but publishes none of it. The alternative
posture, commit_docs: false, silently costs parallel executor isolation — a
worktree is checked out from a commit, so an untracked or ignored .planning/
is simply absent inside it and the executor has no PLAN.md to read. That claim
is now established by an executed fixture rather than inherited.

The two path lists are declared once and both projections derived from them,
so create_pr_branch and verify can no longer disagree about what the filter
promised. verify previously counted every .planning/ path against a documented
success criterion of zero while create_pr_branch was specified to preserve five
structural files, so a correct run reported itself as failed on every phase
that touched STATE.md — which is every phase. It now asserts against the active
mode, and names the .planning/ paths default mode deliberately keeps rather
than trading a wrong signal for silence.

Two verified defects in the same recipe are fixed alongside, because strict
mode would have amplified both. `git rm -r --cached` staged a deletion for any
.planning/ path the target branch already tracked, so the generated PR removed
the base branch's planning files — under strict mode that would have been the
entire tree. The same command left the picked file untracked on disk, so a
second commit touching that path aborted the cherry-pick with "untracked
working tree files would be overwritten" and every remaining commit was
silently dropped. Both were reproduced against real git before being fixed.
The filter now forces excluded paths back to what the PR branch's HEAD carries,
in the index and the working tree; a conflict outside the filter halts instead
of being improvised past; a commit left empty by filtering is skipped rather
than failing. A clean-working-tree precondition makes the worktree half safe.

Closes #2971

* fix(#2971): unwind the checkout on a conflict halt, and test the real recipe

Two review findings, both fixed in place.

The isolated adversarial pass found that the conflict-outside-the-filter branch
exited while leaving the user checked out on the half-built PR branch with
cherry-pick state still live — this loop runs in the user's own working
directory, so stranding them there is a real cost even though it is not a
vulnerability. The branch now aborts the pick, returns to the original branch,
removes the partial PR branch, and says so before exiting.

The standards pass found the L2 fixtures executed a hand-written mirror of the
cherry-pick filter recipe rather than the recipe itself, so a reordering in the
workflow would not have been caught — and the order is load-bearing, since
restoring a path from HEAD before removing it inverts the filter. The helper now
extracts the canonical loop from the shipped workflow and the fixtures execute
that verbatim, which also gives the conflict-halt unwind above real coverage.
The drift guard additionally pins the two commands' relative order and asserts
the workflow carries exactly one canonical loop.

Also records the publication gate in the CONTEXT.md glossary next to the commit
gate it is distinct from.

Refs #2971

* fix(#2971): make the conflict-halt unwind actually unwind, and use the colon slash form

The remote matrix caught two defects in the previous commit.

The halt path claimed to restore the original branch but did not. `git
cherry-pick --abort` does not apply to a single `--no-commit` pick with no
sequencer file, and the fallback left the unmerged index in place, which makes
`git checkout` refuse — a failure the `2>/dev/null || true` then swallowed, so
the user was told they had been restored while still sitting on the half-built
PR branch. The unwind now drops sequencer state, hard-resets the disposable PR
branch to clear the unmerged index, and only claims a restore when the checkout
actually succeeded; when it does not, it says where the user is and gives them
the two commands to finish it by hand. Verified against real git: exit 1, the
conflict named, HEAD back on the original branch, the partial branch gone, a
clean tree and no CHERRY_PICK_HEAD.

Two runtime-loaded source artifacts used the retired `/gsd-<cmd>` hyphen form,
which names a command no runtime registers. The canonical authoring token for
workflows and references is `/gsd:<cmd>`; docs keep the hyphen form, so the
documentation added in this branch is unaffected. The comment in src/config.cts
moves to the colon form too, since it propagates into the generated lib.

Refs #2971

* docs(#2971): backfill PR number into the changeset fragments (#3720)

---------

Co-authored-by: sim <sim@local>
2026-08-20 15:07:40 -04:00

32 KiB
Raw Blame History

<planning_config>

Configuration options for .planning/ directory behavior.

<config_schema>

"planning": {
  "commit_docs": true,
  "pr_strict": false,
  "search_gitignored": false
},
"git": {
  "branching_strategy": "none",
  "base_branch": null,
  "phase_branch_template": "gsd/phase-{phase}-{slug}",
  "milestone_branch_template": "gsd/{milestone}-{slug}",
  "quick_branch_template": null
},
"manager": {
  "flags": {
    "discuss": "",
    "plan": "",
    "execute": ""
  }
}
Option Default Description
commit_docs true Whether to commit planning artifacts to git
pr_strict false Filter mode for /gsd:pr-branch. false keeps structural planning state (STATE.md, ROADMAP.md, MILESTONES.md, PROJECT.md, REQUIREMENTS.md, milestones/) in the PR branch; true drops every .planning/ path
search_gitignored false Add --no-ignore to broad rg searches
git.branching_strategy "none" Git branching approach: "none", "phase", or "milestone"
git.base_branch null (auto-detect) Target branch for PRs and merges (e.g. "master", "develop"). When null, auto-detects from git symbolic-ref refs/remotes/origin/HEAD, falling back to "main".
git.create_tag true Create git tags on milestone completion
git.phase_branch_template "gsd/phase-{phase}-{slug}" Branch template for phase strategy
git.milestone_branch_template "gsd/{milestone}-{slug}" Branch template for milestone strategy
git.quick_branch_template null Optional branch template for quick-task runs
workflow.use_worktrees true Whether executor agents run in isolated git worktrees. Set to false to disable worktrees — agents execute sequentially on the main working tree instead. Recommended for solo developers or when worktree merges cause issues. Note: if your branch is ahead of origin/HEAD (a diverged milestone or feature branch), GSD auto-degrades to sequential and prints a warning; set worktree.baseRef:"head" in .claude/settings.local.json to restore parallel execution. See the branch-divergence note below.
workflow.subagent_timeout 300000 Timeout in milliseconds for parallel subagent tasks (e.g. codebase mapping). Increase for large codebases or slower models. Default: 300000 (5 minutes).
workflow.test_command null Custom shell command run as the regression/test gate by execute-phase, audit-fix, and post-merge-gate. When unset, GSD auto-detects (Makefile / package.json / Cargo.toml / go.mod / pyproject.toml). Example: npm test.
workflow.build_command null Custom shell command run as the build gate by the post-merge gate. When unset, the build step is skipped/auto-detected. Example: npm run build.
workflow.inline_plan_threshold 2 Plans with this many tasks or fewer execute inline (Pattern C) instead of spawning a subagent. Avoids ~14K token spawn overhead for small plans. Set to 0 to always spawn subagents.
manager.flags.discuss "" Flags passed to /gsd:discuss-phase when dispatched from manager (e.g. "--auto --analyze")
manager.flags.plan "" Flags passed to plan workflow when dispatched from manager
manager.flags.execute "" Flags passed to execute workflow when dispatched from manager
response_language null Language for user-facing questions and prompts across all phases/subagents (e.g. "Portuguese", "Japanese", "Spanish"). When set, all spawned agents include a directive to respond in this language.
</config_schema>

<commit_docs_behavior>

When commit_docs: true (default):

  • Planning files committed normally
  • SUMMARY.md, STATE.md, ROADMAP.md tracked in git
  • Full history of planning decisions preserved

When commit_docs: false:

  • Skip all git add/git commit for .planning/ files
  • User must add .planning/ to .gitignore
  • Useful for: OSS contributions, client projects, keeping planning private

Using gsd-tools query (preferred):

# Commit with automatic commit_docs + gitignore checks:
gsd-tools query commit "docs: update state" --files .planning/STATE.md

# Load config via state load (returns JSON):
INIT=$(gsd-tools query state.load)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
# commit_docs is available in the JSON output

# Or use init commands which include commit_docs:
INIT=$(gsd-tools query init.execute-phase "1")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
# commit_docs is included in all init command outputs

Auto-detection: If .planning/ is gitignored, commit_docs is automatically false regardless of config.json. This prevents git errors when users have .planning/ in .gitignore.

Per-phase override: phase_commit_docs.<phase-id> (e.g. phase_commit_docs.03) overrides commit_docs for one phase only, and wins over both the explicit config value and gitignore auto-detection — see docs/CONFIGURATION.md#per-phase-override-phase_commit_docs for the full precedence chain and examples.

Commit via CLI (handles checks automatically):

gsd-tools query commit "docs: update state" --files .planning/STATE.md

The CLI checks commit_docs config and gitignore status internally — no manual conditionals needed.

</commit_docs_behavior>

<search_behavior>

When search_gitignored: false (default):

  • Standard rg behavior (respects .gitignore)
  • Direct path searches work: rg "pattern" .planning/ finds files
  • Broad searches skip gitignored: rg "pattern" skips .planning/

When search_gitignored: true:

  • Add --no-ignore to broad rg searches that should include .planning/
  • Only needed when searching entire repo and expecting .planning/ matches

Note: Most GSD operations use direct file reads or explicit paths, which work regardless of gitignore status.

</search_behavior>

<setup_uncommitted_mode>

To use uncommitted mode:

  1. Set config:

    "planning": {
      "commit_docs": false,
      "search_gitignored": true
    }
    
  2. Add to .gitignore:

    .planning/
    
  3. Existing tracked files: If .planning/ was previously tracked:

    git rm -r --cached .planning/
    git commit -m "chore: stop tracking planning docs"
    
  4. Branch merges: When using branching_strategy: phase or milestone, the complete-milestone workflow automatically strips .planning/ files from staging before merge commits when commit_docs: false.

</setup_uncommitted_mode>

<branching_strategy_behavior>

Branching Strategies:

Strategy When branch created Branch scope Merge point
none Never N/A N/A
phase At execute-phase start Single phase User merges after phase
milestone At first execute-phase of milestone Entire milestone At complete-milestone

When git.branching_strategy: "none" (default):

  • All work commits to current branch
  • Standard GSD behavior

When git.branching_strategy: "phase":

  • execute-phase creates/switches to a branch before execution
  • Branch name from phase_branch_template (e.g., gsd/phase-03-authentication)
  • All plan commits go to that branch
  • User merges branches manually after phase completion
  • complete-milestone offers to merge all phase branches

When git.branching_strategy: "milestone":

  • First execute-phase of milestone creates the milestone branch
  • Branch name from milestone_branch_template (e.g., gsd/v1.0-mvp)
  • All phases in milestone commit to same branch
  • complete-milestone offers to merge milestone branch to main

Template variables:

Variable Available in Description
{phase} phase_branch_template Zero-padded phase number (e.g., "03")
{slug} Both Lowercase, hyphenated name
{milestone} milestone_branch_template Milestone version (e.g., "v1.0")

Checking the config:

Use init execute-phase which returns all config as JSON:

INIT=$(gsd-tools query init.execute-phase "1")
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
# JSON output includes: branching_strategy, phase_branch_template, milestone_branch_template

Or use state load for the config values:

INIT=$(gsd-tools query state.load)
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
# Parse branching_strategy, phase_branch_template, milestone_branch_template from JSON

Branch creation:

# For phase strategy
if [ "$BRANCHING_STRATEGY" = "phase" ]; then
  PHASE_SLUG=$(echo "$PHASE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
  BRANCH_NAME=$(echo "$PHASE_BRANCH_TEMPLATE" | sed "s/{phase}/$PADDED_PHASE/g" | sed "s/{slug}/$PHASE_SLUG/g")
  git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
fi

# For milestone strategy
if [ "$BRANCHING_STRATEGY" = "milestone" ]; then
  MILESTONE_SLUG=$(echo "$MILESTONE_NAME" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9]/-/g' | sed 's/--*/-/g' | sed 's/^-//;s/-$//')
  BRANCH_NAME=$(echo "$MILESTONE_BRANCH_TEMPLATE" | sed "s/{milestone}/$MILESTONE_VERSION/g" | sed "s/{slug}/$MILESTONE_SLUG/g")
  git checkout -b "$BRANCH_NAME" 2>/dev/null || git checkout "$BRANCH_NAME"
fi

Merge options at complete-milestone:

Option Git command Result
Squash merge (recommended) git merge --squash Single clean commit per branch
Merge with history git merge --no-ff Preserves all individual commits
Delete without merging git branch -D Discard branch work
Keep branches (none) Manual handling later

Squash merge is recommended — keeps main branch history clean while preserving the full development history in the branch (until deleted).

Use cases:

Strategy Best for
none Solo development, simple projects
phase Code review per phase, granular rollback, team collaboration
milestone Release branches, staging environments, PR per version

</branching_strategy_behavior>

<complete_field_reference>

Complete Field Reference

Generated from CONFIG_DEFAULTS (configuration.cjs) and VALID_CONFIG_KEYS (config-schema.cjs).

Core Fields

Key Type Default Allowed Values Description
model_profile string "balanced" "quality", "balanced", "budget", "adaptive", "inherit" Model selection preset for subagents
mode string "interactive" "interactive", "yolo" Operation mode: "interactive" shows gates and confirmations; "yolo" runs autonomously without prompts
granularity string (none) "coarse", "standard", "fine" Planning depth for phase plans (migrated from deprecated depth)
commit_docs boolean true true, false Commit .planning/ artifacts to git (auto-false if .planning/ is gitignored)
search_gitignored boolean false true, false Include gitignored paths in broad rg searches via --no-ignore
phase_naming string "sequential" "sequential", "custom" Phase numbering: auto-increment or arbitrary string IDs
project_code string|null null Any short string Prefix for phase dirs (e.g., "CK" produces CK-01-foundation)
response_language string|null null Any language name Language for user-facing prompts (e.g., "Portuguese", "Japanese")
context_window number 200000 200000, 1000000 Context window size; set 1000000 for 1M-context models
resolve_model_ids boolean|string false false, true, "omit" Map model aliases to full Claude IDs; "omit" returns empty string
context string|null null "dev", "research", "review" Execution context profile that adjusts agent behavior: "dev" for development tasks, "research" for investigation/exploration, "review" for code review workflows
review.models.<cli> string|null null Any model ID string Per-CLI model override for /gsd:review (e.g., review.models.gemini). Falls back to CLI default when null.

Workflow Fields

Set via workflow.* namespace in config.json (e.g., "workflow": { "research": true }).

Key Type Default Allowed Values Description
workflow.research boolean true true, false Run research agent before planning
workflow.plan_check boolean true true, false Run plan-checker agent to validate plans. Alias: plan_checker is the flat-key form used in CONFIG_DEFAULTS; workflow.plan_check is the canonical namespaced form.
workflow.verifier boolean true true, false Run verifier agent after execution
workflow.nyquist_validation boolean true true, false Enable Nyquist-inspired validation gates
workflow.auto_prune_state boolean false true, false Automatically prune old STATE.md entries on phase completion (keeps 3 most recent phases)
workflow.auto_advance boolean false true, false Auto-advance to next phase after completion
workflow.node_repair boolean true true, false Attempt automatic repair of failed plan nodes
workflow.node_repair_budget number 2 Any positive integer Max repair retries per failed node
workflow.smart_zone_tokens number 100000 Any positive integer Smart-zone token budget for phase-effort estimation (#2630, ADR-2629). A phase whose estimate exceeds this is flagged with a split recommendation — advisory only, never a block. A policy default, not a benchmark constant: degradation begins before the advertised context window is full, but the effective ceiling is model- and task-dependent, so the calibration loop corrects it per project. Alias: smart_zone_tokens is the flat-key form used in CONFIG_DEFAULTS; workflow.smart_zone_tokens is the canonical namespaced form.
workflow.ai_integration_phase boolean true true, false Run /gsd:ai-integration-phase before planning AI system phases
workflow.api_coverage_gate boolean true true, false Require an explicit API-coverage decision (full-by-default, opt-out-not-opt-in) before a phase that integrates an external API/SDK/service can seal. At plan:pre prompts a COVERAGE.md matrix; at verify:pre a blocking gate fails the seal unless the matrix exists with every non-integrated capability an explicit, reasoned opt-out (#1562)
workflow.ui_phase boolean true true, false Generate UI-SPEC.md for frontend phases
workflow.ui_safety_gate boolean true true, false Require safety gate approval for UI changes
workflow.text_mode boolean false true, false Use plain-text numbered lists instead of AskUserQuestion menus
workflow.research_before_questions boolean false true, false Run research before interactive questions in discuss phase
workflow.discuss_mode string "discuss" "discuss", "assumptions" Default mode for discuss-phase: "discuss" runs interactive questioning; "assumptions" analyzes codebase and surfaces assumptions instead
workflow.skip_discuss boolean false true, false Skip discuss phase entirely
workflow.use_worktrees boolean true true, false Run executor agents in isolated git worktrees
workflow.subagent_timeout number 300000 Any positive integer (ms) Timeout for parallel subagent tasks (default: 5 minutes)
workflow.test_command string|null null Any shell command Regression/test gate command run by execute-phase, audit-fix, and post-merge-gate. Unset → GSD auto-detects (Makefile / package.json / Cargo.toml / go.mod / pyproject.toml).
workflow.build_command string|null null Any shell command Build gate command run by the post-merge gate. Unset → build step auto-detected/skipped.
workflow.mvp_mode boolean false true, false Persist the MVP-mode flag in config so every phase defaults to MVP framing without requiring --mvp on the CLI. Resolved via the chain: --mvp CLI flag → ROADMAP.md **Mode:** mvp field → this config value → false. When true, the planner, executor, verifier, and discovery surfaces (progress, stats, graphify) all treat the phase as an MVP vertical slice (UI → API → DB) of one user-visible capability.
workflow.context_guard_mode string "warn" "auto", "warn", "off" Context exhaustion guard mode for execute-phase. Before each wave, the orchestrator self-assesses context pressure using degradation signals from context-budget.md. "warn" (default): emit a warning and recommend /gsd:pause-work when POOR tier is detected. "auto": automatically invoke /gsd:pause-work before the next wave when POOR tier is detected. "off": disable the guard. The guard is heuristic — no programmatic context-% API exists.
workflow.plan_chunked boolean false true, false Enable chunked planning mode. When true, the plan-phase orchestrator splits the single long-lived planner Task into a short outline Task followed by N short per-plan Tasks (~3–5 min each). Each plan is committed individually for crash resilience. Particularly useful on Windows where long-lived Tasks may hang on stdio. Also activated by the --chunked flag.
workflow.specless_probe_fallback boolean true true, false Gate the SPEC-less probe fallback in plan-phase. When true (default), a phase that did not supply a ## Edge Coverage / ## Prohibitions SPEC section (header absent or present-but-empty) runs the existing probe protocol — the deterministic edge-probe.cjs for edges and an in-planner LLM recall pass for prohibitions — and authors the resulting predicates into PLAN.md must_haves (section-level precedence: a SPEC-supplied section is never re-run or overwritten). When false, the fallback is skipped but the skip is recorded: plan-phase emits a visible "probe fallback disabled" marker, never a silent skip.
workflow.code_review_command string|null null Any shell command External code-review command integrated into /gsd:ship. The diff is piped to the command via stdin; the command must output JSON with a verdict field ("APPROVED" or "REVISE"). Non-zero exit or "REVISE" verdict blocks the ship workflow. When unset, the built-in review flow runs. Example: my-review-tool --review.
workflow.inline_plan_threshold number 2 0–10 Plans with ≤N tasks execute inline instead of spawning a subagent
workflow.code_review boolean true true, false Enable built-in code review step in the ship workflow
workflow.code_review_depth string "standard" "quick", "standard", "deep" Depth level for code review analysis in the ship workflow
workflow.code_review_depth_overrides array [] Array of {paths, depth} rule objects Ordered path-scoped depth rules for /gsd:code-review (#2554). Each rule's paths are matched against the review's changed-file set by whole-segment directory-path prefix (src/auth matches src/auth/token.ts, never src/authfoo/x.ts); matching is case-sensitive. Glob syntax (*, ?) is a configuration error. One matched file escalates the entire review — depth is not applied per file. Resolution order: --depth= flag → strongest matching rule → workflow.code_review_depth → standard. A malformed rule halts the review with a typed error rather than falling back silently.
workflow._auto_chain_active boolean false true, false Internal: tracks whether autonomous chaining is active
workflow.security_enforcement boolean true true, false Enable threat-model-anchored security verification via /gsd:secure-phase. When false, security checks are skipped entirely
workflow.security_asvs_level number 1 1, 2, 3 OWASP ASVS verification level. Level 1 = opportunistic, Level 2 = standard, Level 3 = comprehensive. Scales both planner threat-disposition rigor (which threats must be mitigated vs. accepted) and auditor verification depth (grep-level → boundary-placement check → full data-flow trace). See gsd-core/references/security-asvs-levels.md.
workflow.security_block_on string "high" "critical", "high", "medium", "low", "none" Minimum threat severity that blocks phase advancement. The auditor counts only open threats at or above this severity toward the blocking gate (SECURITY.md threats_open); none disables severity blocking.
workflow.post_planning_gaps boolean true true, false Post-planning gap report (#2493). After plans are generated, scans REQUIREMENTS.md and CONTEXT.md <decisions> against all PLAN.md files and emits a unified Source | Item | Status table. Non-blocking. Set to false to skip Step 13e of plan-phase. Alias: post_planning_gaps is the flat-key form used in CONFIG_DEFAULTS; workflow.post_planning_gaps is the canonical namespaced form.

Ship Fields

Set via ship.* namespace in config.json. These fields affect /gsd:ship PRD-style pull request body composition only.

Key Type Default Allowed Values Description
ship.pr_body_sections array [] Array of section objects Append-only project-specific PR body sections. Each entry has heading, optional enabled, and one or more of source, template, or fallback. Disabled entries remain in onboarding config but do not render. Core sections remain required and cannot be removed or replaced.

Git Fields

Set via git.* namespace (e.g., "git": { "branching_strategy": "phase" }).

Key Type Default Allowed Values Description
git.branching_strategy string "none" "none", "phase", "milestone" Git branching approach for phase/milestone isolation
git.base_branch string|null null (auto-detect) Any branch name Target branch for PRs and merges; auto-detects from origin/HEAD when null
git.create_tag boolean true true, false Create git tags on milestone completion
git.phase_branch_template string "gsd/phase-{phase}-{slug}" Template with {phase}, {slug} Branch naming template for phase strategy
git.milestone_branch_template string "gsd/{milestone}-{slug}" Template with {milestone}, {slug} Branch naming template for milestone strategy
git.quick_branch_template string|null null Template with {slug} Optional branch template for quick-task runs

Search & API Fields

These toggle external search integrations. Auto-detected at project creation when API keys are present.

Key Type Default Allowed Values Description
brave_search boolean false true, false Enable Brave web search for research agent (requires BRAVE_API_KEY)
firecrawl boolean false true, false Enable Firecrawl page scraping (requires FIRECRAWL_API_KEY)
exa_search boolean false true, false Enable Exa semantic search (requires EXA_API_KEY)

Features Fields

Set via features.* namespace (e.g., "features": { "thinking_partner": true }).

Key Type Default Allowed Values Description
features.thinking_partner boolean false true, false Enable conditional extended thinking at workflow decision points (used by discuss-phase and plan-phase for architectural tradeoff analysis)
features.global_learnings boolean false true, false Enable injection of global learnings from ~/.gsd/knowledge/ into agent prompts

Hook Fields

Set via hooks.* namespace (e.g., "hooks": { "context_warnings": true }).

Key Type Default Allowed Values Description
hooks.context_warnings boolean true true, false Show warnings when context budget is exceeded

Learnings Fields

Set via learnings.* namespace (e.g., "learnings": { "max_inject": 5 }). Used together with features.global_learnings.

Key Type Default Allowed Values Description
learnings.max_inject number 10 Any positive integer Maximum number of global learning entries to inject into agent prompts per session

Intel Fields

Set via intel.* namespace (e.g., "intel": { "enabled": true }). Controls the queryable codebase intelligence system consumed by /gsd:map-codebase --query.

Key Type Default Allowed Values Description
intel.enabled boolean false true, false Enable queryable codebase intelligence system. When true, /gsd:map-codebase --query builds and queries a JSON index in .planning/intel/.

Manager Fields

Set via manager.* namespace (e.g., "manager": { "flags": { "discuss": "--auto" } }).

Key Type Default Allowed Values Description
manager.flags.discuss string "" Any CLI flags string Flags passed to /gsd:discuss-phase from manager (e.g., "--auto --analyze")
manager.flags.plan string "" Any CLI flags string Flags passed to plan workflow from manager
manager.flags.execute string "" Any CLI flags string Flags passed to execute workflow from manager

Advanced Fields

Key Type Default Allowed Values Description
parallelization boolean|object true true, false, { "enabled": true } Enable parallel wave execution; object form allows additional sub-keys
model_overrides object|null null { "<agent-type>": "<model-id>" } Override model selection per agent type
agent_skills object {} { "<agent-type>": "<skill-set>" } or { "<agent-type>": ["<skill-set>", "<skill-set>", ...] } Assign skill sets to specific agent types. Each value is a single skill-set path (string) or an array of skill-set paths — the array form assigns multiple skill sets to one agent type. Paths cannot be comma-joined into one string; each path must be its own array element
sub_repos array [] Array of relative path strings Child directories with independent .git repos (auto-detected)

Planning Fields

These can be set at top level or nested under planning.* (e.g., "planning": { "commit_docs": false }). Both forms are equivalent; top-level takes precedence if both exist.

Key Type Default Allowed Values Description
planning.commit_docs boolean true true, false Alias for top-level commit_docs
planning.search_gitignored boolean false true, false Alias for top-level search_gitignored

Field Interactions

Several config fields affect each other or trigger special behavior:

  1. commit_docs resolution chain -- Four tiers, highest wins: (1) phase_commit_docs.<phase-id> for the phase being committed, (2) an explicit commit_docs (or planning.commit_docs) value in config.json, (3) .gitignore auto-detection (.planning/ in .gitignore resolves to false), (4) the manifest default (true). Precedence: per-phase → explicit config → gitignore auto-detect → default.

  2. branching_strategy controls branch templates -- The phase_branch_template and milestone_branch_template fields are only used when branching_strategy is set to "phase" or "milestone" respectively. When branching_strategy is "none", all template fields are ignored.

  3. context_window threshold triggers -- When context_window >= 500000, workflows enable adaptive context enrichment: full-body reads of prior phase SUMMARYs, cross-phase context injection in plan-phase, and deeper read depth for anti-pattern references. Below 500000, only frontmatter and summaries are read.

  4. parallelization polymorphism -- Accepts both a simple boolean and an object with an enabled field. loadConfig() normalizes either form to a boolean. { "enabled": true } is equivalent to true.

  5. Search API keys and flags -- brave_search, firecrawl, and exa_search are auto-set to true during project creation if the corresponding API key is detected (environment variable or ~/.gsd/<name>_api_key file). Setting them to true without the API key has no effect.

  6. planning.* and top-level equivalence -- planning.commit_docs and commit_docs are equivalent; planning.search_gitignored and search_gitignored are equivalent. If both are set, the top-level value takes precedence.

  7. depth to granularity migration -- The deprecated depth key (quick/standard/comprehensive) is automatically migrated to granularity (coarse/standard/fine) on config load and persisted back to disk.

  8. sub_repos auto-sync -- On every config load, GSD scans for child directories with .git and updates the sub_repos array if the filesystem has changed. Legacy multiRepo: true is automatically migrated to a detected sub_repos array.

  9. workflow.use_worktrees and branch divergence -- When use_worktrees is true (default), executor worktrees are forked from origin/HEAD -- by the host's own harness on dispatch.isolation: harness-worktree runtimes (Claude Code, Cursor), or by GSD itself on orchestrator-worktree runtimes (Codex, OpenCode, Kimi, Kimi Code). The divergence behavior below is identical either way, because the fork base is a property of the repository rather than of whoever creates the worktree. If your current branch has commits that origin/HEAD does not (for example an unmerged milestone or feature branch), GSD automatically degrades to sequential execution for that run and prints a one-line ⚠ Worktree base mismatch warning. To restore parallel execution permanently, set worktree.baseRef:"head" in .claude/settings.local.json (run node gsd-tools.cjs worktree set-baseref). This makes the harness fork worktrees from the live HEAD instead of origin/HEAD. Both fresh installs and upgrades of GSD Core set this automatically (no-clobber) when use_worktrees is enabled; you can also run the command manually at any time. Setting workflow.use_worktrees: false is the alternative if worktrees are not needed at all. On a runtime whose declared dispatch.isolation is none, an explicit true is a config the execution workflows fail closed on; /gsd:health reports it as warning W025 and /gsd:settings offers to repair it (#2486).


Example Configurations

Minimal -- Solo Developer

{
  "model_profile": "balanced",
  "commit_docs": true,
  "workflow": {
    "research": true,
    "plan_check": true,
    "verifier": true,
    "use_worktrees": false
  }
}

Team Project with Branching

{
  "model_profile": "quality",
  "commit_docs": true,
  "project_code": "APP",
  "git": {
    "branching_strategy": "phase",
    "base_branch": "develop",
    "phase_branch_template": "gsd/phase-{phase}-{slug}"
  },
  "workflow": {
    "research": true,
    "plan_check": true,
    "verifier": true,
    "nyquist_validation": true,
    "use_worktrees": true,
    "discuss_mode": "discuss"
  },
  "manager": {
    "flags": {
      "discuss": "",
      "plan": "",
      "execute": ""
    }
  },
  "response_language": "English"
}

Large Codebase -- 1M Context with Extended Timeouts

{
  "model_profile": "quality",
  "context_window": 1000000,
  "commit_docs": true,
  "project_code": "MEGA",
  "phase_naming": "sequential",
  "git": {
    "branching_strategy": "milestone",
    "milestone_branch_template": "gsd/{milestone}-{slug}"
  },
  "workflow": {
    "research": true,
    "plan_check": true,
    "verifier": true,
    "nyquist_validation": true,
    "subagent_timeout": 600000,
    "use_worktrees": true,
    "node_repair": true,
    "node_repair_budget": 3,
    "auto_advance": true
  },
  "brave_search": true,
  "hooks": {
    "context_warnings": true
  }
}

</complete_field_reference>

</planning_config>