Skip to content

fix(#2601): include 'inherit' in VALID_PROFILES and unify allowlists#2614

Open
ssmulders wants to merge 3 commits intogsd-build:mainfrom
ssmulders:fix/2601-config-set-model-profile-rejects-inherit
Open

fix(#2601): include 'inherit' in VALID_PROFILES and unify allowlists#2614
ssmulders wants to merge 3 commits intogsd-build:mainfrom
ssmulders:fix/2601-config-set-model-profile-rejects-inherit

Conversation

@ssmulders
Copy link
Copy Markdown

@ssmulders ssmulders commented Apr 23, 2026

Fix PR


Linked Issue

Fixes #2601


What was broken

gsd-sdk query config-set-model-profile inherit failed with "Invalid profile 'inherit'. Valid profiles: quality, balanced, budget, adaptive" — despite inherit being a supported meta-profile that the runtime getter and health validator both accept.

What this fix does

Adds 'inherit' to the shared VALID_PROFILES constant and replaces the hardcoded allowlist in validate.ts with that same constant, so all three consumers (setter, getter, health validator) agree on valid profiles.

Root cause

Three independent allowlists had drifted out of sync:

Source File Has inherit? Has adaptive?
VALID_PROFILES config-query.ts:54 No Yes
Health validator validate.ts:468 Yes No
Runtime getter config-query.ts:171 Yes (special-cased) N/A

VALID_PROFILES was derived from Object.keys(MODEL_PROFILES['gsd-planner']), and inherit cannot be a key there since its purpose is to bypass the map entirely. The health validator had a separate hardcoded list that included inherit but omitted adaptive.

Testing

How I verified the fix

  1. Ran npx vitest run on config-query, config-mutation, and validate test files — 82 tests pass
  2. New regression test 'accepts inherit profile' in config-mutation.test.ts calls configSetModelProfile(['inherit'], ...) and verifies it writes to config.json successfully

Regression test added?

  • Yes — added a test that would have caught this bug

Platforms tested

  • macOS
  • Windows (including backslash path handling)
  • Linux
  • N/A (not platform-specific)

Runtimes tested

  • Claude Code
  • Gemini CLI
  • OpenCode
  • Other: ___
  • N/A (not runtime-specific)

Checklist

  • Issue linked above with Fixes #NNNPR will be auto-closed if missing
  • Linked issue has the confirmed-bug label
  • Fix is scoped to the reported bug — no unrelated changes included
  • Regression test added (or explained why not)
  • All existing tests pass (npm test)
  • CHANGELOG.md updated if this is a user-facing fix
  • No unnecessary dependencies added

Breaking changes

None

Summary by CodeRabbit

  • Bug Fixes

    • The inherit model profile is now accepted during configuration updates and validated consistently across health checks.
  • Tests

    • New tests verify inherit is recognized as valid, returned by queries, and persisted correctly.
  • Documentation

    • Changelog updated to reflect the profile validation and behavior changes.

…lowlists

The 'inherit' meta-profile (bypass MODEL_PROFILES, use session model) was
accepted by the runtime getter and health validator but rejected by the
setter — the only path users actually invoke to change profiles.

Root cause: three independent allowlists had drifted out of sync.
VALID_PROFILES was derived solely from MODEL_PROFILES keys, which cannot
include 'inherit' since it intentionally bypasses that map. The health
validator had its own hardcoded list that included 'inherit' but omitted
'adaptive'.

Fix:
- Append 'inherit' to VALID_PROFILES after deriving from MODEL_PROFILES
- Replace hardcoded list in validate.ts with shared VALID_PROFILES import
- Add regression test for inherit acceptance in configSetModelProfile

Closes gsd-build#2601

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 33a9a24d-56bb-4bb6-9d4f-560ee1bf28c7

📥 Commits

Reviewing files that changed from the base of the PR and between 70ac3fa and ffd301a.

📒 Files selected for processing (2)
  • sdk/src/query/config-query.test.ts
  • sdk/src/query/config-query.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • sdk/src/query/config-query.test.ts

📝 Walkthrough

Walkthrough

Appends 'inherit' to the VALID_PROFILES list, updates health/config validation to use that unified constant, and adds tests plus a changelog entry so config-set-model-profile 'inherit' is accepted and persisted.

Changes

Cohort / File(s) Summary
Core Profile Validation
sdk/src/query/config-query.ts, sdk/src/query/validate.ts
VALID_PROFILES now includes 'inherit' (appended to keys from MODEL_PROFILES['gsd-planner']); validation in validate.ts uses the shared VALID_PROFILES and normalizes input before checking.
Config Mutations Tests
sdk/src/query/config-mutation.test.ts
Added test verifying configSetModelProfile('inherit') is accepted, returns expected response, and persists model_profile: "inherit".
Config Query Tests
sdk/src/query/config-query.test.ts
Updated tests to expect 'inherit' in VALID_PROFILES and to assert getAgentToModelMapForProfile('inherit') maps all agents to 'inherit'; also verifies non-inherit profiles map to concrete models.
Changelog
CHANGELOG.md
Added entry documenting the fix for config-set-model-profile rejecting 'inherit' despite runtime support.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Suggested labels

size/M

Suggested reviewers

  • glittercowboy

Poem

🐰 I hopped through keys both old and new,
Found "inherit" waiting in the dew —
I nudged the list, aligned the rest,
Now profiles settle where they're best. 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed Title accurately summarizes the main change: adding 'inherit' to VALID_PROFILES and unifying allowlists across three independent validation sources.
Description check ✅ Passed Description follows the fix template structure with clear sections: linked issue, what was broken, what the fix does, root cause analysis, testing verification, and checklist completion.
Linked Issues check ✅ Passed Changes fully address issue #2601 objectives: adds 'inherit' to VALID_PROFILES, unifies allowlists across setter/getter/validator, and includes regression test for the reported bug.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #2601: updating VALID_PROFILES constant, handling 'inherit' in getAgentToModelMapForProfile, replacing hardcoded allowlist in validate.ts, adding tests, and updating CHANGELOG.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@sdk/src/query/validate.ts`:
- Around line 468-469: The current guard `parsed.model_profile &&
!VALID_PROFILES.includes(parsed.model_profile as string)` skips empty-string
values and thus bypasses W004; change the check to explicitly detect presence
(e.g., ensure `parsed.model_profile` is not null/undefined and treat empty
string as invalid) before validating against `VALID_PROFILES`, then call
`addIssue('warning','W004',...)` when the value is present but not in
`VALID_PROFILES`; reference `parsed.model_profile`, `VALID_PROFILES`, and the
`addIssue` invocation so you update that conditional to reject empty strings (or
use a trimmed length check) rather than relying on truthiness.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0704e03f-dabd-43ed-9dde-541645dd14f9

📥 Commits

Reviewing files that changed from the base of the PR and between f30da83 and d608f4a.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • sdk/src/query/config-mutation.test.ts
  • sdk/src/query/config-query.test.ts
  • sdk/src/query/config-query.ts
  • sdk/src/query/validate.ts

Comment thread sdk/src/query/validate.ts Outdated
Address CodeRabbit review: the truthiness guard on parsed.model_profile
skipped empty-string values, allowing "" to bypass W004 validation.
Switch to explicit undefined check with trim/lowercase normalization.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Collaborator

@trek-e trek-e left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — one semantic defect, otherwise the fix is sound.

Finding — getAgentToModelMapForProfile does not handle inherit:

sdk/src/query/config-query.ts:59-67

export function getAgentToModelMapForProfile(normalizedProfile: string): Record<string, string> {
  const profile = VALID_PROFILES.includes(normalizedProfile) ? normalizedProfile : 'balanced';
  const agentToModelMap: Record<string, string> = {};
  for (const [agent, profileToModelMap] of Object.entries(MODEL_PROFILES)) {
    const mapped = profileToModelMap[profile] ?? profileToModelMap.balanced;
    agentToModelMap[agent] = mapped ?? 'sonnet';
  }
  return agentToModelMap;
}

After this PR, VALID_PROFILES.includes('inherit') is true, so profile = 'inherit', then profileToModelMap['inherit'] is always undefined (MODEL_PROFILES never had an inherit column), so every lookup falls through to profileToModelMap.balanced. Callers of configSetModelProfile with inherit get back agentToModelMap populated as if the user chose balanced.

This contradicts inherit semantics as implemented by resolveModel (sdk/src/query/config-query.ts:186-188), which correctly special-cases inherit and returns {model: 'inherit'}.

Callsite that exposes the defect: sdk/src/query/config-mutation.ts:329configSetModelProfile returns agentToModelMap in its response. A user running gsd-sdk query config-set-model-profile inherit --raw sees profile: 'inherit' but agentToModelMap: {gsd-planner: 'opus', ...}, which is misleading and would produce wrong behavior if any caller trusts that map instead of re-checking the profile.

Required fix: either

(a) early-return an empty map (or {agent: 'inherit' for each agent}) from getAgentToModelMapForProfile when normalizedProfile === 'inherit', mirroring the resolveModel special-case; or

(b) have configSetModelProfile omit agentToModelMap from the response payload when the profile is inherit.

(a) is safer — keeps the function shape consistent and lets every caller handle inherit uniformly. Add a regression test asserting the returned map is empty / sentinel when inherit is passed.

Rest of the diff (the VALID_PROFILES consolidation in config-query.ts, the validate.ts replacement of the hardcoded list, the CHANGELOG entry, the existing new test) all look correct.

@trek-e trek-e added the bug Something isn't working label Apr 23, 2026
getAgentToModelMapForProfile silently fell through to balanced when
called with 'inherit' — MODEL_PROFILES has no inherit column, so every
lookup hit the ?? fallback. Now early-returns a map with 'inherit' for
all agents, consistent with resolveModel's special-case.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

config-set-model-profile rejects 'inherit' despite runtime support

2 participants