feat(converters): centralize model field normalization across targets#442
Conversation
Each target platform expects a different format for the model frontmatter field, but converters handled this inconsistently — some passed through raw values, others had duplicated normalization with wrong aliases. Extracts shared utilities into src/utils/model.ts (CLAUDE_FAMILY_ALIASES, normalizeModelWithProvider, addProviderPrefix) and applies per-target behavior based on external research: - OpenCode/Qwen/OpenClaw: resolve aliases + provider prefix - Droid: pass-through (Factory natively resolves bare aliases) - Copilot: drop (uncertain display-name format) - Codex: drop (skill frontmatter doesn't support model) Fixes Qwen's wrong alias mapping (claude-sonnet → claude-sonnet-4-6) and adds test coverage for all target model behaviors. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 00f3e33a75
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
The Copilot spec documents model as a supported frontmatter field (docs/specs/copilot.md). Dropping it silently removes user intent when they explicitly set a model. Restore pass-through to preserve the source agent's model choice. Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
…ormat
Reverts pass-through from the previous commit. Copilot supports a model
field but expects display names ("Claude Opus 4.5"), not Claude model IDs
or bare aliases. Unlike Droid (which natively resolves "sonnet"), Copilot
has no documented resolution for Claude-specific values. Emitting an
unrecognized value is worse than omitting the field (which falls back to
the platform default).
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
|
Re: the Copilot model field discussion — I initially restored pass-through per the review feedback, but on further consideration reverted it. The issue: Copilot's The distinction from Droid: Factory explicitly documents that it accepts If Copilot model mapping becomes needed later, it would require a dedicated |
Summary
Each target platform expects a different format for the
modelfrontmatter field, but converters handled this inconsistently — Qwen had wrong alias mappings (claude-sonnetinstead ofclaude-sonnet-4-6), OpenClaw and Copilot passed through raw values, and there were two divergent copies ofCLAUDE_FAMILY_ALIASESacross OpenCode and Qwen.This PR extracts shared model normalization into
src/utils/model.tsand applies per-target behavior based on external research into each platform's model field format:model: sonnetoutputanthropic/claude-sonnet-4-6anthropic/claude-sonnet-4-6anthropic/claude-sonnet-4-6sonnet(pass-through)nameanddescriptionKey decisions
sonnet,opus,haikunatively and resolves them internally. No normalization needed.SkillFrontmatterstruct) that skill frontmatter only supportsnameanddescription. Model is set globally inconfig.toml.sonnet→claude-sonnet(wrong). Now uses shared aliases mapping toclaude-sonnet-4-6.Test plan
tests/model-utils.test.tscovers all shared utility functions including provider prefix routing forclaude-*,gpt-*,gemini-*,qwen-*, ando3-*patterns🤖 Generated with Claude Opus 4.6 (1M context, extended thinking) via Claude Code