feat(routing): match model config names spelling-agnostically - #478
Merged
Conversation
The router canonicalizes an inbound model name (dots→dashes for known families: `gpt-5.5` → `gpt-5-5`) before routing, but the `[[models]]` lookup compared config names verbatim. So a config entry spelled `gpt-5.5` never matched a request the router had rewritten to `gpt-5-5`, and the turn failed with "Model 'gpt-5-5' is not configured" — even though the operator had clearly configured it. Routing was agnostic to the spelling for the request but not for the config. Canonicalizes the config side of every model-name comparison: - `AppState::model_index` is keyed by the canonical name, and `find_model` canonicalizes its query — the authoritative `[[models]]` resolution. - `ProviderRegistry::model_to_provider` (the backward-compat direct lookup) does the same. - The router's auto-map guard compares canonical config names, so a dotted explicit entry still takes precedence instead of being auto-mapped away. Canonicalization borrows unchanged for names outside a known family, so configs that already use canonical IDs are untouched. Verified live against Codex OAuth with a config entry named `gpt-5.5` (dotted): before (0.36.79): "Model 'gpt-5-5' is not configured" after: routes, returns "OK" Tests: `provider_for_model` resolves both `gpt-5.5` and `gpt-5-5` for a dotted config entry. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Destynova2
enabled auto-merge
July 26, 2026 07:32
Contributor
Mutation testing (PR diff sample)Informational — never blocks merge. Full matrix runs on main.
Legend: clean (no survivors), missed (inspect artifact), timed-out (25 min cap reached). Artifact: mutants-pr-results-05d8c0f867f69ae21bf2bb42a713cf0210c1ff87. |
This was referenced Jul 26, 2026
Destynova2
added a commit
that referenced
this pull request
Jul 26, 2026
## The CI/CD bug this fixes `feat(routing)` (#478) merged cleanly but **never produced a release**. release-plz kept reporting `grob: no commit matches the release_commits regex` and there was no way to cut v0.36.84. ### Root cause Every PR was merged with a **merge commit** (`gh pr merge --merge`), so the feature commit sits on the merge's **second parent**: ``` 572f539 Merge #478 parents: 95db3f6 (release v0.36.83) de4b690 (feat routing) ``` When release-plz cut v0.36.83 **between** the routing branch's creation and its merge, `de4b690` ended up *beside* the release commit rather than after it. Its version walk over `v0.36.83..HEAD` doesn't traverse into that second parent — `git log --first-parent v0.36.83..main` is **empty** — so the `feat` commit is invisible to the bump calc. It works for most PRs only because they're released immediately after merging, before the next merge buries them. ## Fix **Squash-merge only.** Repo settings now: - `allow_merge_commit = false` - `squash_merge_commit_title = PR_TITLE` Every PR becomes one conventional commit on a **linear** `main`, which release-plz reads reliably — the second-parent hiding place no longer exists. Applied to the repo already; this PR aligns CLAUDE.md (`--auto --merge` → `--auto --squash`, plus a note on why the PR title must be a conventional commit) and documents the manual `gh workflow run release-plz.yml` trigger added in #479. ## For the currently-stranded routing fix It's on `main` and ships with the next release cut for any releasable commit (now reliably, thanks to squash). Meanwhile it has a trivial workaround — name the config entry `gpt-5-5` instead of `gpt-5.5`. ## Note This PR is the first squash-only merge — its single commit validates the new policy end to end. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The friction
The router canonicalizes an inbound model name — dots→dashes for known families,
so
gpt-5.5becomesgpt-5-5— before routing. But the[[models]]lookupthen compared config names verbatim. A config entry spelled
gpt-5.5nevermatched the request the router had just rewritten to
gpt-5-5, and the turn diedwith:
…even though the operator had configured it. Routing was spelling-agnostic for
the request but not for the config — the mismatch reported with
K4DI_VISION_MODEL=gpt-5.5.Fix — make the config side agnostic too, like text
Canonicalize the config side of every model-name comparison, so both ends are
compared in canonical form:
AppState::model_indexkey +find_modelqueryto_lowercase()canonicalize(...).to_lowercase()ProviderRegistry::model_to_providerkey +provider_for_modelquerym.name == request.model)canonicalize_model_nameborrows unchanged for names outside a known family(
glm-4.6,mistral-3.1, …), so configs already using canonical IDs areuntouched — this only adds matches, never removes them.
Verified live (Codex OAuth, config entry named
gpt-5.5with a dot)Unit test:
provider_for_modelresolves bothgpt-5.5andgpt-5-5for adotted
[[models]]entry. Full local prek gate — clean.Note
This closes the last of the three points from the vision report:
gpt-5.5not routable — this PRIndependent files from #476; no conflict.
🤖 Generated with Claude Code