Thanks to visit codestin.com
Credit goes to github.com

Skip to content

feat(converters): centralize model field normalization across targets#442

Merged
tmchow merged 3 commits into
mainfrom
feat/codex-model-mapping
Mar 29, 2026
Merged

feat(converters): centralize model field normalization across targets#442
tmchow merged 3 commits into
mainfrom
feat/codex-model-mapping

Conversation

@tmchow
Copy link
Copy Markdown
Collaborator

@tmchow tmchow commented Mar 29, 2026

Summary

Each target platform expects a different format for the model frontmatter field, but converters handled this inconsistently — Qwen had wrong alias mappings (claude-sonnet instead of claude-sonnet-4-6), OpenClaw and Copilot passed through raw values, and there were two divergent copies of CLAUDE_FAMILY_ALIASES across OpenCode and Qwen.

This PR extracts shared model normalization into src/utils/model.ts and applies per-target behavior based on external research into each platform's model field format:

Target model: sonnet output Rationale
OpenCode anthropic/claude-sonnet-4-6 Multi-provider, needs provider prefix
Qwen anthropic/claude-sonnet-4-6 Multi-provider (supports Anthropic via settings.json)
OpenClaw anthropic/claude-sonnet-4-6 Multi-provider, needs provider prefix
Droid sonnet (pass-through) Factory natively resolves bare aliases
Copilot dropped Supports model but format is display names, not IDs
Codex dropped Skill frontmatter only supports name and description

Key decisions

  • Droid pass-through: Factory docs confirm it accepts sonnet, opus, haiku natively and resolves them internally. No normalization needed.
  • Copilot drop: Copilot supports Claude models but uses display-name format ("Claude Opus 4.5") rather than model IDs. Omitting is safer than guessing the format; docs say "If unset, inherits the default model."
  • Codex drop: Confirmed via Rust source (SkillFrontmatter struct) that skill frontmatter only supports name and description. Model is set globally in config.toml.
  • Qwen fix: Old code mapped sonnetclaude-sonnet (wrong). Now uses shared aliases mapping to claude-sonnet-4-6.

Test plan

  • New tests/model-utils.test.ts covers all shared utility functions including provider prefix routing for claude-*, gpt-*, gemini-*, qwen-*, and o3-* patterns
  • Each converter test file has explicit model handling tests (bare alias resolution, pass-through, drop behavior)
  • Full suite: 524 tests pass

Compound Engineering v2.59.0
🤖 Generated with Claude Opus 4.6 (1M context, extended thinking) via Claude Code

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]>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 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".

Comment thread src/converters/claude-to-copilot.ts
tmchow and others added 2 commits March 29, 2026 13:03
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]>
@tmchow
Copy link
Copy Markdown
Collaborator Author

tmchow commented Mar 29, 2026

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 model field expects display names like "Claude Opus 4.5", not Claude model IDs (claude-sonnet-4-20250514) or bare aliases (sonnet). Unlike Droid/Factory which natively resolves sonnet, Copilot has no documented resolution for Claude-specific values. So pass-through would emit a field Copilot can't use — which is arguably worse than omitting it (where the spec says it falls back to the platform default).

The distinction from Droid: Factory explicitly documents that it accepts sonnet, opus, haiku as valid model values. Copilot's model field works with a different value space entirely.

If Copilot model mapping becomes needed later, it would require a dedicated COPILOT_MODEL_MAP translating Claude aliases to Copilot display names — similar to how we'd need one for Codex if they ever add per-skill model support.

@tmchow tmchow merged commit f93d10c into main Mar 29, 2026
2 checks passed
@github-actions github-actions Bot mentioned this pull request Mar 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant