Merge pull request #3188 from gsd-build/fix/3162-config-set-missing-keys
fix(config): add resolve_model_ids to VALID_CONFIG_KEYS; accept workflow._auto_chain_active via RUNTIME_STATE_KEYS
This commit is contained in:
5
.changeset/wise-mice-cheer.md
Normal file
5
.changeset/wise-mice-cheer.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
type: Fixed
|
||||
pr: 3188
|
||||
---
|
||||
config-set resolve_model_ids no longer rejected with "Unknown config key"; workflow._auto_chain_active written by workflows no longer emits spurious key-validation errors. Fixes #3162.
|
||||
@@ -309,6 +309,15 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
||||
pre-existing sentinel force-removes the orphan worktree before starting fresh, making
|
||||
the agent self-healing across crashes. (#2839)
|
||||
|
||||
- **`config-set resolve_model_ids` no longer rejected** — `resolve_model_ids` was
|
||||
documented in CONFIGURATION.md and read by model-resolution paths, but missing from
|
||||
the CJS/SDK `VALID_CONFIG_KEYS` allowlists. Added to both. (#3162)
|
||||
- **`config-set workflow._auto_chain_active` no longer emits spurious errors** — this
|
||||
internal runtime-state key is written by `plan-phase`, `execute-phase`,
|
||||
`discuss-phase`, `transition`, and `new-project` workflows via `config-set`, but was
|
||||
excluded from the public allowlist after #2530. A new `RUNTIME_STATE_KEYS` set lets
|
||||
`isValidConfigKey()` accept it without exposing it as a user-settable option. (#3162)
|
||||
|
||||
|
||||
## [1.39.1] - 2026-05-01
|
||||
|
||||
|
||||
@@ -69,6 +69,18 @@ const VALID_CONFIG_KEYS = new Set([
|
||||
'claude_md_assembly.mode',
|
||||
// #2517 — runtime-aware model profiles
|
||||
'runtime',
|
||||
// #3162 — documented top-level key: controls model ID resolution for non-Claude runtimes
|
||||
'resolve_model_ids',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Internal runtime-state keys — accepted by config-set (workflows write them) but not
|
||||
* exposed as user-settable options. Excluded from VALID_CONFIG_KEYS so they stay out of
|
||||
* the public docs-parity check and the "Valid keys:" error message.
|
||||
* See: #3162 (workflow._auto_chain_active written by plan/execute/discuss workflows)
|
||||
*/
|
||||
const RUNTIME_STATE_KEYS = new Set([
|
||||
'workflow._auto_chain_active',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -98,11 +110,12 @@ const DYNAMIC_KEY_PATTERNS = [
|
||||
];
|
||||
|
||||
/**
|
||||
* Returns true if keyPath is a valid config key (exact or dynamic pattern).
|
||||
* Returns true if keyPath is a valid config key (exact, dynamic pattern, or runtime state).
|
||||
*/
|
||||
function isValidConfigKey(keyPath) {
|
||||
if (VALID_CONFIG_KEYS.has(keyPath)) return true;
|
||||
if (RUNTIME_STATE_KEYS.has(keyPath)) return true;
|
||||
return DYNAMIC_KEY_PATTERNS.some((p) => p.test(keyPath));
|
||||
}
|
||||
|
||||
module.exports = { VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS, isValidConfigKey };
|
||||
module.exports = { VALID_CONFIG_KEYS, RUNTIME_STATE_KEYS, DYNAMIC_KEY_PATTERNS, isValidConfigKey };
|
||||
|
||||
@@ -23,7 +23,7 @@ import { homedir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { GSDError, ErrorClassification } from '../errors.js';
|
||||
import { VALID_PROFILES, getAgentToModelMapForProfile } from './config-query.js';
|
||||
import { VALID_CONFIG_KEYS, DYNAMIC_KEY_PATTERNS } from './config-schema.js';
|
||||
import { VALID_CONFIG_KEYS, RUNTIME_STATE_KEYS, DYNAMIC_KEY_PATTERNS } from './config-schema.js';
|
||||
import { planningPaths } from './helpers.js';
|
||||
import { acquireStateLock, releaseStateLock } from './state-mutation.js';
|
||||
import { maskIfSecret } from './secrets.js';
|
||||
@@ -86,6 +86,7 @@ const CONFIG_KEY_SUGGESTIONS: Record<string, string> = {
|
||||
*/
|
||||
export function isValidConfigKey(keyPath: string): { valid: boolean; suggestion?: string } {
|
||||
if (VALID_CONFIG_KEYS.has(keyPath)) return { valid: true };
|
||||
if (RUNTIME_STATE_KEYS.has(keyPath)) return { valid: true };
|
||||
|
||||
// Dynamic patterns — all sourced from shared config-schema (#2653).
|
||||
// Covers agent_skills.*, review.models.*, features.*,
|
||||
|
||||
@@ -71,6 +71,16 @@ export const VALID_CONFIG_KEYS: ReadonlySet<string> = new Set([
|
||||
'claude_md_assembly.mode',
|
||||
// #2517 — runtime-aware model profiles
|
||||
'runtime',
|
||||
// #3162 — documented top-level key: controls model ID resolution for non-Claude runtimes
|
||||
'resolve_model_ids',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Internal runtime-state keys accepted by config-set workflows but not exposed
|
||||
* as user-facing config options.
|
||||
*/
|
||||
export const RUNTIME_STATE_KEYS: ReadonlySet<string> = new Set([
|
||||
'workflow._auto_chain_active',
|
||||
]);
|
||||
|
||||
/**
|
||||
@@ -125,8 +135,9 @@ export const DYNAMIC_KEY_PATTERNS: readonly DynamicKeyPattern[] = [
|
||||
},
|
||||
];
|
||||
|
||||
/** Returns true if keyPath is a valid config key (exact or dynamic pattern). */
|
||||
/** Returns true if keyPath is a valid config key (exact, runtime-state, or dynamic pattern). */
|
||||
export function isValidConfigKeyPath(keyPath: string): boolean {
|
||||
if (VALID_CONFIG_KEYS.has(keyPath)) return true;
|
||||
if (RUNTIME_STATE_KEYS.has(keyPath)) return true;
|
||||
return DYNAMIC_KEY_PATTERNS.some((p) => p.test(keyPath));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
* #2532 — workflow.ui_review is used in autonomous.md but missing from VALID_CONFIG_KEYS
|
||||
* #2533 — workflow.max_discuss_passes is used in discuss-phase.md but missing from VALID_CONFIG_KEYS
|
||||
* #2535 — sub_repos and plan_checker legacy keys need CONFIG_KEY_SUGGESTIONS migration hints
|
||||
* #3162 — resolve_model_ids missing from VALID_CONFIG_KEYS; workflow._auto_chain_active must be
|
||||
* accepted by isValidConfigKey (written by workflows) without being user-visible
|
||||
*/
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
@@ -14,7 +16,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const { createTempProject, cleanup, runGsdTools } = require('./helpers.cjs');
|
||||
|
||||
const { VALID_CONFIG_KEYS } = require('../get-shit-done/bin/lib/config-schema.cjs');
|
||||
const { VALID_CONFIG_KEYS, isValidConfigKey } = require('../get-shit-done/bin/lib/config-schema.cjs');
|
||||
|
||||
describe('VALID_CONFIG_KEYS correctness', () => {
|
||||
test('#2530: workflow._auto_chain_active must not be in VALID_CONFIG_KEYS (internal state)', () => {
|
||||
@@ -44,6 +46,21 @@ describe('VALID_CONFIG_KEYS correctness', () => {
|
||||
'workflow.max_discuss_passes is read in discuss-phase.md via gsd-sdk query config-get'
|
||||
);
|
||||
});
|
||||
|
||||
test('#3162: resolve_model_ids must be in VALID_CONFIG_KEYS (documented user-facing key)', () => {
|
||||
assert.ok(
|
||||
VALID_CONFIG_KEYS.has('resolve_model_ids'),
|
||||
'resolve_model_ids is documented in CONFIGURATION.md and read by core.cjs/session-runner.ts'
|
||||
);
|
||||
});
|
||||
|
||||
test('#3162: workflow._auto_chain_active must be accepted by isValidConfigKey (written by workflows)', () => {
|
||||
assert.strictEqual(
|
||||
isValidConfigKey('workflow._auto_chain_active'),
|
||||
true,
|
||||
'workflow._auto_chain_active is written by plan-phase, execute-phase, discuss-phase, transition workflows via config-set'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CONFIG_KEY_SUGGESTIONS migration hints (#2535)', () => {
|
||||
|
||||
@@ -23,18 +23,22 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const { VALID_CONFIG_KEYS: CJS_KEYS, DYNAMIC_KEY_PATTERNS: CJS_PATTERNS } =
|
||||
const {
|
||||
VALID_CONFIG_KEYS: CJS_KEYS,
|
||||
RUNTIME_STATE_KEYS: CJS_RUNTIME_KEYS,
|
||||
DYNAMIC_KEY_PATTERNS: CJS_PATTERNS,
|
||||
} =
|
||||
require('../get-shit-done/bin/lib/config-schema.cjs');
|
||||
|
||||
const SDK_SCHEMA_PATH = path.join(ROOT, 'sdk', 'src', 'query', 'config-schema.ts');
|
||||
const SDK_SRC = fs.readFileSync(SDK_SCHEMA_PATH, 'utf8');
|
||||
|
||||
function extractSdkKeys(src) {
|
||||
const start = src.indexOf('VALID_CONFIG_KEYS');
|
||||
assert.ok(start > -1, 'SDK config-schema.ts must export VALID_CONFIG_KEYS');
|
||||
function extractSdkSet(src, setName) {
|
||||
const start = src.indexOf(setName);
|
||||
assert.ok(start > -1, `SDK config-schema.ts must export ${setName}`);
|
||||
const setOpen = src.indexOf('new Set([', start);
|
||||
const setClose = src.indexOf('])', setOpen);
|
||||
assert.ok(setOpen > -1 && setClose > -1, 'VALID_CONFIG_KEYS must be a new Set([...]) literal');
|
||||
assert.ok(setOpen > -1 && setClose > -1, `${setName} must be a new Set([...]) literal`);
|
||||
const body = src.slice(setOpen + 'new Set(['.length, setClose);
|
||||
const keys = new Set();
|
||||
for (const match of body.matchAll(/'([^']+)'/g)) keys.add(match[1]);
|
||||
@@ -52,7 +56,7 @@ function extractSdkPatternSources(src) {
|
||||
}
|
||||
|
||||
test('#2653 — SDK VALID_CONFIG_KEYS matches CJS VALID_CONFIG_KEYS', () => {
|
||||
const sdkKeys = extractSdkKeys(SDK_SRC);
|
||||
const sdkKeys = extractSdkSet(SDK_SRC, 'VALID_CONFIG_KEYS');
|
||||
const missingInSdk = [...CJS_KEYS].filter((k) => !sdkKeys.has(k));
|
||||
const extraInSdk = [...sdkKeys].filter((k) => !CJS_KEYS.has(k));
|
||||
assert.deepStrictEqual(
|
||||
@@ -69,6 +73,24 @@ test('#2653 — SDK VALID_CONFIG_KEYS matches CJS VALID_CONFIG_KEYS', () => {
|
||||
);
|
||||
});
|
||||
|
||||
test('#3162 — SDK RUNTIME_STATE_KEYS matches CJS RUNTIME_STATE_KEYS', () => {
|
||||
const sdkRuntimeKeys = extractSdkSet(SDK_SRC, 'RUNTIME_STATE_KEYS');
|
||||
const missingInSdk = [...CJS_RUNTIME_KEYS].filter((k) => !sdkRuntimeKeys.has(k));
|
||||
const extraInSdk = [...sdkRuntimeKeys].filter((k) => !CJS_RUNTIME_KEYS.has(k));
|
||||
assert.deepStrictEqual(
|
||||
missingInSdk,
|
||||
[],
|
||||
'CJS runtime-state keys missing from sdk/src/query/config-schema.ts:\n' +
|
||||
missingInSdk.map((k) => ' ' + k).join('\n'),
|
||||
);
|
||||
assert.deepStrictEqual(
|
||||
extraInSdk,
|
||||
[],
|
||||
'SDK runtime-state keys missing from get-shit-done/bin/lib/config-schema.cjs:\n' +
|
||||
extraInSdk.map((k) => ' ' + k).join('\n'),
|
||||
);
|
||||
});
|
||||
|
||||
test('#2653 — SDK DYNAMIC_KEY_PATTERNS sources match CJS regex .source', () => {
|
||||
const sdkSources = new Set(extractSdkPatternSources(SDK_SRC));
|
||||
const cjsSources = CJS_PATTERNS.map((p) => {
|
||||
|
||||
Reference in New Issue
Block a user