diff --git a/.changeset/sturdy-koalas-zip.md b/.changeset/sturdy-koalas-zip.md new file mode 100644 index 000000000..7fc0bc106 --- /dev/null +++ b/.changeset/sturdy-koalas-zip.md @@ -0,0 +1,5 @@ +--- +type: Fixed +pr: 3449 +--- +planning-config.md documented "light" as an allowed workflow.code_review_depth value, but config-set only accepts quick/standard/deep — the reference now matches the validator, pinned by a doc↔capability-registry parity test. The agent_skills row now also documents the array-of-strings form for assigning multiple skill sets to one agent type. diff --git a/gsd-core/references/planning-config.md b/gsd-core/references/planning-config.md index 9beedde7f..8b48b3e0f 100644 --- a/gsd-core/references/planning-config.md +++ b/gsd-core/references/planning-config.md @@ -275,7 +275,7 @@ Set via `workflow.*` namespace in config.json (e.g., `"workflow": { "research": | `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"` | `"light"`, `"standard"`, `"deep"` | Depth level for code review analysis in the ship workflow | +| `workflow.code_review_depth` | string | `"standard"` | `"quick"`, `"standard"`, `"deep"` | Depth level for code review analysis in the ship workflow | | `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`. | @@ -362,7 +362,7 @@ Set via `manager.*` namespace (e.g., `"manager": { "flags": { "discuss": "--auto |-----|------|---------|----------------|-------------| | `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 | +| `agent_skills` | object | `{}` | `{ "": "" }` or `{ "": ["", "", ...] }` | 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 diff --git a/tests/config-field-docs.test.cjs b/tests/config-field-docs.test.cjs index 9ff819c05..2630841ec 100644 --- a/tests/config-field-docs.test.cjs +++ b/tests/config-field-docs.test.cjs @@ -238,6 +238,82 @@ describe('config-field-docs', () => { }); }); +// ─── Capability-enum doc parity (#3303) ───────────────────────────────────── + +describe('capability-enum doc parity (#3303)', () => { + const CAPABILITY_PATH = path.join( + __dirname, '..', 'capabilities', 'code-review', 'capability.json' + ); + + let docContent; + let capability; + + before(() => { + docContent = fs.readFileSync(REFERENCE_PATH, 'utf-8'); + capability = JSON.parse(fs.readFileSync(CAPABILITY_PATH, 'utf-8')); + }); + + /** + * Extract a single Complete-Field-Reference table row by key and split it into + * trimmed cells: ['', '`key`', type, default, allowedValues, description, '']. + * Row-scoping keeps unrelated wording elsewhere in the doc from satisfying + * (or breaking) the parity assertions. + */ + function docRow(key) { + const line = docContent + .split(/\r?\n/) + .find(l => l.startsWith(`| \`${key}\` |`)); + assert.ok(line, `planning-config.md must have a table row for \`${key}\``); + return line.split('|').map(c => c.trim()); + } + + test('workflow.code_review_depth allowed values match the capability registry (#3303)', () => { + const spec = capability.config && capability.config['workflow.code_review_depth']; + assert.ok( + spec && Array.isArray(spec.values), + 'capabilities/code-review/capability.json must declare workflow.code_review_depth values' + ); + + const allowedCell = docRow('workflow.code_review_depth')[4]; + const docValues = [...allowedCell.matchAll(/"([^"]+)"/g)].map(m => m[1]); + assert.ok( + docValues.length > 0, + `workflow.code_review_depth Allowed-Values cell must list quoted values, got: ${allowedCell}` + ); + assert.deepStrictEqual( + [...docValues].sort(), + [...spec.values].sort(), + `planning-config.md workflow.code_review_depth allowed values (${docValues.join(', ')}) ` + + `must exactly match what config-set accepts per capability.json (${spec.values.join(', ')})` + ); + }); + + test('workflow.code_review_depth default matches the capability registry (#3303)', () => { + const spec = capability.config && capability.config['workflow.code_review_depth']; + assert.ok(spec, 'capability.json must declare workflow.code_review_depth'); + const defaultCell = docRow('workflow.code_review_depth')[3]; + const [docDefault] = [...defaultCell.matchAll(/"([^"]+)"/g)].map(m => m[1]); + assert.strictEqual( + docDefault, + spec.default, + `planning-config.md workflow.code_review_depth default must match capability.json default (${spec.default})` + ); + }); + + test('agent_skills row documents the array-of-strings form (#3303)', () => { + const cells = docRow('agent_skills'); + const allowedCell = cells[4]; + assert.ok( + /\[\s*""\s*,/.test(allowedCell), + `agent_skills Allowed-Values cell must show the array-of-strings form, got: ${allowedCell}` + ); + assert.ok( + /array/i.test(cells[5]), + 'agent_skills description must explain the array form assigns multiple skill sets to one agent type' + ); + }); +}); + // ─── CONFIGURATION.md parity (#1216) ──────────────────────────────────────── describe('CONFIGURATION.md parity (#1216)', () => {