Codex Desktop on macOS shadows project-local developer_instructions with a host-generated thread override
What version of the Codex App are you using?
- ChatGPT/Codex desktop app:
26.707.72221 (5307) - Bundle identifier:
com.openai.codex - Embedded
codex-cli:0.144.2
What subscription do you have?
Pro
What platform is your computer?
- macOS
26.5.1 (25F80) - Apple Silicon,
arm64
What issue are you seeing?
In a trusted repository, a top-level developer_instructions value from the project-local .codex/config.toml is missing from fresh threads created by Codex Desktop.
The same embedded Codex binary, running from the same working directory, includes the project-local instructions in codex debug prompt-input.
This is not a complete project-config loading failure:
- the project is trusted;
- project-specific
AGENTS.md, MCP, sandbox, and other project context can still load; - the failure appears specific to
developer_instructions.
The Desktop app appears to generate its own non-empty developerInstructions value and send it as a request-level field in thread/start. The app-server treats that value as a ConfigOverrides.developer_instructions override, which shadows the value resolved from .codex/config.toml.
As a result, repository-specific workflow or safety instructions can be silently omitted from Desktop-created threads.
What steps can reproduce the bug?
- In a trusted Git repository, create
.codex/config.toml:
developer_instructions = """
Include the exact marker PROJECT_DEV_INSTRUCTIONS_7F3A
in the first line of every response.
"""
- From the repository root, inspect the prompt using the same Codex binary bundled with the desktop app:
/Applications/ChatGPT.app/Contents/Resources/codex \
debug prompt-input "noop"
- Confirm that the output contains:
PROJECT_DEV_INSTRUCTIONS_7F3A
- Open the same trusted repository in Codex Desktop.
- Start a completely fresh thread.
- Inspect the initial developer-role messages in the corresponding rollout under
~/.codex/sessions/, or ask the thread which project developer instruction it received.
What is the expected behavior?
The project-local developer_instructions should remain part of the effective developer context.
Desktop-specific app instructions may be added, but they should not replace explicitly configured project instructions.
The resulting developer context should effectively contain both:
project .codex/config.toml developer_instructions
+
Desktop-specific app context
What is the actual behavior?
In the reproduced Desktop thread:
originator: Codex Desktop
source: vscode
cli_version: 0.144.2
The distinctive project instruction markers were absent from all initial developer-role messages.
By comparison, codex debug prompt-input using the bundled 0.144.2 binary from the same working directory contained those markers.
Source-level evidence
The current packaged Desktop webview performs the following sequence when constructing a fresh conversation:
buildNewConversationParams(...)calls the host endpoint nameddeveloper-instructions.- The returned app-generated instructions are assigned to
developerInstructions. - That value is sent through
thread/start.
In the open-source app-server implementation, the request field is passed into ConfigOverrides:
thread/startpasses developer_instructions into build_thread_config_overridesConfigOverrides.developer_instructionsis populated from that request value
The core configuration merge then uses:
let developer_instructions =
developer_instructions.or(cfg.developer_instructions);
Source:
Therefore, whenever Desktop supplies a non-None request-level value, the cfg.developer_instructions value resolved from user/project config files is never used.
This source path appears to explain the observed CLI/Desktop difference:
- CLI/debug path: no Desktop host override, so project config survives.
- Desktop path: host-generated
developerInstructionsbecomes a higher-precedence override and shadows project config.
Suggested fix direction
One of the following should preserve the intended behavior:
- Do not use the top-level
thread/start.developerInstructionsfield solely for Desktop app-context instructions. - Merge the resolved config-file
developer_instructionswith the Desktop-generated app context before callingthread/start. - Introduce a separate protocol field for additive Desktop context so it cannot replace user-configured developer instructions.
A regression test should start a Desktop-style thread with both:
- project-local
developer_instructions; - host-generated Desktop instructions.
The resulting model request should contain both instruction blocks.
Related issues
- #11004 — Codex App developer_instructions are not attached to App-created threads
- #18259 — Project-local developer_instructions do not override global configuration
- #25145 — macOS Desktop does not load developer_instructions from config.toml
This report adds a current macOS reproduction on app version 26.707.72221, verifies the behavior with the same bundled Codex binary, and narrows the failure to the Desktop host’s request-level developerInstructions override rather than the general project-config loader.
3 Comments
Yes. This applies not only to repo level config.toml but also the user level one under ~/.codex
Its extremely annoying because neither codex cli nor the new ChatGPT Desktop / Codex app support main agent modes like opencode so the only place to put main agent only instructions is in the developer_instructions field which is overridden in the Desktop app.
I believe #12926 was about the same missing behavior: Codex has no supported way to add developer instructions without replacing instructions from another layer
I have encountered the same problem in other project. It writes generated project rules to
.codex/config.toml. If it setsdeveloper_instructions, the user's profile-level instructions are lost because the field is replaced, not composed. UpdatingAGENTS.mdis not an option because it is normally tracked by the project and would leave a working-tree diff.#33238 shows the same problem at the Desktop boundary: app-provided
developerInstructionsshadows the project value.#9424 already demonstrates a possible internal approach: collaboration-mode instructions are added as a separate developer message and coexist with regular developer instructions.
A backward-compatible solution could keep
developer_instructionsas the replacement field, while adding an explicitly additive field such asadditional_developer_instructionsordeveloper_instruction_files. App Server should similarly have a separate additive field for host context.This is not only a Desktop bug; it is a missing instruction-composition feature.
Please reconsider the request from #12926 in this context
I can confirm that this also affects user-level
~/.codex/config.tomlon a newer macOS Desktop build, and I have rollout-level evidence that the shadowing can cause substantial unnecessary model re-entry during asynchronous waits.Environment
26.803.41515(build6321)27.0(26A5388g)gpt-5.6-soloriginator = "Codex Desktop",source = "vscode"User-level configuration
I added a sanitized form of the following to
~/.codex/config.tomland started fresh Desktop tasks:The same block appears as an initial developer-role message in child sessions with:
Those child sessions then used
functions.waitwithyield_time_ms = 180000, showing that the configuration is valid and is applied on that path.However, the block and marker are absent from the initial developer-role messages of fresh primary Desktop sessions. The Desktop sessions instead contain the host-generated app context and other request-level developer messages.
Measured behavioral impact
In local rollout files created after this configuration was added:
functions.waitcalls occurred.10000–30000ms.180000ms, and all six were incodex_execsessions that contained the configured developer instructions.write_stdinpolls:30000,30000, and1000ms.write_stdinpoll used180000or300000ms.One representative trace demonstrates the resulting extra model turn:
functions.execcell awaited a nestedwrite_stdin(..., yield_time_ms: 30000).Script running with cell ID ..., while the nested process was still running.functions.wait(..., yield_time_ms: 30000).The unnecessary intermediate model invocation recorded:
This is local rollout telemetry, not a claim about the private subscription billing ledger. It nevertheless shows that losing the configured wait policy can force a full large-context model re-entry merely to continue waiting.
This connects the configuration-shadowing bug to the polling/usage problems reported in #35259 and #13733.
Expected behavior
User-level and project-level developer instructions should be composed additively with Desktop host context. Desktop-generated context must not replace explicitly configured instructions.
Conceptually:
If replacement semantics must remain available for backward compatibility, the Desktop host context should use a separate additive protocol field rather than
ConfigOverrides.developer_instructions.Suggested regression coverage
A Desktop-style
thread/starttest should provide all of the following:developer_instructionscontaining a unique marker;developer_instructionscontaining a second marker;The resulting initial model request should contain all expected additive blocks exactly once. The test should also verify parity between primary Desktop sessions and
codex_execsessions.Full rollout files are not attached because they contain private prompts, local paths, repository context, and account metadata. Sanitized event excerpts can be provided if needed.