Custom agent roles discard explicit spawn model/reasoning when the role leaves them unset
What version of Codex CLI is running?
codex-cli 0.144.3
Runtime reproduction: Windows 10.0.26200 x64, PowerShell 7.5.8, GPT-5.6 Sol parent using Multi-Agent V2.
I also source-checked current main after #32749. The same role-application order remains in both V1 and V2.
What issue are you seeing?
An explicit model / reasoning_effort supplied to spawn_agent is discarded when a custom agent_type is selected, even when that role does not configure either field.
The role itself is selected correctly and its developer instructions are loaded, but the child falls back to the parent model/reasoning instead of using the explicit spawn request.
This is distinct from #31814 and the schema fix merged in #32749. #32749 makes V2 model overrides callable by default, but selecting a custom role still discards them during role application.
What steps can reproduce the bug?
Register a custom role:
# config.toml
[agents.probe]
description = "Minimal custom role used to verify spawn routing."
config_file = "agents/probe.toml"
Give the role instructions but deliberately leave model and reasoning unset:
# agents/probe.toml
developer_instructions = "Reply with exactly PROBE_OK."
For Codex CLI 0.144.3, expose the V2 routing fields:
[features.multi_agent_v2]
hide_spawn_agent_metadata = false
tool_namespace = "agents"
From a GPT-5.6 Sol parent, issue a context-free V2 spawn equivalent to:
{
"task_name": "probe",
"message": "Reply with exactly PROBE_OK.",
"fork_turns": "none",
"agent_type": "probe",
"model": "gpt-5.6-luna",
"reasoning_effort": "low"
}
Observed child configuration:
agent_role = probe
model = gpt-5.6-sol
reasoning_effort = low
The custom developer instructions are active, confirming that the role resolved successfully. A follow-up sent to the same child remains on Sol/low.
Control: spawning the built-in worker with the same explicit Luna/low request produces a Luna/low child, and follow-up retains Luna/low.
A custom role that explicitly pins Terra/high produces Terra/high. That locked-role behavior is expected and should remain unchanged.
Root cause
Both handlers apply the explicit model override first and the role second:
- V2: https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/multi_agents_v2/spawn.rs#L73-L84
- V1: https://github.com/openai/codex/blob/main/codex-rs/core/src/tools/handlers/multi_agents/spawn.rs#L103-L116
apply_role_to_config() rebuilds Config from the layer stack. A transient spawn-time model/reasoning override is not represented in that stack, so applying a role with no model settings restores the parent values:
https://github.com/openai/codex/blob/main/codex-rs/core/src/agent/role.rs#L56-L84
Role-defined model/reasoning settings are intentionally locked. That contract is surfaced in role guidance and was also used as the precedence model in #22169. The unexpected case is specifically a role that leaves one or both settings unspecified.
What is the expected behavior?
Resolve model and reasoning independently:
- If the effective role configuration defines
model, the role model remains locked and wins. - If it does not define
model, an explicit spawn-time model survives role application. - Apply the same rule independently to
model_reasoning_effort. - With no explicit spawn override, preserve current inheritance behavior.
- Keep full-history fork restrictions and service-tier behavior unchanged.
This is backwards-compatible for pinned roles while allowing custom roles to provide instructions, tools, permissions, and sandbox settings without preventing per-task model routing.
The effective-role check should account for profile-scoped values, not only top-level TOML keys.
Suggested regression coverage:
- V1 and V2: model-free role + explicit model/reasoning preserves both.
- V1 and V2: pinned role continues to override conflicting spawn values.
- Partial lock: role pins only model or only reasoning; the other explicit field survives.
- No explicit override continues to inherit.
- Follow-up retains the resolved child configuration.
- Full-history override rejection remains unchanged.
Additional information
No open issue or PR matching this narrower precedence bug was found.
Per the repository's invitation-only contribution policy, I have not opened an unsolicited PR. If maintainers agree with this behavior, I would be happy to prepare a focused implementation with V1/V2 integration coverage.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