Preserve base instruction provenance across sessions (#37446)

## Why

Persisted base instructions need to retain whether they were explicitly customized or generated from a model template. Without that distinction, forks and config-lock replays can treat model-generated instructions as custom and keep the wrong template after a model or personality change.

## What changed

- Record custom or model provenance with base instructions in rollouts and config locks.
- Preserve custom instructions across model changes, while allowing model-generated instructions to follow the selected model and personality.
- Keep legacy rollouts without provenance compatible and propagate provenance through forks and spawned agents.

## Testing

- Cover provenance serialization and legacy rollout decoding.
- Cover role personality changes, forked model changes, config-lock replay, and multi-agent configuration.

GitOrigin-RevId: 801c26559835f558027ce5112700c0cd31fcb21b
This commit is contained in:
jif
2026-08-07 14:08:13 +00:00
committed by copyberry
parent e58d9ef447
commit 964a227d8c
29 changed files with 324 additions and 35 deletions
@@ -26,6 +26,7 @@ use codex_external_agent_migration::sessions::record_completed_session_imports;
use codex_models_manager::manager::RefreshStrategy;
use codex_protocol::ThreadId;
use codex_protocol::models::BaseInstructions;
use codex_protocol::models::BaseInstructionsProvenance;
use codex_protocol::protocol::EventMsg;
use codex_protocol::protocol::MultiAgentVersion;
use codex_protocol::protocol::RolloutItem;
@@ -448,6 +449,17 @@ impl ExternalAgentSessionImporter {
.base_instructions
.clone()
.unwrap_or_else(|| model_info.get_model_instructions(config.personality)),
provenance: Some(config.base_instructions_provenance.clone().unwrap_or_else(
|| {
if config.base_instructions.is_some() {
BaseInstructionsProvenance::Custom
} else {
BaseInstructionsProvenance::Model {
model: model_info.slug.clone(),
}
}
},
)),
},
dynamic_tools: Vec::new(),
selected_capability_roots: Vec::new(),
@@ -624,6 +624,7 @@ mod tests {
cwd: tempdir.path().to_path_buf(),
base_instructions: Some(codex_protocol::models::BaseInstructions {
text: "actual developer prompt".to_string(),
provenance: None,
}),
..Default::default()
},