Files
msd-core/get-shit-done/workflows/settings-advanced.md
Tom Boucher 8bc255c266 fix(workstream): normalize migration workstream names (#3269)
* fix(workstream): normalize migrate-name to valid slug

* docs(context): record workstream migrate-name slug invariant

* fix(catalog-cjs): balanced fallback for unknown profile (CR finding A)

profiles[profile] could return undefined for any profile key absent from
the catalog entry, causing downstream callers like formatAgentToModelMapAsTable
to crash on .length. Add ?? profiles.balanced fallback to match the SDK adapter.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(sdk): anchor path resolution on import.meta.url not cwd (CR finding B)

resolve(process.cwd(), '..') breaks when Vitest is invoked from the repo root
because cwd is already the repo root and '..' goes one level above. Replace
with a file-relative path using fileURLToPath(new URL('../../../', import.meta.url))
anchored at the test file's location (sdk/src/query/).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test: derive Group B runtime list from catalog (CR finding C)

Hardcoded ['kilo', 'cline', ...] throws TypeError if a runtime name is
removed from the catalog. Derive group B dynamically via
Object.keys(catalog.runtimeTierDefaults).filter(r => !r.opus) so the
test never goes stale and auto-covers future Group B additions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(workflow): add hermes to Step B runtime options (CR finding D)

hermes appears in the Group A built-in defaults table but was missing from
the AskUserQuestion options in Step B, forcing users to manually type it via
'Other (Group B or custom)'. Add explicit hermes entry for UI consistency.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(config): refresh dynamic_routing tier table; fix stale L671 (findings E+F)

Finding E: tier table was missing 6 heavy-tier agents and 15 standard/light
agents added by this PR. Updated all three rows to match catalog routingTier
assignments (33 agents total).

Finding F: removed stale '18 of 31' claim and agent enumeration; replaced
with accurate note that all 33 agents have explicit catalog entries. Updated
authoritative source pointers to model-catalog.cjs / model-catalog.ts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(core): add profile-fallback unit tests for quality and budget (CR nitpick G)

The PR introduced quality→opus and budget→haiku unknown-agent fallbacks but
only balanced→sonnet and inherit→inherit were tested. Add two tests covering
the remaining two branches to complete coverage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* adr: define planning workspace and worktree seam

* refactor(worktree): extract worktree safety policy module

* refactor(workstream): extract active workstream pointer store seam

* test(worktree): cover policy branch paths and persist seam guardrails

* refactor(worktree): centralize health inventory seam for W017

* fix(workspace): align SDK project path policy with CJS planningDir

* refactor(query): unify SDK planning path projection seam

* refactor(init): route workspace projection through planningPaths seam

* docs(adr): add SDK architecture and planning path ADRs

* refactor(worktree): deepen name, pointer, inventory, and config seams

* docs(config): harmonize claude-opus-4-6 to 4-7 in resolve_model_ids example (CR finding 2)

* fix(sdk): return undefined for model_profile='inherit' sentinel (CR finding 3)

* docs(adr): renumber conflicting 0003-sdk-package-seam-module to 0007, update seam-map reference (CR finding 4)

* fix(workstream): align CJS and SDK name validation to accept dots, guard path traversal via includes('..') (CR finding 5)

* fix(sdk): guard writeActiveWorkstream against non-existent workstream directory, k014/k031 parity (CR finding 6)

* chore(changeset): add #3269 changeset (CR finding 1 — proper changeset for this PR)

* docs(inventory): register 3 new CLI modules in INVENTORY.md/MANIFEST (active-workstream-store, workstream-name-policy, worktree-safety)

* fix(sdk): use relPlanningPath(workstream) in planningPaths, fix setActiveWorkstream/getActiveWorkstream name errors in workstream.ts

* fix(sdk): validate GSD_WORKSTREAM in planningPaths before use (#3269 regression)

planningPaths() called resolveWorkspaceContext() which returned GSD_WORKSTREAM
raw (no validation). An invalid value like '../evil' was used as effectiveWorkstream,
constructing a bad path; roadmapAnalyze() caught the ENOENT and returned a
no-phase_count error object instead of the root ROADMAP result.

Fix: validate envCtx.workstream with validateWorkstreamName() in planningPaths()
before accepting it as effectiveWorkstream. Invalid env → null → root .planning/
fallback, preserving the bug-2791 contract: invalid GSD_WORKSTREAM is silently
ignored and falls back to the root context (phase_count: 0 for empty root ROADMAP).

The bug-2791 regression test now passes. No other call sites read GSD_WORKSTREAM
without validation: query-runtime-context.ts already validates; cli.ts already
validates; context-engine.ts takes a caller-validated workstream parameter.

Closes #3268 (regression introduced by #3269 workstream-name-policy work).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 00:15:04 -04:00

24 KiB
Raw Blame History

Interactive configuration of GSD power-user knobs — plan bounce, node repair, subagent timeouts, inline plan threshold, cross-AI execution, base branch, branch templates, response language, context window, gitignored search, graphify build timeout, and runtime model tier overrides.

This is a companion to /gsd-settings — the common-case prompt there covers model profile, research/plan_check/verifier toggles, branching strategy, UI/AI phase gates, and worktree isolation. This advanced command covers everything else that is user-settable, grouped into seven sections so each prompt batch stays cognitively scoped. Every answer pre-selects the current value; numeric-input answers that are non-numeric are rejected and re-prompted.

<required_reading> Read all files referenced by the invoking prompt's execution_context before starting. </required_reading>

Ensure config exists and resolve the workstream-aware config path (mirrors `settings.md`):
gsd-sdk query config-ensure-section
if [[ -z "${GSD_CONFIG_PATH:-}" ]]; then
  if [[ -f .planning/active-workstream ]]; then
    WS=$(tr -d '\n\r' < .planning/active-workstream)
    GSD_CONFIG_PATH=".planning/workstreams/${WS}/config.json"
  else
    GSD_CONFIG_PATH=".planning/config.json"
  fi
fi

All subsequent reads and writes go through $GSD_CONFIG_PATH. Never hardcode .planning/config.json — workstream installs must route to their own config file.

```bash cat "$GSD_CONFIG_PATH" ```

Parse the following current values. If a key is absent, fall back to the documented default shown in parentheses:

Planning Tuning:

  • workflow.plan_bounce (default: false)
  • workflow.plan_bounce_passes (default: 2)
  • workflow.plan_bounce_script (default: null)
  • workflow.subagent_timeout (default: 600)
  • workflow.inline_plan_threshold (default: 3)

Execution Tuning:

  • workflow.node_repair (default: true)
  • workflow.node_repair_budget (default: 2)
  • workflow.auto_prune_state (default: false)

Discussion Tuning:

  • workflow.max_discuss_passes (default: 3)

Cross-AI Execution:

  • workflow.cross_ai_execution (default: false)
  • workflow.cross_ai_command (default: null)
  • workflow.cross_ai_timeout (default: 300)

Git Customization:

  • git.base_branch (default: main)
  • git.phase_branch_template (default: gsd/phase-{phase}-{slug})
  • git.milestone_branch_template (default: gsd/{milestone}-{slug})

Runtime / Output:

  • response_language (default: null)
  • context_window (default: 200000)
  • search_gitignored (default: false)
  • graphify.build_timeout (default: 300)

Runtime Model Tiers:

  • runtime (default: null — reads as "claude")
  • model_profile_overrides.<runtime>.opus (default: built-in for the runtime, or absent)
  • model_profile_overrides.<runtime>.sonnet (default: built-in for the runtime, or absent)
  • model_profile_overrides.<runtime>.haiku (default: built-in for the runtime, or absent)

Each field's current value is pre-selected in the prompt rendering below. When the current value is absent from the config, render the documented default as the pre-selected option so the user sees what the effective value is.

Text mode (workflow.text_mode: true or --text flag): Set TEXT_MODE=true if --text is in $ARGUMENTS OR text_mode is true in config. When TEXT_MODE=true, replace every AskUserQuestion call below with a plain-text numbered list and ask the user to type the choice number or free-text value.

Numeric-input validation. For any numeric field (*_passes, *_budget, *_timeout, *_threshold, context_window, graphify.build_timeout), if the user types a value that is not a non-negative integer, the workflow MUST reject it, state which value was invalid, and re-prompt that single field. The minimum accepted value is field-specific and is stated in each field's prompt below — workflow.plan_bounce_passes and workflow.max_discuss_passes require >= 1; all other numeric fields accept >= 0. An empty input means "keep current" — the existing value is retained. Non-numeric input is never silently coerced.

Free-text validation. For branch template fields (git.phase_branch_template, git.milestone_branch_template), if the user supplies a non-default value, it MUST be non-empty and SHOULD contain at least one {placeholder}. A template missing placeholders is rejected with a message explaining the available variables ({phase}, {slug}, {milestone}) and re-prompted. An empty input means "keep current."

Null-allowed fields. For response_language, workflow.plan_bounce_script, workflow.cross_ai_command: an empty input clears the field (null). A non-empty input is stored verbatim as a string.


Section 1 — Planning Tuning

AskUserQuestion([
  {
    question: "Run external plan-bounce validator against generated PLAN.md? (current: <value or false>)",
    header: "Plan Bounce",
    multiSelect: false,
    options: [
      { label: "No (default: false)", description: "Skip external plan validation." },
      { label: "Yes", description: "Pipe each PLAN.md through `plan_bounce_script` and block on non-zero exit." }
    ]
  },
  {
    question: "How many plan-bounce passes? (current: <value or 2>)",
    header: "Bounce Passes",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave the existing value unchanged." },
      { label: "Enter number", description: "Type an integer >= 1. Non-numeric input is rejected and re-prompted. Default: 2" }
    ]
  },
  {
    question: "Path to plan-bounce validation script? (current: <value or null>)",
    header: "Bounce Script",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave existing path unchanged." },
      { label: "Clear (null)", description: "Unset the script path." },
      { label: "Enter path", description: "Type an absolute or repo-relative path. Receives PLAN.md path as first argument." }
    ]
  },
  {
    question: "Subagent timeout (seconds)? (current: <value or 600>)",
    header: "Subagent Timeout",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave timeout unchanged." },
      { label: "Enter seconds", description: "Integer number of seconds. Non-numeric rejected. Default: 600" }
    ]
  },
  {
    question: "Inline plan threshold — tasks allowed inline before splitting to PLAN.md? (current: <value or 3>)",
    header: "Inline Plan Threshold",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave threshold unchanged." },
      { label: "Enter number", description: "Integer count. Non-numeric rejected. Default: 3" }
    ]
  }
])

Section 2 — Execution Tuning

AskUserQuestion([
  {
    question: "Enable autonomous node repair on verification failure? (current: <value or true>)",
    header: "Node Repair",
    multiSelect: false,
    options: [
      { label: "Yes (default: true)", description: "Executor retries failed tasks up to the repair budget." },
      { label: "No", description: "Stop on first verification failure." }
    ]
  },
  {
    question: "Maximum node-repair attempts per failed task? (current: <value or 2>)",
    header: "Repair Budget",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave existing budget unchanged." },
      { label: "Enter number", description: "Integer >= 0. Non-numeric rejected. Default: 2" }
    ]
  },
  {
    question: "Auto-prune stale STATE.md entries at phase boundaries? (current: <value or false>)",
    header: "Auto Prune",
    multiSelect: false,
    options: [
      { label: "No (default: false)", description: "Prompt before pruning." },
      { label: "Yes", description: "Prune stale entries without prompting." }
    ]
  }
])

Section 3 — Discussion Tuning

AskUserQuestion([
  {
    question: "Maximum discuss-phase question rounds? (current: <value or 3>)",
    header: "Max Discuss Passes",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave existing value unchanged." },
      { label: "Enter number", description: "Integer >= 1. Non-numeric rejected. Default: 3. Prevents infinite discussion loops in headless mode." }
    ]
  }
])

