compact_prompt is loaded but ignored for OpenAI remote compaction

Open 💬 2 comments Opened Jul 20, 2026 by skorkac

What version of Codex CLI is running?

Codex version: 0.144.6

What subscription do you have?

Enterprise

Which model were you using?

gpt-5.6-sol

What platform is your computer?

Linux 4.18.0-553.124.1.el8_10.x86_64 x86_64 x86_64

What terminal emulator and version are you using (if applicable)?

_No response_

Codex doctor report

What issue are you seeing?

In codex-rs/core/src/tasks/compact.rs, config.compact_prompt is read only
in the local-compaction branch. The built-in OpenAI provider reports remote
compaction support and is routed through compact_remote or compact_remote_v2, neither of which consumes config.compact_prompt.

Therefore both manual and automatic compaction silently ignore the documented setting for the default OpenAI provider. experimental_compact_prompt_file has
the same problem because it resolves into the same config field.

compact_prompt is explicitly documented as: https://learn.chatgpt.com/docs/config-file/config-reference#configtoml

config/read confirms the setting loads successfully.

=====================
Codex version: 0.144.6
Platform: Linux
Provider: built-in OpenAI
Model: gpt-5.6-sol

codex_home=/nobackup/skorka/context_compaction/.codex
compact_prompt_loaded=true
config_probe=WORKSPACE_CONFIG_LOADED_20260720
context_compaction_started=true
context_compaction_completed=true
post_compaction_response=NONE

What steps can reproduce the bug?

  1. Using Codex CLI 0.144.6 with the built-in OpenAI provider, create or open a trusted Git repository.
  1. Add the following project-local .codex/config.toml:

developer_instructions = """
When the user sends CONFIG_PROBE_REQUEST, reply exactly:
WORKSPACE_CONFIG_LOADED
"""

compact_prompt = """
Begin the compacted summary with exactly this identifier:
CUSTOM_COMPACT_MARKER_7F3A

Then provide the normal handoff summary.
"""

  1. Start a new Codex session from the repository and accept the project trust prompt.
  1. Send CONFIG_PROBE_REQUEST. Confirm that Codex replies WORKSPACE_CONFIG_LOADED. This demonstrates that the project-local config was loaded.
  1. Continue with one or more ordinary turns. Do not mention CUSTOM_COMPACT_MARKER_7F3A anywhere in the conversation.
  1. Run /compact manually and wait for context compaction to complete.
  1. Send the following message:

During the immediately preceding compaction, you were instructed to begin
the summary with a unique all-caps identifier. Reply with only that
identifier. If no identifier is present in your compacted context, reply NONE.

  1. Observe that Codex replies NONE, or otherwise cannot identify the marker.

Expected behavior:

Codex should reply CUSTOM_COMPACT_MARKER_7F3A, proving that the documented compact_prompt was used.

Actual behavior:

The project configuration loads and compaction completes, but the marker is absent. The built-in OpenAI provider routes compaction through the remote compaction implementation, which does not consume config.compact_prompt.

What is the expected behavior?

Codex should reply CUSTOM_COMPACT_MARKER_7F3A, proving that the documented compact_prompt was used.

Additional information

_No response_

View original on GitHub ↗

2 Comments

NaruSora · 21 days ago

Confirmed on 0.146.1 (ChatGPT auth, gpt-5.6-sol), with field evidence from a long-running thread.

Dispatch (pinned to rust-v0.146.1): both the manual task and the inline auto path gate on should_use_remote_compact_task = provider.supports_remote_compaction() (= is_openai() || azure), so the local arm — the only consumer of config.compact_prompt — is unreachable on the default provider:

Rollout evidence: across 37 auto-compactions in one thread, every compacted event has payload.message == "" (hardcoded empty on the remote-v2 path) and replacement_history ends in an encrypted cmp_* compaction item; the configured prompt text never appears in any request.

Practical harm beyond the dead setting: with no client-visible distillate, the replacement history is the verbatim user turns. In our case a stale one-time directive ("pause and commit") retained as the last real user message re-steered a long campaign into an unwanted pause immediately after two consecutive compactions — exactly the failure mode a custom handoff prompt is written to prevent.

Until the setting is honored (or explicitly rejected), a startup warning when compact_prompt / experimental_compact_prompt_file is set while the provider uses remote compaction would at least prevent the silent no-op.

Related: #37305 — the local compaction path additionally omits tool specs from its request and therefore never hits the prompt cache; any fix that routes custom prompts to local compaction will surface that cost bug too.

dias-yegizekov · 4 hours ago

I rechecked this on the official Codex CLI 0.150.1 source (rust-v0.150.1, commit 90854393966b21e9ebfd21b122334eb09a20c93d). The remote compaction paths still do not apply config.compact_prompt, although local compaction does.

Relevant paths:

  • codex-rs/core/src/compact.rs — local compaction reads turn_context.config.compact_prompt
  • codex-rs/core/src/compact_remote_request.rs — remote V1 uses only sess.get_base_instructions()
  • codex-rs/core/src/compact_remote_v2_attempt.rs — remote V2 also uses only sess.get_base_instructions()
  • codex-rs/core/src/compact_remote.rs — common remote-compaction module where a shared composition helper can live

The minimal change that makes V1 and V2 honor the configured compaction prompt while preserving the existing BaseInstructions provenance is:

diff --git a/codex-rs/core/src/compact_remote.rs b/codex-rs/core/src/compact_remote.rs
@@
+pub(crate) fn compose_remote_compaction_instructions(
+    base_instructions: BaseInstructions,
+    compact_prompt: Option<&str>,
+) -> BaseInstructions {
+    let Some(compact_prompt) = compact_prompt else {
+        return base_instructions;
+    };
+    let BaseInstructions {
+        mut text,
+        provenance,
+    } = base_instructions;
+    text.push_str("\n\n<custom_compaction_instructions>\n");
+    text.push_str(compact_prompt);
+    text.push_str("\n</custom_compaction_instructions>");
+    BaseInstructions { text, provenance }
+}
diff --git a/codex-rs/core/src/compact_remote_request.rs b/codex-rs/core/src/compact_remote_request.rs
@@
-    let base_instructions = sess.get_base_instructions().await;
+    let base_instructions = super::compose_remote_compaction_instructions(
+        sess.get_base_instructions().await,
+        turn_context.config.compact_prompt.as_deref(),
+    );
diff --git a/codex-rs/core/src/compact_remote_v2_attempt.rs b/codex-rs/core/src/compact_remote_v2_attempt.rs
@@
-    let base_instructions = sess.get_base_instructions().await;
+    let base_instructions = crate::compact_remote::compose_remote_compaction_instructions(
+        sess.get_base_instructions().await,
+        turn_context.config.compact_prompt.as_deref(),
+    );

I also checked the same behavior on 0.150.0 (rust-v0.150.0, commit 3b3b4f8fb3f6403e72c2d0533ed0d2f309c59717).

A useful regression set would cover:

  • manual remote V1 with compact_prompt
  • automatic remote V1 with compact_prompt
  • manual remote V2 with compact_prompt
  • automatic remote V2 with compact_prompt
  • unset compact_prompt, verifying serialized instructions remain exactly unchanged

It would be great if compact_prompt / experimental_compact_prompt_file could be applied consistently by the official remote-compaction paths so downstream users do not need to carry this source-level difference.