@@ -2989,14 +2989,15 @@ impl Session {
29892989 self . record_conversation_items ( turn_context, & context_items)
29902990 . await ;
29912991 }
2992- if previous_user_turn_model. is_none ( )
2993- || ( !should_inject_full_context && !context_items. is_empty ( ) )
2992+ if should_inject_full_context
2993+ || previous_user_turn_model. is_none ( )
2994+ || !context_items. is_empty ( )
29942995 {
2995- // Keep rollout TurnContext entries to:
2996+ // Keep rollout TurnContext entries for any turn that establishes or changes the
2997+ // persisted model-visible baseline:
29962998 // - the first real user turn (to recover `previous_model` on resume)
2999+ // - full-context reinjection after the baseline was cleared
29973000 // - steady-state turns that emitted explicit context diffs
2998- // Full reinjection after compaction is tracked in runtime state only; resume will
2999- // conservatively fall back to a missing baseline and reinject again if needed.
30003001 self . persist_rollout_items ( & [ RolloutItem :: TurnContext ( turn_context_item. clone ( ) ) ] )
30013002 . await ;
30023003 }
@@ -6382,6 +6383,10 @@ mod tests {
63826383 use crate :: protocol:: TokenCountEvent ;
63836384 use crate :: protocol:: TokenUsage ;
63846385 use crate :: protocol:: TokenUsageInfo ;
6386+ use crate :: protocol:: UserMessageEvent ;
6387+ use crate :: rollout:: policy:: EventPersistenceMode ;
6388+ use crate :: rollout:: recorder:: RolloutRecorder ;
6389+ use crate :: rollout:: recorder:: RolloutRecorderParams ;
63856390 use crate :: state:: TaskKind ;
63866391 use crate :: tasks:: SessionTask ;
63876392 use crate :: tasks:: SessionTaskContext ;
@@ -8550,6 +8555,83 @@ mod tests {
85508555 assert ! ( text. contains( "<model_switch>" ) ) ;
85518556 }
85528557
8558+ #[ tokio:: test]
8559+ async fn record_context_updates_and_set_reference_context_item_persists_full_reinjection_to_rollout (
8560+ ) {
8561+ let ( session, previous_context) = make_session_and_context ( ) . await ;
8562+ let next_model = if previous_context. model_info . slug == "gpt-5.1" {
8563+ "gpt-5"
8564+ } else {
8565+ "gpt-5.1"
8566+ } ;
8567+ let turn_context = previous_context
8568+ . with_model ( next_model. to_string ( ) , & session. services . models_manager )
8569+ . await ;
8570+ let config = session. get_config ( ) . await ;
8571+ let recorder = RolloutRecorder :: new (
8572+ config. as_ref ( ) ,
8573+ RolloutRecorderParams :: new (
8574+ ThreadId :: default ( ) ,
8575+ None ,
8576+ SessionSource :: Exec ,
8577+ BaseInstructions :: default ( ) ,
8578+ Vec :: new ( ) ,
8579+ EventPersistenceMode :: Limited ,
8580+ ) ,
8581+ None ,
8582+ None ,
8583+ )
8584+ . await
8585+ . expect ( "create rollout recorder" ) ;
8586+ let rollout_path = recorder. rollout_path ( ) . to_path_buf ( ) ;
8587+ {
8588+ let mut rollout = session. services . rollout . lock ( ) . await ;
8589+ * rollout = Some ( recorder) ;
8590+ }
8591+
8592+ session
8593+ . persist_rollout_items ( & [ RolloutItem :: EventMsg ( EventMsg :: UserMessage (
8594+ UserMessageEvent {
8595+ message : "seed rollout" . to_string ( ) ,
8596+ images : None ,
8597+ local_images : Vec :: new ( ) ,
8598+ text_elements : Vec :: new ( ) ,
8599+ } ,
8600+ ) ) ] )
8601+ . await ;
8602+ {
8603+ let mut state = session. state . lock ( ) . await ;
8604+ state. set_reference_context_item ( None ) ;
8605+ }
8606+
8607+ session
8608+ . record_context_updates_and_set_reference_context_item (
8609+ & turn_context,
8610+ Some ( previous_context. model_info . slug . as_str ( ) ) ,
8611+ )
8612+ . await ;
8613+ session. ensure_rollout_materialized ( ) . await ;
8614+ session. flush_rollout ( ) . await ;
8615+
8616+ let InitialHistory :: Resumed ( resumed) = RolloutRecorder :: get_rollout_history ( & rollout_path)
8617+ . await
8618+ . expect ( "read rollout history" )
8619+ else {
8620+ panic ! ( "expected resumed rollout history" ) ;
8621+ } ;
8622+ let persisted_turn_context = resumed. history . iter ( ) . find_map ( |item| match item {
8623+ RolloutItem :: TurnContext ( ctx) => Some ( ctx. clone ( ) ) ,
8624+ _ => None ,
8625+ } ) ;
8626+
8627+ assert_eq ! (
8628+ serde_json:: to_value( persisted_turn_context)
8629+ . expect( "serialize persisted turn context item" ) ,
8630+ serde_json:: to_value( Some ( turn_context. to_turn_context_item( ) ) )
8631+ . expect( "serialize expected turn context item" )
8632+ ) ;
8633+ }
8634+
85538635 #[ tokio:: test]
85548636 async fn run_user_shell_command_does_not_set_reference_context_item ( ) {
85558637 let ( session, _turn_context, rx) = make_session_and_context_with_rx ( ) . await ;
0 commit comments