Section 4 — Cross-AI Execution

AskUserQuestion([
  {
    question: "Delegate phase execution to an external AI CLI? (current: <value or false>)",
    header: "Cross-AI",
    multiSelect: false,
    options: [
      { label: "No (default: false)", description: "Use local executor agents." },
      { label: "Yes", description: "Pipe phase prompt to `cross_ai_command` via stdin. Requires command to be set." }
    ]
  },
  {
    question: "Cross-AI command template? (current: <value or null>)",
    header: "Cross-AI Command",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave command unchanged." },
      { label: "Clear (null)", description: "Unset the command." },
      { label: "Enter command", description: "Shell command receiving phase prompt via stdin. Must produce SUMMARY.md-compatible output." }
    ]
  },
  {
    question: "Cross-AI timeout (seconds)? (current: <value or 300>)",
    header: "Cross-AI Timeout",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave timeout unchanged." },
      { label: "Enter seconds", description: "Integer seconds. Non-numeric rejected. Default: 300" }
    ]
  }
])

Section 5 — Git Customization

AskUserQuestion([
  {
    question: "Git base branch? (current: <value or main>)",
    header: "Base Branch",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave base branch unchanged." },
      { label: "Enter branch name", description: "e.g., main, master, develop. Integration branch for phase/milestone branches." }
    ]
  },
  {
    question: "Phase branch template? (current: <value or gsd/phase-{phase}-{slug}>)",
    header: "Phase Template",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave template unchanged." },
      { label: "Enter template", description: "Non-empty string with at least one placeholder. Available: {phase}, {slug}. Non-default values missing placeholders are rejected." }
    ]
  },
  {
    question: "Milestone branch template? (current: <value or gsd/{milestone}-{slug}>)",
    header: "Milestone Template",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave template unchanged." },
      { label: "Enter template", description: "Non-empty string. Available placeholders: {milestone}, {slug}. Non-default values missing placeholders are rejected." }
    ]
  }
])

