Preserve null developer instructions (#16976)

Preserve explicit null developer-instruction overrides across app-server
resume and fork flows.
This commit is contained in:
Ahmed Ibrahim
2026-04-07 09:32:14 -07:00
committed by GitHub
Unverified
parent feb4f0051a
commit cd591dc457
19 changed files with 121 additions and 1 deletions
+17 -1
View File
@@ -615,6 +615,14 @@ impl Codex {
dynamic_tools
};
let developer_instructions_override = config
.developer_instructions_override
.clone()
.or_else(|| conversation_history.get_developer_instructions());
let developer_instructions = developer_instructions_override
.clone()
.unwrap_or_else(|| config.developer_instructions.clone());
// TODO (aibrahim): Consolidate config.model and config.model_reasoning_effort into config.collaboration_mode
// to avoid extracting these fields separately and constructing CollaborationMode here.
let collaboration_mode = CollaborationMode {
@@ -630,7 +638,8 @@ impl Codex {
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
service_tier: config.service_tier,
developer_instructions: config.developer_instructions.clone(),
developer_instructions,
developer_instructions_override,
user_instructions,
personality: config.personality,
base_instructions,
@@ -1095,6 +1104,10 @@ pub(crate) struct SessionConfiguration {
/// Developer instructions that supplement the base instructions.
developer_instructions: Option<String>,
/// Explicit developer instructions override, preserving `null` as distinct
/// from a missing override.
developer_instructions_override: Option<Option<String>>,
/// Model instructions that are appended to the base instructions.
user_instructions: Option<String>,
@@ -1545,6 +1558,9 @@ impl Session {
.base_instructions
.clone()
.map(|text| BaseInstructions { text }),
session_configuration
.developer_instructions_override
.clone(),
session_configuration.dynamic_tools.clone(),
if session_configuration.persist_extended_history {
EventPersistenceMode::Extended
+9
View File
@@ -1853,6 +1853,7 @@ async fn set_rate_limits_retains_previous_credits() {
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -1955,6 +1956,7 @@ async fn set_rate_limits_updates_plan_type_when_present() {
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -2227,6 +2229,7 @@ async fn attach_rollout_recorder(session: &Arc<Session>) -> PathBuf {
/*forked_from_id*/ None,
SessionSource::Exec,
Some(BaseInstructions::default()),
/*developer_instructions*/ None,
Vec::new(),
EventPersistenceMode::Limited,
),
@@ -2304,6 +2307,7 @@ pub(crate) async fn make_session_configuration_for_tests() -> SessionConfigurati
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -2570,6 +2574,7 @@ async fn session_new_fails_when_zsh_fork_enabled_without_zsh_path() {
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -2673,6 +2678,7 @@ pub(crate) async fn make_session_and_context() -> (Session, TurnContext) {
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -3513,6 +3519,7 @@ pub(crate) async fn make_session_and_context_with_dynamic_tools_and_rx(
collaboration_mode,
model_reasoning_summary: config.model_reasoning_summary,
developer_instructions: config.developer_instructions.clone(),
developer_instructions_override: config.developer_instructions_override.clone(),
user_instructions: config.user_instructions.clone(),
service_tier: None,
personality: config.personality,
@@ -4264,6 +4271,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_baseline
/*forked_from_id*/ None,
SessionSource::Exec,
Some(BaseInstructions::default()),
/*developer_instructions*/ None,
Vec::new(),
EventPersistenceMode::Limited,
),
@@ -4361,6 +4369,7 @@ async fn record_context_updates_and_set_reference_context_item_persists_full_rei
/*forked_from_id*/ None,
SessionSource::Exec,
Some(BaseInstructions::default()),
/*developer_instructions*/ None,
Vec::new(),
EventPersistenceMode::Limited,
),
+4
View File
@@ -4506,6 +4506,7 @@ fn test_precedence_fixture_with_o3_profile() -> std::io::Result<()> {
experimental_realtime_ws_startup_context: None,
base_instructions: None,
developer_instructions: None,
developer_instructions_override: None,
guardian_developer_instructions: None,
include_permissions_instructions: true,
include_apps_instructions: true,
@@ -4651,6 +4652,7 @@ fn test_precedence_fixture_with_gpt3_profile() -> std::io::Result<()> {
experimental_realtime_ws_startup_context: None,
base_instructions: None,
developer_instructions: None,
developer_instructions_override: None,
guardian_developer_instructions: None,
include_permissions_instructions: true,
include_apps_instructions: true,
@@ -4794,6 +4796,7 @@ fn test_precedence_fixture_with_zdr_profile() -> std::io::Result<()> {
experimental_realtime_ws_startup_context: None,
base_instructions: None,
developer_instructions: None,
developer_instructions_override: None,
guardian_developer_instructions: None,
include_permissions_instructions: true,
include_apps_instructions: true,
@@ -4923,6 +4926,7 @@ fn test_precedence_fixture_with_gpt5_profile() -> std::io::Result<()> {
experimental_realtime_ws_startup_context: None,
base_instructions: None,
developer_instructions: None,
developer_instructions_override: None,
guardian_developer_instructions: None,
include_permissions_instructions: true,
include_apps_instructions: true,
+6
View File
@@ -248,6 +248,10 @@ pub struct Config {
/// Developer instructions override injected as a separate message.
pub developer_instructions: Option<String>,
/// Explicit developer instructions override, preserving `null` as distinct
/// from a missing override.
pub developer_instructions_override: Option<Option<String>>,
/// Guardian-specific developer instructions override from requirements.toml.
pub guardian_developer_instructions: Option<String>,
@@ -1761,6 +1765,7 @@ impl Config {
let file_base_instructions =
Self::try_read_non_empty_file(model_instructions_path, "model instructions file")?;
let base_instructions = base_instructions.or_else(|| file_base_instructions.map(Some));
let developer_instructions_override = developer_instructions.clone();
let developer_instructions =
developer_instructions.unwrap_or_else(|| cfg.developer_instructions.clone());
let include_permissions_instructions = config_profile
@@ -1945,6 +1950,7 @@ impl Config {
base_instructions,
personality,
developer_instructions,
developer_instructions_override,
compact_prompt,
commit_attribution,
include_permissions_instructions,
@@ -43,6 +43,7 @@ async fn write_session_with_user_event(codex_home: &Path) -> io::Result<()> {
agent_role: None,
model_provider: None,
base_instructions: None,
developer_instructions: None,
dynamic_tools: None,
memory_mode: None,
},