diff --git a/get-shit-done/references/planning-config.md b/get-shit-done/references/planning-config.md index df8f54b98..db9c2964f 100644 --- a/get-shit-done/references/planning-config.md +++ b/get-shit-done/references/planning-config.md @@ -214,4 +214,219 @@ Squash merge is recommended — keeps main branch history clean while preserving + + +## Complete Field Reference + +Generated from `CONFIG_DEFAULTS` (core.cjs) and `VALID_CONFIG_KEYS` (config.cjs). + +### Core Fields + +| Key | Type | Default | Allowed Values | Description | +|-----|------|---------|----------------|-------------| +| `model_profile` | string | `"balanced"` | `"quality"`, `"balanced"`, `"budget"`, `"inherit"` | Model selection preset for subagents | +| `mode` | string | (none) | `"code-first"`, `"plan-first"`, `"hybrid"` | Per-phase workflow mode controlling discuss/plan/execute flow | +| `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 | + +### 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_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.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"`, `"auto"`, `"analyze"` | Default mode for discuss-phase agent | +| `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._auto_chain_active` | boolean | `false` | `true`, `false` | Internal: tracks whether autonomous chaining is active | + +### 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.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) | + +### 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 | + +### 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` | `{ "": "" }` | Override model selection per agent type | +| `agent_skills` | object | `{}` | `{ "": "" }` | Assign skill sets to specific agent types | +| `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` auto-detection** -- When no explicit value is set in config.json and `.planning/` is in `.gitignore`, `commit_docs` automatically resolves to `false`. An explicit `true` or `false` in config always overrides auto-detection. + +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/_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. + +--- + +## Example Configurations + +### Minimal -- Solo Developer + +```json +{ + "model_profile": "balanced", + "commit_docs": true, + "workflow": { + "research": true, + "plan_check": true, + "verifier": true, + "use_worktrees": false + } +} +``` + +### Team Project with Branching + +```json +{ + "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 + +```json +{ + "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 + } +} +``` + + + diff --git a/tests/config-field-docs.test.cjs b/tests/config-field-docs.test.cjs new file mode 100644 index 000000000..bbe765d09 --- /dev/null +++ b/tests/config-field-docs.test.cjs @@ -0,0 +1,179 @@ +/** + * Verify planning-config.md documents all config fields from source code. + */ + +const { describe, test, before } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const REFERENCE_PATH = path.join(__dirname, '..', 'get-shit-done', 'references', 'planning-config.md'); +const CORE_PATH = path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'core.cjs'); + +describe('config-field-docs', () => { + let content; + + before(() => { + content = fs.readFileSync(REFERENCE_PATH, 'utf-8'); + }); + + test('contains Complete Field Reference section', () => { + assert.ok( + content.includes('## Complete Field Reference'), + 'planning-config.md must contain a "Complete Field Reference" heading' + ); + }); + + test('documents at least 15 config fields in tables', () => { + // Count table rows that start with | `` (field rows, not header/separator) + const fieldRows = content.match(/^\| `[a-z_][a-z0-9_.]*` \|/gm); + assert.ok(fieldRows, 'Expected markdown table rows with backtick-quoted keys'); + assert.ok( + fieldRows.length >= 15, + `Expected at least 15 documented fields, found ${fieldRows.length}` + ); + }); + + test('contains example configurations', () => { + assert.ok( + content.includes('## Example Configurations'), + 'planning-config.md must contain an "Example Configurations" section' + ); + // Verify at least one JSON code block with a model_profile key + assert.ok( + content.includes('"model_profile"'), + 'Example configurations must include model_profile' + ); + }); + + test('contains field interactions section', () => { + assert.ok( + content.includes('## Field Interactions'), + 'planning-config.md must contain a "Field Interactions" section' + ); + }); + + test('every CONFIG_DEFAULTS key appears in the doc', () => { + // Extract CONFIG_DEFAULTS keys from core.cjs source + const coreSource = fs.readFileSync(CORE_PATH, 'utf-8'); + const defaultsMatch = coreSource.match( + /const CONFIG_DEFAULTS\s*=\s*\{([\s\S]*?)\n\};/ + ); + assert.ok(defaultsMatch, 'Could not find CONFIG_DEFAULTS in core.cjs'); + + const body = defaultsMatch[1]; + // Match property keys (word characters before the colon) + const keys = [...body.matchAll(/^\s*(\w+)\s*:/gm)].map(m => m[1]); + assert.ok(keys.length > 0, 'Could not extract any keys from CONFIG_DEFAULTS'); + + // CONFIG_DEFAULTS uses flat keys; the doc may use namespaced equivalents. + // Map flat keys to the namespace forms used in config.json and the doc. + const NAMESPACE_MAP = { + research: 'workflow.research', + plan_checker: 'workflow.plan_check', + verifier: 'workflow.verifier', + nyquist_validation: 'workflow.nyquist_validation', + text_mode: 'workflow.text_mode', + subagent_timeout: 'workflow.subagent_timeout', + branching_strategy: 'git.branching_strategy', + phase_branch_template: 'git.phase_branch_template', + milestone_branch_template: 'git.milestone_branch_template', + quick_branch_template: 'git.quick_branch_template', + }; + + const missing = keys.filter(k => { + // Check both bare key and namespaced form + if (content.includes(`\`${k}\``)) return false; + const ns = NAMESPACE_MAP[k]; + if (ns && content.includes(`\`${ns}\``)) return false; + return true; + }); + assert.deepStrictEqual( + missing, + [], + `CONFIG_DEFAULTS keys missing from planning-config.md: ${missing.join(', ')}` + ); + }); + + test('documents workflow namespace fields', () => { + const workflowFields = [ + 'workflow.research', + 'workflow.plan_check', + 'workflow.verifier', + 'workflow.nyquist_validation', + 'workflow.use_worktrees', + 'workflow.subagent_timeout', + 'workflow.text_mode', + ]; + const missing = workflowFields.filter(f => !content.includes(`\`${f}\``)); + assert.deepStrictEqual( + missing, + [], + `Workflow fields missing from planning-config.md: ${missing.join(', ')}` + ); + }); + + test('documents git namespace fields', () => { + const gitFields = [ + 'git.branching_strategy', + 'git.base_branch', + 'git.phase_branch_template', + 'git.milestone_branch_template', + ]; + const missing = gitFields.filter(f => !content.includes(`\`${f}\``)); + assert.deepStrictEqual( + missing, + [], + `Git fields missing from planning-config.md: ${missing.join(', ')}` + ); + }); + + test('documents KNOWN_TOP_LEVEL internal fields not in CONFIG_DEFAULTS', () => { + // These fields are in KNOWN_TOP_LEVEL (core.cjs) and read by loadConfig() + // but not in CONFIG_DEFAULTS, so the CONFIG_DEFAULTS test doesn't cover them. + const internalFields = [ + 'model_overrides', + 'agent_skills', + ]; + const missing = internalFields.filter(f => !content.includes(`\`${f}\``)); + assert.deepStrictEqual( + missing, + [], + `KNOWN_TOP_LEVEL internal fields missing from planning-config.md: ${missing.join(', ')}` + ); + }); + + test('documents sub_repos field (CONFIG_DEFAULTS, no namespace form)', () => { + // sub_repos is in CONFIG_DEFAULTS but has no NAMESPACE_MAP entry + // (it uses a planning.sub_repos nested lookup but is documented as a + // top-level field). Verify it explicitly since the NAMESPACE_MAP path + // would silently skip it. + assert.ok( + content.includes('`sub_repos`'), + 'planning-config.md must document the sub_repos field' + ); + }); + + test('documents features.thinking_partner field', () => { + // features.thinking_partner is in VALID_CONFIG_KEYS (config.cjs) and + // used by discuss-phase.md and plan-phase.md for conditional extended + // thinking at workflow decision points. + assert.ok( + content.includes('`features.thinking_partner`'), + 'planning-config.md must document the features.thinking_partner field' + ); + }); + + test('documents plan_checker alias for workflow.plan_check', () => { + // plan_checker is the flat-key form in CONFIG_DEFAULTS; workflow.plan_check + // is the canonical namespaced form. The doc should mention the alias. + assert.ok( + content.includes('`workflow.plan_check`'), + 'planning-config.md must document workflow.plan_check' + ); + assert.ok( + content.includes('plan_checker'), + 'planning-config.md must mention the plan_checker flat-key alias' + ); + }); +});