Decoupling Model Selection from Profile Binding
June 24, 2026 ยท View on GitHub
Problem
Profiles currently provide useful defaults (engine, tool policy, system prompt), but users need to choose model independently at runtime (task/agent/session) without being locked to profile model defaults.
Goals
- Preserve profile-level behavior and policy defaults.
- Let callers specify model independently from profile.
- Keep explicit engine selection possible.
- Support clear precedence and mismatch visibility.
Final Design
1) Canonical run contract now has top-level model
LemonCore.RunRequest now includes :model so model overrides can be passed explicitly (not only via meta).
2) Dedicated resolver module
Added LemonRouter.ModelSelection to centralize resolution:
- Model precedence:
- request-level explicit model
- meta model (back-compat)
- session policy model
- profile model
- config default model (
LemonCore.Config.cached().agent.default_model, backed by[defaults])
SubmissionBuilder also stores the resolved history_model in submission
metadata for routing-feedback history when that feature is enabled. Runtime
modules should not read Application.get_env(:lemon_router, :default_model);
model defaults come from Lemon config or policy stores.
- Engine precedence:
- resume token engine
- explicit
engine_id - model-implied engine (
codex:*,claude:*, etc.) - profile default engine
3) Mismatch warnings (non-blocking)
If explicit engine conflicts with model-implied engine, the system:
- keeps explicit engine (caller intent wins),
- records warning in
job.meta[:model_resolution_warning], - logs warning in orchestrator.
4) API/tool surface updates
agentcontrol-plane method acceptsmodel.agent.inbox.sendacceptsmodel.CodingAgent.Tools.Agentacceptsmodeland forwards it throughRunRequest.model.CodingAgent.Tools.Taskacceptsmodelandthinking_levelfor internal-session subtasks.
Why this works
Profiles continue to supply persona/tool defaults, while model selection is independently controlled by request/session/runtime layers.
Validation policy
No hard failures for engine/model mismatches yet; warnings only. This avoids breaking existing flows while making conflicts visible.
Future extensions
- Add strict mode to reject incompatible engine/model combinations.
- Add capability checks (e.g., tool-calling requirements vs selected model).
- Expose model-resolution diagnostics in run inspection APIs.