Section 6 — Runtime / Output

AskUserQuestion([
  {
    question: "Response language for agent output? (current: <value or null>)",
    header: "Language",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave unchanged." },
      { label: "Clear (null)", description: "Use Claude default (English)." },
      { label: "Enter language", description: "Free-text language name or code (e.g., Japanese, pt, ko). Propagates to spawned agents." }
    ]
  },
  {
    question: "Context window size (tokens)? (current: <value or 200000>)",
    header: "Context Window",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave unchanged." },
      { label: "Enter number", description: "Integer. Non-numeric rejected. Default: 200000. Use 1000000 for 1M-context models. Values >= 500000 enable adaptive enrichment." }
    ]
  },
  {
    question: "Include gitignored files in broad searches? (current: <value or false>)",
    header: "Search Gitignored",
    multiSelect: false,
    options: [
      { label: "No (default: false)", description: "Respect .gitignore during searches." },
      { label: "Yes", description: "Add --no-ignore to broad searches (includes .planning/)." }
    ]
  },
  {
    question: "Graphify build timeout (seconds)? (current: <value or 300>)",
    header: "Graphify Timeout",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave timeout unchanged." },
      { label: "Enter seconds", description: "Integer seconds. Non-numeric rejected. Default: 300" }
    ]
  }
])

Section 7 — Runtime Model Tiers

This section lets the user inspect and override the built-in model IDs GSD resolves for each profile tier (opus / sonnet / haiku) on their configured runtime.

Step A — Show current runtime and built-in defaults:

Read runtime from the config (or treat as "claude" if absent). Look up the built-in tier map from the table below. For each tier, also read the current override from model_profile_overrides.<runtime>.<tier> if present.

Built-in tier defaults by runtime:

Runtime opus sonnet haiku
claude claude-opus-4-7 claude-sonnet-4-6 claude-haiku-4-5
codex gpt-5.4 gpt-5.3-codex gpt-5.4-mini
gemini gemini-3-pro gemini-3-flash gemini-2.5-flash-lite
qwen qwen3-max-2026-01-23 qwen3-coder-plus qwen3-coder-next
opencode anthropic/claude-opus-4-7 anthropic/claude-sonnet-4-6 anthropic/claude-haiku-4-5
copilot claude-opus-4-7 claude-sonnet-4-6 claude-haiku-4-5
hermes anthropic/claude-opus-4-7 anthropic/claude-sonnet-4-6 anthropic/claude-haiku-4-5
Group B (kilo, cline, cursor, windsurf, augment, trae, codebuddy, antigravity) (no built-in default — your runtime handles model selection)

Display a table to the user showing the effective configuration:

Runtime model tiers — runtime: <current runtime or "claude (default)">

| Tier   | Built-in default                  | Current override (if any)         |
|--------|-----------------------------------|-----------------------------------|
| opus   | <built-in or "(no built-in)">     | <override value or "(none)">      |
| sonnet | <built-in or "(no built-in)">     | <override value or "(none)">      |
| haiku  | <built-in or "(no built-in)">     | <override value or "(none)">      |

For Group B runtimes (those without a built-in default), show (no built-in default — your runtime handles model selection) in the built-in column.

Step B — Let the user choose a runtime (optional):

AskUserQuestion([
  {
    question: "Which runtime do you want to configure tier overrides for? (current: <runtime or 'claude'>)",
    header: "Runtime Selection",
    multiSelect: false,
    options: [
      { label: "Keep current (<runtime>)", description: "Configure overrides for the current runtime." },
      { label: "claude", description: "Claude Code / Anthropic CLI." },
      { label: "codex", description: "OpenAI Codex CLI." },
      { label: "gemini", description: "Gemini CLI." },
      { label: "qwen", description: "Qwen CLI." },
      { label: "opencode", description: "OpenCode (uses anthropic/ prefix)." },
      { label: "copilot", description: "GitHub Copilot." },
      { label: "hermes", description: "Hermes (uses anthropic/ prefix)." },
      { label: "Other (Group B or custom)", description: "kilo, cline, cursor, windsurf, augment, trae, codebuddy, antigravity, or a custom runtime string. Overrides are honored even though no built-in map exists." }
    ]
  }
])

If "Other" is selected, prompt the user to enter the runtime name as a free-text string. If the selected runtime differs from the stored runtime key, update runtime via gsd-sdk query config-set runtime <value> before proceeding to Step C.

Step C — Configure tier overrides for the selected runtime:

AskUserQuestion([
  {
    question: "Override for opus tier? Built-in: <opus default or '(no built-in)'>  Current: <override or '(none)'>",
    header: "Opus Override",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave unchanged (uses built-in default if no override)." },
      { label: "Clear override", description: "Remove any existing override; fall back to built-in." },
      { label: "Enter model ID", description: "Type the exact model ID string to use for opus-tier agents on this runtime." }
    ]
  },
  {
    question: "Override for sonnet tier? Built-in: <sonnet default or '(no built-in)'>  Current: <override or '(none)'>",
    header: "Sonnet Override",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave unchanged." },
      { label: "Clear override", description: "Remove any existing override; fall back to built-in." },
      { label: "Enter model ID", description: "Type the exact model ID string to use for sonnet-tier agents on this runtime." }
    ]
  },
  {
    question: "Override for haiku tier? Built-in: <haiku default or '(no built-in)'>  Current: <override or '(none)'>",
    header: "Haiku Override",
    multiSelect: false,
    options: [
      { label: "Keep current", description: "Leave unchanged." },
      { label: "Clear override", description: "Remove any existing override; fall back to built-in." },
      { label: "Enter model ID", description: "Type the exact model ID string to use for haiku-tier agents on this runtime." }
    ]
  }
])

Step D — Apply the changes:

For each tier where the user chose "Enter model ID":

gsd-sdk query config-set model_profile_overrides.<runtime>.<tier> "<model-id>"

For each tier where the user chose "Clear override", remove the key by setting it to null:

gsd-sdk query config-set model_profile_overrides.<runtime>.<tier> null

"Keep current" selections are skipped entirely. Never write a key the user did not explicitly change.

Merge the new settings into the existing config at `$GSD_CONFIG_PATH`. This merge is the core correctness invariant: **preserve every unrelated key** — do not clobber siblings.

Apply each selected value via gsd-sdk query config-set <key> <value> so the central validator (isValidConfigKey) accepts the write and the deep-merge preserves unrelated keys and sibling sub-objects.

# Example — only write keys the user changed. "Keep current" selections are skipped.
gsd-sdk query config-set workflow.plan_bounce_passes 5
gsd-sdk query config-set workflow.subagent_timeout 900
gsd-sdk query config-set git.base_branch main
gsd-sdk query config-set context_window 1000000
# Runtime model tier examples:
gsd-sdk query config-set runtime gemini
gsd-sdk query config-set model_profile_overrides.gemini.opus gemini-3-ultra
gsd-sdk query config-set model_profile_overrides.gemini.haiku null

Conceptual shape after merge (unchanged top-level keys like model_profile, granularity, mode, brave_search, agent_skills.*, hooks.context_warnings, and anything not listed in Sections 1–7 MUST survive the update):

{
  ...existing_config,
  "workflow": {
    ...existing_workflow,
    "plan_bounce": <new|existing>,
    "plan_bounce_passes": <new|existing>,
    "plan_bounce_script": <new|existing|null>,
    "subagent_timeout": <new|existing>,
    "inline_plan_threshold": <new|existing>,
    "node_repair": <new|existing>,
    "node_repair_budget": <new|existing>,
    "auto_prune_state": <new|existing>,
    "max_discuss_passes": <new|existing>,
    "cross_ai_execution": <new|existing>,
    "cross_ai_command": <new|existing|null>,
    "cross_ai_timeout": <new|existing>
  },
  "git": {
    ...existing_git,
    "base_branch": <new|existing>,
    "phase_branch_template": <new|existing>,
    "milestone_branch_template": <new|existing>
  },
  "response_language": <new|existing|null>,
  "context_window": <new|existing>,
  "search_gitignored": <new|existing>,
  "graphify": {
    ...existing_graphify,
    "build_timeout": <new|existing>
  },
  "runtime": <new|existing|null>,
  "model_profile_overrides": {
    ...existing_model_profile_overrides,
    "<runtime>": {
      ...existing_runtime_overrides,
      "opus": <new|existing|null>,
      "sonnet": <new|existing|null>,
      "haiku": <new|existing|null>
    }
  }
}

Never emit a full overwrite of the file that omits keys the user did not touch. Always route each write through gsd-sdk query config-set so sibling preservation is handled by the central setter.

Display:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 GSD ► ADVANCED SETTINGS UPDATED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

| Setting                                    | Value |
|--------------------------------------------|-------|
| workflow.plan_bounce                       | {on/off} |
| workflow.plan_bounce_passes                | {n} |
| workflow.plan_bounce_script                | {path/null} |
| workflow.subagent_timeout                  | {seconds} |
| workflow.inline_plan_threshold             | {n} |
| workflow.node_repair                       | {on/off} |
| workflow.node_repair_budget                | {n} |
| workflow.auto_prune_state                  | {on/off} |
| workflow.max_discuss_passes                | {n} |
| workflow.cross_ai_execution                | {on/off} |
| workflow.cross_ai_command                  | {cmd/null} |
| workflow.cross_ai_timeout                  | {seconds} |
| git.base_branch                            | {branch} |
| git.phase_branch_template                  | {template} |
| git.milestone_branch_template              | {template} |
| response_language                          | {lang/null} |
| context_window                             | {tokens} |
| search_gitignored                          | {on/off} |
| graphify.build_timeout                     | {seconds} |
| runtime                                    | {runtime/null} |
| model_profile_overrides.<runtime>.opus     | {model/built-in/null} |
| model_profile_overrides.<runtime>.sonnet   | {model/built-in/null} |
| model_profile_overrides.<runtime>.haiku    | {model/built-in/null} |

These settings apply to future /gsd-plan-phase, /gsd-execute-phase, /gsd-discuss-phase,
and /gsd-ship runs.

For common-case toggles (model profile, research/plan_check/verifier, branching strategy,
UI/AI phase gates), use /gsd-settings.

<success_criteria>

  • Current config read from resolved $GSD_CONFIG_PATH
  • Seven sections rendered (Planning, Execution, Discussion, Cross-AI, Git, Runtime/Output, Runtime Model Tiers)
  • Every field pre-selected to its current value (or documented default if absent)
  • Numeric inputs validated — non-numeric rejected and re-prompted
  • Branch-template inputs validated — non-default must contain a placeholder
  • Null-allowed fields accept an empty input as a clear
  • Writes routed through gsd-sdk query config-set so unrelated keys are preserved
  • Section 7 shows current runtime and built-in tier table
  • Group B runtimes display "(no built-in default — your runtime handles model selection)"
  • Override set/clear/keep paths all work correctly for each tier
  • Confirmation table rendered listing all 23 fields (19 + runtime + 3 tier overrides) </success_criteria>