Codex Desktop on macOS shadows project-local developer_instructions with a host-generated thread override

Open 💬 3 comments Opened Jul 15, 2026 by ljh740

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?

  1. 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.
"""
  1. 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"
  1. Confirm that the output contains:
PROJECT_DEV_INSTRUCTIONS_7F3A
  1. Open the same trusted repository in Codex Desktop.
  1. Start a completely fresh thread.
  1. 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:

  1. buildNewConversationParams(...) calls the host endpoint named developer-instructions.
  2. The returned app-generated instructions are assigned to developerInstructions.
  3. That value is sent through thread/start.

In the open-source app-server implementation, the request field is passed into ConfigOverrides:

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 developerInstructions becomes a higher-precedence override and shadows project config.

Suggested fix direction

One of the following should preserve the intended behavior:

  1. Do not use the top-level thread/start.developerInstructions field solely for Desktop app-context instructions.
  2. Merge the resolved config-file developer_instructions with the Desktop-generated app context before calling thread/start.
  3. 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

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.

View original on GitHub ↗

3 Comments

Grekkor · 1 month ago

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.

nikitoprojector · 23 days ago

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 sets developer_instructions, the user's profile-level instructions are lost because the field is replaced, not composed. Updating AGENTS.md is 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 developerInstructions shadows 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_instructions as the replacement field, while adding an explicitly additive field such as additional_developer_instructions or developer_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

sevq1993-cyber · 17 days ago

I can confirm that this also affects user-level ~/.codex/config.toml on 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

  • Codex Desktop: 26.803.41515 (build 6321)
  • macOS: 27.0 (26A5388g)
  • Model: gpt-5.6-sol
  • Primary Desktop rollout metadata: originator = "Codex Desktop", source = "vscode"
  • Observed on 2026-08-10/11

User-level configuration

I added a sanitized form of the following to ~/.codex/config.toml and started fresh Desktop tasks:

developer_instructions = """
DESKTOP_WAIT_POLICY_MARKER

For long-running asynchronous work:
- Empty write_stdin polls must use yield_time_ms >= 180000.
- functions.wait must use yield_time_ms >= 180000.
- The outer functions.exec yield must be longer than the longest nested wait.
- Do not use long waits for non-empty interactive write_stdin calls.
- Do not wake the model only to report that work is still running.
"""

The same block appears as an initial developer-role message in child sessions with:

originator = "codex_exec"
source = "exec"

Those child sessions then used functions.wait with yield_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:

  • 68 functions.wait calls occurred.
  • 59 used only 10000–30000 ms.
  • Six calls used 180000 ms, and all six were in codex_exec sessions that contained the configured developer instructions.
  • Primary Desktop sessions also made three empty write_stdin polls: 30000, 30000, and 1000 ms.
  • No empty Desktop write_stdin poll used 180000 or 300000 ms.

One representative trace demonstrates the resulting extra model turn:

  1. An outer JavaScript functions.exec cell awaited a nested write_stdin(..., yield_time_ms: 30000).
  2. The outer cell yielded after approximately 11.2 seconds with Script running with cell ID ..., while the nested process was still running.
  3. The model was invoked again and issued functions.wait(..., yield_time_ms: 30000).
  4. The original nested operation completed approximately four seconds later.

The unnecessary intermediate model invocation recorded:

input_tokens:        214,725
cached_input_tokens: 214,016
output_tokens:            64
total_tokens:        214,789

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:

user config developer_instructions
+ project config developer_instructions
+ Desktop host/app context
+ collaboration-mode context

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/start test should provide all of the following:

  1. user-level developer_instructions containing a unique marker;
  2. project-level developer_instructions containing a second marker;
  3. non-empty host-generated Desktop instructions;
  4. collaboration-mode developer context.

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_exec sessions.

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.