Client or integration
Other — observed with a T3 Code-managed Codex app-server
I have not independently reproduced this with the bare Codex TUI.
Area
Catalog / models
Summary
OpenCodex 2.11.1 checks the start times of all Codex app-servers owned by the current user. If any app-server predates the on-disk model catalog, OpenCodex injects this developer message into every qualifying Multi-Agent V2 request:
<multi_agent_mode>The model catalog changed after Codex started; do not set model or reasoning_effort overrides until Codex restarts.</multi_agent_mode>
This is safe when the app-server handling the current request is stale. The problem is that OpenCodex calculates one machine-wide state and cannot tell which app-server sent the request.
As a result, an unrelated stale process can inject no-override guidance into a fresh session whose app-server started after the catalog was written.
This code path does not reject model or reasoning parameters. It instructs the model not to provide them, so subsequent spawn_agent calls inherit their default model and effort.
Observed behavior
Environment:
- OpenCodex 2.11.1
- Commit
121f1ad929dc6da3356c06f5192f2f97f7a5dde5
- Codex CLI 0.147.0
- Ubuntu 24.04.4 LTS, x86_64
- Times below are UTC+09:00
Process timeline:
- Detached app-server A started at 00:43:03.
opencodex-catalog.json was written at 04:25:10.
- The active T3-owned app-server B started at 10:24:33.
- A request handled by B still received the quoted no-override message.
OpenCodex therefore classified A as stale according to its timestamp rule, while B was clearly newer than the catalog.
The catalog contents from before the 04:25 write were not preserved, so this report does not claim that A’s actual model list differed. The demonstrated bug is that A’s timestamp controlled the guidance injected into B’s request.
Restarting t3code.service replaced the T3-owned app-servers but did not stop A, which belonged to a separate login session. New T3 sessions continued receiving the same message.
Root cause
collectCodexAppServerCatalogState() calculates one result across every detected app-server:
const stale = withStarts.some(
proc => proc.startedAtMs! <= catalogMtimeMs
);
If one process is old, the result for the entire set is stale.
multiAgentGuidanceText() then uses that machine-wide result when building guidance for an individual request:
if (catalogState.state === "stale" || catalogState.state === "unknown") {
return "<multi_agent_mode>The model catalog changed after Codex started; do not set "
+ "model or reasoning_effort overrides until Codex restarts.</multi_agent_mode>";
}
The decision does not include the identity of the app-server that sent the request. It also does not compare the request with a process-specific catalog, CODEX_HOME, catalog path, or catalog fingerprint.
Codex does include its current model and reasoning options in the spawn_agent tool description, but this information is prose rather than a machine-readable schema enum.
Relevant source and tests:
src/codex/app-server-processes.ts:562-637
src/server/responses/collaboration.ts:243-252
tests/codex-app-server-processes.test.ts:37-53
tests/multi-agent-compat.test.ts:120-143
The tests currently confirm both parts of the behavior:
- One stale process plus one fresh process produces a machine-wide
stale result.
- A
stale result produces the do not set guidance.
Reproduction
This can be reproduced through the existing test seams:
- Call
collectCodexAppServerCatalogState() with:
- catalog mtime:
1000
- app-server A start time:
500
- app-server B start time:
3000
- Confirm that the result is
stale.
- Create a parsed Multi-Agent V2 request containing
spawn_agent and the V2 companion tools. Treat it as a request from fresh app-server B.
- Pass the machine-wide
stale result to multiAgentGuidanceText().
- Confirm that it returns the quoted no-override message. No input to the function identifies whether A or B sent the request.
- Change the state to
fresh and confirm that the no-override message disappears. Positive guidance may return when a preferred model or roster is configured; otherwise the result may be null.
For an end-to-end test, capture the Responses input after injectDeveloperMessage() or route the request through a recording upstream. The forwarded input should contain the quoted developer message even though the app-server handling the request started after the catalog write.
Expected behavior
A stale app-server should not change the instructions sent to a separate fresh app-server.
The safety goal from #857 should remain: OpenCodex should not add disk-derived model recommendations that the active app-server may reject.
When OpenCodex cannot identify the app-server responsible for the current request, it should continue suppressing its own disk-derived recommendations but leave the active spawn_agent tool description authoritative.
Suggested fix
A practical fix within OpenCodex:
- Continue suppressing OpenCodex’s preferred-model and roster additions when the machine-wide state is
stale or unknown.
- Remove the blanket instruction prohibiting all model and reasoning overrides.
- Either inject no additional guidance or tell the model to use only the options listed in the active
spawn_agent description.
- Avoid claiming that the current session’s catalog changed when only an unrelated process may be stale.
A concrete version of the guidance would be:
Instead of:
Do not set model or reasoning-effort overrides.
it should say, at most:
This Codex server may have an older model catalog. Only use models exposed by its current spawn_agent tool. Restart PID 5267 to load the latest catalog.
A fuller solution would add an app-server identity or catalog fingerprint to each request. OpenCodex could then apply stale-catalog guidance only when the app-server handling that request is stale. This may require support from Codex or the launching client.
Acceptance criteria
- One stale app-server plus one fresh app-server no longer causes a blanket no-override instruction.
- When the current app-server cannot be identified, OpenCodex suppresses its disk-derived recommendations while leaving the active
spawn_agent description authoritative.
- When every detected app-server is fresh, the existing positive guidance remains unchanged.
Related issues
Version
2.11.1
Operating system
Ubuntu 24.04.4 LTS, x86_64
Provider and model
Not provider-specific.
Checks
Client or integration
Other — observed with a T3 Code-managed Codex app-server
I have not independently reproduced this with the bare Codex TUI.
Area
Catalog / models
Summary
OpenCodex 2.11.1 checks the start times of all Codex app-servers owned by the current user. If any app-server predates the on-disk model catalog, OpenCodex injects this developer message into every qualifying Multi-Agent V2 request:
This is safe when the app-server handling the current request is stale. The problem is that OpenCodex calculates one machine-wide state and cannot tell which app-server sent the request.
As a result, an unrelated stale process can inject no-override guidance into a fresh session whose app-server started after the catalog was written.
This code path does not reject model or reasoning parameters. It instructs the model not to provide them, so subsequent
spawn_agentcalls inherit their default model and effort.Observed behavior
Environment:
121f1ad929dc6da3356c06f5192f2f97f7a5dde5Process timeline:
opencodex-catalog.jsonwas written at 04:25:10.OpenCodex therefore classified A as stale according to its timestamp rule, while B was clearly newer than the catalog.
The catalog contents from before the 04:25 write were not preserved, so this report does not claim that A’s actual model list differed. The demonstrated bug is that A’s timestamp controlled the guidance injected into B’s request.
Restarting
t3code.servicereplaced the T3-owned app-servers but did not stop A, which belonged to a separate login session. New T3 sessions continued receiving the same message.Root cause
collectCodexAppServerCatalogState()calculates one result across every detected app-server:If one process is old, the result for the entire set is
stale.multiAgentGuidanceText()then uses that machine-wide result when building guidance for an individual request:The decision does not include the identity of the app-server that sent the request. It also does not compare the request with a process-specific catalog,
CODEX_HOME, catalog path, or catalog fingerprint.Codex does include its current model and reasoning options in the
spawn_agenttool description, but this information is prose rather than a machine-readable schema enum.Relevant source and tests:
src/codex/app-server-processes.ts:562-637src/server/responses/collaboration.ts:243-252tests/codex-app-server-processes.test.ts:37-53tests/multi-agent-compat.test.ts:120-143The tests currently confirm both parts of the behavior:
staleresult.staleresult produces thedo not setguidance.Reproduction
This can be reproduced through the existing test seams:
collectCodexAppServerCatalogState()with:10005003000stale.spawn_agentand the V2 companion tools. Treat it as a request from fresh app-server B.staleresult tomultiAgentGuidanceText().freshand confirm that the no-override message disappears. Positive guidance may return when a preferred model or roster is configured; otherwise the result may benull.For an end-to-end test, capture the Responses input after
injectDeveloperMessage()or route the request through a recording upstream. The forwarded input should contain the quoted developer message even though the app-server handling the request started after the catalog write.Expected behavior
A stale app-server should not change the instructions sent to a separate fresh app-server.
The safety goal from #857 should remain: OpenCodex should not add disk-derived model recommendations that the active app-server may reject.
When OpenCodex cannot identify the app-server responsible for the current request, it should continue suppressing its own disk-derived recommendations but leave the active
spawn_agenttool description authoritative.Suggested fix
A practical fix within OpenCodex:
staleorunknown.spawn_agentdescription.A concrete version of the guidance would be:
Instead of:
it should say, at most:
A fuller solution would add an app-server identity or catalog fingerprint to each request. OpenCodex could then apply stale-catalog guidance only when the app-server handling that request is stale. This may require support from Codex or the launching client.
Acceptance criteria
spawn_agentdescription authoritative.Related issues
unknownon Windows.model_catalog_jsonwithout restarting Codex.Version
2.11.1
Operating system
Ubuntu 24.04.4 LTS, x86_64
Provider and model
Not provider-specific.
Checks