Option A — ghost-entry guard (INVENTORY ⊆ actual): tests/inventory-source-parity.test.cjs parses every declared row in INVENTORY.md and asserts the source file exists. Catches deletions and renames that leave ghost entries behind. Option B — auto-generated structural manifest: scripts/gen-inventory-manifest.cjs walks all six family dirs and emits docs/INVENTORY-MANIFEST.json. tests/inventory-manifest-sync.test.cjs fails CI when a new surface ships without a manifest update, surfacing exactly which entries are missing. Option C — schema-driven config validation + docs parity: get-shit-done/bin/lib/config-schema.cjs extracted from config.cjs as the single source of truth for VALID_CONFIG_KEYS and dynamic patterns. config.cjs now imports from it. tests/config-schema-docs-parity.test.cjs asserts every exact-match key appears in docs/CONFIGURATION.md, surfacing 14 previously undocumented keys (planning.sub_repos, workflow.ai_integration_phase, git.base_branch, learnings.max_inject, and 10 others) — all now documented in their appropriate sections. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
3.0 KiB
JavaScript
78 lines
3.0 KiB
JavaScript
'use strict';
|
|
|
|
/**
|
|
* Single source of truth for valid config key paths.
|
|
*
|
|
* Imported by:
|
|
* - config.cjs (isValidConfigKey validator)
|
|
* - tests/config-schema-docs-parity.test.cjs (CI drift guard)
|
|
*
|
|
* Adding a key here without documenting it in docs/CONFIGURATION.md will
|
|
* fail the parity test. Adding a key to docs/CONFIGURATION.md without
|
|
* adding it here will cause config-set to reject it at runtime.
|
|
*/
|
|
|
|
/** Exact-match config key paths accepted by config-set. */
|
|
const VALID_CONFIG_KEYS = new Set([
|
|
'mode', 'granularity', 'parallelization', 'commit_docs', 'model_profile',
|
|
'search_gitignored', 'brave_search', 'firecrawl', 'exa_search',
|
|
'workflow.research', 'workflow.plan_check', 'workflow.verifier',
|
|
'workflow.nyquist_validation', 'workflow.ai_integration_phase', 'workflow.ui_phase', 'workflow.ui_safety_gate',
|
|
'workflow.auto_advance', 'workflow.node_repair', 'workflow.node_repair_budget',
|
|
'workflow.tdd_mode',
|
|
'workflow.text_mode',
|
|
'workflow.research_before_questions',
|
|
'workflow.discuss_mode',
|
|
'workflow.skip_discuss',
|
|
'workflow.auto_prune_state',
|
|
'workflow._auto_chain_active',
|
|
'workflow.use_worktrees',
|
|
'workflow.code_review',
|
|
'workflow.code_review_depth',
|
|
'workflow.code_review_command',
|
|
'workflow.pattern_mapper',
|
|
'workflow.plan_bounce',
|
|
'workflow.plan_bounce_script',
|
|
'workflow.plan_bounce_passes',
|
|
'workflow.security_enforcement',
|
|
'workflow.security_asvs_level',
|
|
'workflow.security_block_on',
|
|
'git.branching_strategy', 'git.base_branch', 'git.phase_branch_template', 'git.milestone_branch_template', 'git.quick_branch_template',
|
|
'planning.commit_docs', 'planning.search_gitignored', 'planning.sub_repos',
|
|
'workflow.cross_ai_execution', 'workflow.cross_ai_command', 'workflow.cross_ai_timeout',
|
|
'workflow.subagent_timeout',
|
|
'workflow.inline_plan_threshold',
|
|
'hooks.context_warnings',
|
|
'features.thinking_partner',
|
|
'context',
|
|
'features.global_learnings',
|
|
'learnings.max_inject',
|
|
'project_code', 'phase_naming',
|
|
'manager.flags.discuss', 'manager.flags.plan', 'manager.flags.execute',
|
|
'response_language',
|
|
'intel.enabled',
|
|
'graphify.enabled',
|
|
'graphify.build_timeout',
|
|
'claude_md_path',
|
|
]);
|
|
|
|
/**
|
|
* Dynamic-pattern validators — keys matching these regexes are also accepted.
|
|
* Each entry has a `test` function and a human-readable `description`.
|
|
*/
|
|
const DYNAMIC_KEY_PATTERNS = [
|
|
{ test: (k) => /^agent_skills\.[a-zA-Z0-9_-]+$/.test(k), description: 'agent_skills.<agent-type>' },
|
|
{ test: (k) => /^review\.models\.[a-zA-Z0-9_-]+$/.test(k), description: 'review.models.<cli-name>' },
|
|
{ test: (k) => /^features\.[a-zA-Z0-9_]+$/.test(k), description: 'features.<feature_name>' },
|
|
];
|
|
|
|
/**
|
|
* Returns true if keyPath is a valid config key (exact or dynamic pattern).
|
|
*/
|
|
function isValidConfigKey(keyPath) {
|
|
if (VALID_CONFIG_KEYS.has(keyPath)) return true;
|
|
return DYNAMIC_KEY_PATTERNS.some((p) => p.test(keyPath));
|
|
}
|
|
|
|
module.exports = { VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS, isValidConfigKey };
|