* test(#4443): failing-first regression coverage for hooks.commit_types config key isValidConfigKey('hooks.commit_types') currently returns false and config-set hooks.commit_types rejects with "Unknown config key", because the key was never added to config-schema.manifest.json's validKeys when it shipped (#3811/#4340, 1.13.0) despite being documented (docs/COMMANDS.md) and consumed by hooks/gsd-validate-commit.sh. This commit adds the regression coverage only; the manifest fix lands in the next commit. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(#4443): avoid false-positive docs-guard registration trip The assert message for the new hooks.commit_types test mentioned "docs/COMMANDS.md" literally, which happened to land between two unrelated pre-existing backticks and tripped lint-docs-guard-registration.cjs's template-literal co-occurrence detector (a known, documented false-positive shape for that lint). Rephrased to drop the literal docs/ path from the message; the test's intent (documenting why the key must be valid) is unchanged. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * fix(#4443): register hooks.commit_types (and sibling hooks.community) in config schema config-schema.manifest.json's validKeys never got hooks.commit_types added when the feature shipped (#3811/#4340, 1.13.0) despite it being documented (docs/COMMANDS.md) and consumed by hooks/gsd-validate-commit.sh -- so config-set hooks.commit_types rejected with "Unknown config key", and the only way to configure a documented feature was hand-editing .planning/config.json. While auditing every hooks.* key actually read by shipped code against validKeys (CLAUDE.md's no-deferrals rule: a defect found anywhere in the tree while working an issue is fixed in the current change, not filed separately), hooks.community -- gsd-validate-commit.sh's own opt-in gate -- turned out to have the exact same gap. Both are added here; an audit of every hooks.* read site confirmed these are the only two missing entries. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(#4443): e2e coverage for hooks.community + changeset Closes the coverage-rigor gap the Standards review flagged: hooks.community had only a unit-level isValidConfigKey assertion, not the same real config-set CLI round-trip hooks.commit_types already got. Also adds the changeset fragment the same review flagged as a missing hard requirement. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * test(#4443): use PROBE_TIMEOUT_MS instead of a bare 15000 literal local/no-adhoc-timeout-literal (lint:ci) correctly flagged both new spawnSync calls' bare timeout: 15000 -- this call class (a short CLI probe against a temp fixture) is exactly what tests/helpers/timeouts.cjs's PROBE_TIMEOUT_MS documents. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> * docs(#4443): backfill changeset PR number Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> --------- Co-authored-by: sim <sim@local> Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
5
.changeset/sturdy-jays-cheer.md
Normal file
5
.changeset/sturdy-jays-cheer.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 4501
|
||||
---
|
||||
**`hooks.commit_types` and `hooks.community` are now settable via `config-set`** — both keys are consumed by shipped hooks (`hooks/gsd-validate-commit.sh`), and `hooks.commit_types` is documented in `docs/COMMANDS.md`, but neither was registered in `config-schema.manifest.json`'s `validKeys`, so `config-set` rejected them with "Unknown config key" — the only way to configure either was hand-editing `.planning/config.json`. (#4443)
|
||||
@@ -77,6 +77,8 @@
|
||||
"workflow.inline_plan_threshold",
|
||||
"hooks.context_warnings",
|
||||
"hooks.workflow_guard",
|
||||
"hooks.commit_types",
|
||||
"hooks.community",
|
||||
"workflow.context_coverage_gate",
|
||||
"statusline.show_last_command",
|
||||
"statusline.context_position",
|
||||
|
||||
@@ -24,6 +24,7 @@ const os = require('node:os');
|
||||
const path = require('node:path');
|
||||
const fc = require('./helpers/fast-check-setup.cjs');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const {
|
||||
isValidConfigKey,
|
||||
@@ -150,6 +151,22 @@ describe('config-schema: isValidConfigKey properties', () => {
|
||||
assert.equal(result, false, 'empty string must not be a valid config key');
|
||||
});
|
||||
|
||||
test('hooks.commit_types is a valid config key (#4443)', () => {
|
||||
assert.equal(
|
||||
isValidConfigKey('hooks.commit_types'),
|
||||
true,
|
||||
'hooks.commit_types is a documented, hook-consumed key (see the CLI reference) and must be settable via config-set'
|
||||
);
|
||||
});
|
||||
|
||||
test('hooks.community is a valid config key (sibling of #4443)', () => {
|
||||
assert.equal(
|
||||
isValidConfigKey('hooks.community'),
|
||||
true,
|
||||
'hooks.community is the gsd-validate-commit.sh opt-in gate and must be settable via config-set'
|
||||
);
|
||||
});
|
||||
|
||||
// Boundary: null/undefined/number return false (not throw, not true)
|
||||
test('null, undefined, number inputs return false', () => {
|
||||
assert.equal(isValidConfigKey(null), false);
|
||||
@@ -1232,3 +1249,47 @@ describe('feat-3210: workflow and config contracts', () => {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
describe('config-set: hooks.commit_types end-to-end (#4443)', () => {
|
||||
const { spawnSync } = require('node:child_process');
|
||||
|
||||
test('config-set hooks.commit_types accepts a JSON array and does not report Unknown config key', (t) => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4443-'));
|
||||
t.after(() => cleanup(dir));
|
||||
fs.mkdirSync(path.join(dir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, '.planning', 'config.json'), '{}\n');
|
||||
|
||||
const gsdTools = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[gsdTools, 'config-set', 'hooks.commit_types', '["enhance"]'],
|
||||
{ cwd: dir, encoding: 'utf8', timeout: PROBE_TIMEOUT_MS },
|
||||
);
|
||||
|
||||
assert.equal(result.status, 0, `config-set failed: ${result.stderr || result.stdout}`);
|
||||
assert.doesNotMatch(result.stdout + result.stderr, /Unknown config key/);
|
||||
|
||||
const written = JSON.parse(fs.readFileSync(path.join(dir, '.planning', 'config.json'), 'utf8'));
|
||||
assert.deepEqual(written.hooks.commit_types, ['enhance']);
|
||||
});
|
||||
|
||||
test('config-set hooks.community accepts a boolean and does not report Unknown config key', (t) => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-4443-community-'));
|
||||
t.after(() => cleanup(dir));
|
||||
fs.mkdirSync(path.join(dir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, '.planning', 'config.json'), '{}\n');
|
||||
|
||||
const gsdTools = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const result = spawnSync(
|
||||
process.execPath,
|
||||
[gsdTools, 'config-set', 'hooks.community', 'true'],
|
||||
{ cwd: dir, encoding: 'utf8', timeout: PROBE_TIMEOUT_MS },
|
||||
);
|
||||
|
||||
assert.equal(result.status, 0, `config-set failed: ${result.stderr || result.stdout}`);
|
||||
assert.doesNotMatch(result.stdout + result.stderr, /Unknown config key/);
|
||||
|
||||
const written = JSON.parse(fs.readFileSync(path.join(dir, '.planning', 'config.json'), 'utf8'));
|
||||
assert.equal(written.hooks.community, true);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user