diff --git a/get-shit-done/bin/lib/model-profiles.cjs b/get-shit-done/bin/lib/model-profiles.cjs index 64105ffe2..ae3472148 100644 --- a/get-shit-done/bin/lib/model-profiles.cjs +++ b/get-shit-done/bin/lib/model-profiles.cjs @@ -26,7 +26,7 @@ const MODEL_PROFILES = { 'gsd-doc-writer': { quality: 'opus', balanced: 'sonnet', budget: 'haiku', adaptive: 'sonnet' }, 'gsd-doc-verifier': { quality: 'sonnet', balanced: 'sonnet', budget: 'haiku', adaptive: 'haiku' }, }; -const VALID_PROFILES = Object.keys(MODEL_PROFILES['gsd-planner']); +const VALID_PROFILES = [...Object.keys(MODEL_PROFILES['gsd-planner']), 'inherit']; /** * Formats the agent-to-model mapping as a human-readable table (in string format). @@ -58,7 +58,9 @@ function formatAgentToModelMapAsTable(agentToModelMap) { function getAgentToModelMapForProfile(normalizedProfile) { const agentToModelMap = {}; for (const [agent, profileToModelMap] of Object.entries(MODEL_PROFILES)) { - agentToModelMap[agent] = profileToModelMap[normalizedProfile]; + agentToModelMap[agent] = normalizedProfile === 'inherit' + ? 'inherit' + : profileToModelMap[normalizedProfile]; } return agentToModelMap; } diff --git a/tests/bug-2601-inherit-model-profile.test.cjs b/tests/bug-2601-inherit-model-profile.test.cjs new file mode 100644 index 000000000..c80234784 --- /dev/null +++ b/tests/bug-2601-inherit-model-profile.test.cjs @@ -0,0 +1,49 @@ +'use strict'; + +/** + * Regression tests for bug #2601 + * + * `config-set-model-profile inherit` (and `config-set model_profile inherit`) + * was rejected by the validator even though the runtime accepts 'inherit' as a + * valid model_profile value meaning "inherit from parent configuration". + * + * Root cause: VALID_PROFILES in model-profiles.cjs is derived from + * Object.keys(MODEL_PROFILES['gsd-planner']), which does not include 'inherit'. + * cmdConfigSetModelProfile() rejects any value not in VALID_PROFILES. + */ + +const { describe, test } = require('node:test'); +const assert = require('node:assert/strict'); +const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs'); + +describe('bug #2601: config-set-model-profile accepts inherit', () => { + test('config-set-model-profile inherit succeeds', (t) => { + const tmpDir = createTempProject(); + t.after(() => cleanup(tmpDir)); + const result = runGsdTools(['config-set-model-profile', 'inherit'], tmpDir); + assert.ok(result.success, `should accept inherit: ${result.error}`); + }); + + test('config-set model_profile inherit succeeds', (t) => { + const tmpDir = createTempProject(); + t.after(() => cleanup(tmpDir)); + const result = runGsdTools(['config-set', 'model_profile', 'inherit'], tmpDir); + assert.ok(result.success, `config-set model_profile inherit should succeed: ${result.error}`); + }); + + test('config-set-model-profile inherit writes inherit to config', (t) => { + const tmpDir = createTempProject(); + t.after(() => cleanup(tmpDir)); + runGsdTools(['config-set-model-profile', 'inherit'], tmpDir); + const getResult = runGsdTools(['config-get', 'model_profile'], tmpDir); + assert.ok(getResult.success, `config-get should succeed: ${getResult.error}`); + assert.strictEqual(JSON.parse(getResult.output), 'inherit'); + }); + + test('config-set-model-profile still rejects truly invalid profiles', (t) => { + const tmpDir = createTempProject(); + t.after(() => cleanup(tmpDir)); + const result = runGsdTools(['config-set-model-profile', 'not-a-real-profile'], tmpDir); + assert.ok(!result.success, 'should reject invalid profiles'); + }); +}); diff --git a/tests/model-profiles.test.cjs b/tests/model-profiles.test.cjs index 03d36f7d7..ca06fa9f7 100644 --- a/tests/model-profiles.test.cjs +++ b/tests/model-profiles.test.cjs @@ -65,13 +65,16 @@ describe('MODEL_PROFILES', () => { // ─── VALID_PROFILES ─────────────────────────────────────────────────────────── describe('VALID_PROFILES', () => { - test('contains quality, balanced, and budget', () => { - assert.deepStrictEqual(VALID_PROFILES.sort(), ['adaptive', 'balanced', 'budget', 'quality']); + test('contains quality, balanced, budget, adaptive, and inherit', () => { + assert.deepStrictEqual(VALID_PROFILES.sort(), ['adaptive', 'balanced', 'budget', 'inherit', 'quality']); }); - test('is derived from MODEL_PROFILES keys', () => { + test('includes all MODEL_PROFILES keys plus inherit', () => { const fromData = Object.keys(MODEL_PROFILES['gsd-planner']); - assert.deepStrictEqual(VALID_PROFILES.sort(), fromData.sort()); + for (const profile of fromData) { + assert.ok(VALID_PROFILES.includes(profile), `VALID_PROFILES should include ${profile}`); + } + assert.ok(VALID_PROFILES.includes('inherit'), 'VALID_PROFILES should include inherit'); }); });