fix(#1747): register four search-provider keys in the config schema (#1814)

* fix(#1747): register four search-provider keys in the config schema

buildNewProjectConfig emits seven search-provider availability flags and
research-provider.cts providerAvailability() consumes all seven, but only
three were registered in VALID_CONFIG_KEYS (config-schema.manifest.json).
config-loader.cts then printed an 'unknown config key(s)' warning for the
four unregistered keys (tavily_search, ref_search, perplexity, jina) on
every freshly generated .planning/config.json.

Register the four missing keys in the schema manifest and document them
alongside brave/exa/firecrawl in CONFIGURATION.md. Add a regression test
plus a structural drift guard that requires every config-driven
research-provider flag to be in VALID_CONFIG_KEYS, so a future provider
addition cannot silently reintroduce the drift.

* fix(#1747): move regression into owning test file + add changeset

lint-regression-test-names rejects new bug-NNNN-*.test.cjs files; move the
#1747 regression (four provider keys in VALID_CONFIG_KEYS + provider-flag
drift guard) into tests/bug-2530-valid-config-keys.test.cjs, the canonical
home for VALID_CONFIG_KEYS regressions, and delete the standalone file.

Add the missing .changeset fragment — config-schema.manifest.json lives
under gsd-core/ (user-facing), so changeset-lint requires a fragment.

* test(#1747): regenerate golden-install-parity fixtures for schema change

Adding four provider keys to config-schema.manifest.json shifts its shipped
content hash (65dea848 -> 7d398e94); recapture all 16 runtime fixtures via
UPDATE_GOLDEN=1. Each fixture changes exactly one line — the manifest hash.
This commit is contained in:
Tom Boucher
2026-06-28 22:42:36 -04:00
committed by GitHub
parent 2d314c3a28
commit fd576528a7
20 changed files with 78 additions and 16 deletions

View File

@@ -0,0 +1,5 @@
---
type: Fixed
pr: 1814
---
**`/gsd-settings` no longer warns about four search-provider keys on fresh projects (#1747)** — `buildNewProjectConfig` emits seven search-provider availability flags and `research-provider.cts` `providerAvailability()` consumes all seven, but only three were registered in `VALID_CONFIG_KEYS` (`config-schema.manifest.json`). Running `/gsd-settings` on a freshly generated `.planning/config.json` printed `unknown config key(s) … tavily_search, ref_search, perplexity, jina — these will be ignored` even though the user never hand-edited the config. The four missing keys are now registered alongside `brave_search`/`firecrawl`/`exa_search` and documented in `docs/CONFIGURATION.md`; a drift guard in `tests/bug-2530-valid-config-keys.test.cjs` now requires every config-driven research-provider flag to be in the schema, so a future provider addition cannot reintroduce the drift.

View File

@@ -171,6 +171,10 @@ GSD stores project settings in `.planning/config.json`. Created during `/gsd-new
| `brave_search` | boolean | `true`/`false` | auto-detected | Override auto-detection of Brave Search API availability. When unset, GSD checks for `BRAVE_API_KEY` env var or `~/.gsd/brave_api_key` file |
| `firecrawl` | boolean | `true`/`false` | auto-detected | Override auto-detection of Firecrawl API availability. When unset, GSD checks for `FIRECRAWL_API_KEY` env var or `~/.gsd/firecrawl_api_key` file |
| `exa_search` | boolean | `true`/`false` | auto-detected | Override auto-detection of Exa Search API availability. When unset, GSD checks for `EXA_API_KEY` env var or `~/.gsd/exa_api_key` file |
| `tavily_search` | boolean | `true`/`false` | auto-detected | Override auto-detection of Tavily Search API availability. When unset, GSD checks for `TAVILY_API_KEY` env var or `~/.gsd/tavily_api_key` file |
| `ref_search` | boolean | `true`/`false` | auto-detected | Override auto-detection of Ref search API availability. When unset, GSD checks for `REF_API_KEY` env var or `~/.gsd/ref_api_key` file |
| `perplexity` | boolean | `true`/`false` | auto-detected | Override auto-detection of Perplexity API availability. When unset, GSD checks for `PERPLEXITY_API_KEY` env var or `~/.gsd/perplexity_api_key` file |
| `jina` | boolean | `true`/`false` | `true` | Override auto-detection of Jina API availability. Jina is a terminal fallback in the docs waterfall and defaults to available (`true`); GSD checks for `JINA_API_KEY` env var or `~/.gsd/jina_api_key` file when an explicit override is needed |
| `search_gitignored` | boolean | `true`/`false` | `false` | Legacy top-level alias for `planning.search_gitignored`. Prefer the namespaced form; this alias is accepted for backward compatibility |
> **Note:** `granularity` was renamed from `depth` in v1.22.3. Existing configs are auto-migrated.
@@ -190,6 +194,10 @@ API key fields accept a string value (the key itself). They can also be set to t
| `brave_search` | string \| boolean \| null | `null` | Brave Search API key used for web research. Displayed as `****<last-4>` in all UI / `config-set` output; never echoed plaintext |
| `firecrawl` | string \| boolean \| null | `null` | Firecrawl API key for deep-crawl scraping. Masked in display |
| `exa_search` | string \| boolean \| null | `null` | Exa Search API key for semantic search. Masked in display |
| `tavily_search` | string \| boolean \| null | `null` | Tavily Search API key used in the web-discovery waterfall. Masked in display |
| `ref_search` | string \| boolean \| null | `null` | Ref search API key used in the docs-discovery waterfall. Masked in display |
| `perplexity` | string \| boolean \| null | `null` | Perplexity API key used in the web-discovery waterfall. Masked in display |
| `jina` | string \| boolean \| null | `null` | Jina API key (docs / scrape fallback). Masked in display |
**Masking convention (`gsd-core/bin/lib/secrets.cjs`):** keys 8+ characters render as `****<last-4>`; shorter keys render as `****`; `null`/empty renders as `(unset)`. Plaintext is written as-is to `.planning/config.json` — that file is the security boundary — but the CLI, confirmation tables, logs, and `AskUserQuestion` descriptions never display the plaintext. This applies to the `config-set` command output itself: `config-set brave_search <key>` returns a JSON payload with the value masked.

View File

@@ -10,6 +10,10 @@
"brave_search",
"firecrawl",
"exa_search",
"tavily_search",
"ref_search",
"perplexity",
"jina",
"workflow.plan_check",
"workflow.verifier",
"workflow.auto_advance",

View File

@@ -9,6 +9,9 @@
* #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
* #1747 — buildNewProjectConfig emits four search-provider keys (tavily_search, ref_search,
* perplexity, jina) that research-provider.cts consumes but were missing from
* VALID_CONFIG_KEYS, causing /gsd-settings unknown-key warnings on fresh projects
*/
const { describe, test } = require('node:test');
@@ -74,6 +77,48 @@ describe('VALID_CONFIG_KEYS correctness', () => {
});
});
describe('#1747: new-project config emits only schema-recognized provider keys', () => {
// buildNewProjectConfig emits seven search-provider availability flags and
// research-provider.cts providerAvailability() consumes all seven, but only
// three were in VALID_CONFIG_KEYS → /gsd-settings warned on the four
// unregistered keys (tavily_search, ref_search, perplexity, jina) for every
// freshly generated .planning/config.json.
// The four keys that were emitted + consumed but missing from the schema.
const MISSING_KEYS = ['tavily_search', 'ref_search', 'perplexity', 'jina'];
// Every config-driven provider flag read by providerAvailability() in
// src/research-provider.cts. context7/websearch are excluded: hardcoded
// `true`, not config-gated, so no config key to register.
const PROVIDER_CONFIG_KEYS = [
'brave_search',
'firecrawl',
'exa_search',
'tavily_search',
'ref_search',
'perplexity',
'jina',
];
test('the four previously-missing provider keys are in VALID_CONFIG_KEYS', () => {
const absent = MISSING_KEYS.filter((k) => !VALID_CONFIG_KEYS.has(k));
assert.deepStrictEqual(
absent,
[],
`These provider keys are emitted by buildNewProjectConfig and consumed by research-provider.cts but missing from VALID_CONFIG_KEYS:\n ${absent.join('\n ')}\n\nAdd them to gsd-core/bin/shared/config-schema.manifest.json (validKeys).`
);
});
test('every config-driven research-provider flag is registered in the schema (drift guard)', () => {
const drifted = PROVIDER_CONFIG_KEYS.filter((k) => !VALID_CONFIG_KEYS.has(k));
assert.deepStrictEqual(
drifted,
[],
`These research-provider config flags are not in VALID_CONFIG_KEYS — a fresh /gsd-new-project config would trigger an unknown-key warning under /gsd-settings:\n ${drifted.join('\n ')}\n\nWhen you add a provider to providerAvailability() in src/research-provider.cts, also register its config key in gsd-core/bin/shared/config-schema.manifest.json.`
);
});
});
describe('ADR-857 Phase 6 capability config ownership', () => {
test('migrated capability config keys are valid through the registry, not central schema residue', () => {
const capabilityKeys = Object.keys(capabilityRegistry.configSchema || {}).sort();

View File

@@ -41,7 +41,7 @@
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "8bc541aabc2e143c",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -40,7 +40,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -44,7 +44,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -76,7 +76,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -42,7 +42,7 @@
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "10226e9512dd44bf",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -41,7 +41,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -77,7 +77,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -110,7 +110,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -41,7 +41,7 @@
"gsd-core/bin/gsd-tools.cjs": "74594e8bf36e5580",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -41,7 +41,7 @@
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",

View File

@@ -41,7 +41,7 @@
"gsd-core/bin/gsd-tools.cjs": "5cf26d5f9e588cf8",
"gsd-core/bin/gsd_run": "62d9b647ede212e6",
"gsd-core/bin/shared/config-defaults.manifest.json": "517e6a7c1e9f4f16",
"gsd-core/bin/shared/config-schema.manifest.json": "65dea848d50969a2",
"gsd-core/bin/shared/config-schema.manifest.json": "7d398e94c44e5268",
"gsd-core/bin/shared/model-catalog.json": "dbe26e683236d8c6",
"gsd-core/bin/shared/runtime-aliases.manifest.json": "f6c8b3af10dcfdc4",
"gsd-core/bin/verify-reapply-patches.cjs": "caec5dbce11e3904",