@@ -80,6 +80,10 @@ pub(crate) struct GuardianReviewSessionManager {
8080struct GuardianReviewSessionState {
8181 trunk : Option < Arc < GuardianReviewSession > > ,
8282 ephemeral_reviews : Vec < Arc < GuardianReviewSession > > ,
83+ // Parent-session history index captured after the latest terminal guardian
84+ // review. This remains authoritative even when no reusable trunk is
85+ // currently cached.
86+ parent_history_boundary : Option < usize > ,
8387}
8488
8589struct GuardianReviewSession {
@@ -89,9 +93,9 @@ struct GuardianReviewSession {
8993 has_prior_review : AtomicBool ,
9094 review_lock : Mutex < ( ) > ,
9195 last_committed_rollout_items : Mutex < Option < Vec < RolloutItem > > > ,
92- // Parent-session history index captured after the latest terminal guardian
93- // review. Future guardian prompts use it to slice parent transcript
94- // evidence without persisting extra rollout metadata.
96+ // Mirror of the manager checkpoint while this reusable trunk is alive.
97+ // Keeping it on the trunk preserves the boundary across config-driven trunk
98+ // replacement without persisting extra rollout metadata.
9599 parent_history_boundary : Mutex < Option < usize > > ,
96100}
97101
@@ -159,10 +163,6 @@ impl GuardianReviewSessionReuseKey {
159163}
160164
161165impl GuardianReviewSession {
162- async fn parent_history_boundary ( & self ) -> Option < usize > {
163- * self . parent_history_boundary . lock ( ) . await
164- }
165-
166166 async fn set_parent_history_boundary ( & self , boundary : Option < usize > ) {
167167 * self . parent_history_boundary . lock ( ) . await = boundary;
168168 }
@@ -241,15 +241,15 @@ impl Drop for EphemeralReviewCleanup {
241241
242242impl GuardianReviewSessionManager {
243243 pub ( crate ) async fn parent_history_boundary ( & self ) -> Option < usize > {
244- let trunk = self . state . lock ( ) . await . trunk . clone ( ) ;
245- match trunk {
246- Some ( trunk) => trunk. parent_history_boundary ( ) . await ,
247- None => None ,
248- }
244+ self . state . lock ( ) . await . parent_history_boundary
249245 }
250246
251247 pub ( crate ) async fn set_parent_history_boundary ( & self , boundary : Option < usize > ) {
252- let trunk = self . state . lock ( ) . await . trunk . clone ( ) ;
248+ let trunk = {
249+ let mut state = self . state . lock ( ) . await ;
250+ state. parent_history_boundary = boundary;
251+ state. trunk . clone ( )
252+ } ;
253253 if let Some ( trunk) = trunk {
254254 trunk. set_parent_history_boundary ( boundary) . await ;
255255 }
@@ -278,7 +278,6 @@ impl GuardianReviewSessionManager {
278278 let deadline = tokio:: time:: Instant :: now ( ) + GUARDIAN_REVIEW_TIMEOUT ;
279279 let next_reuse_key = GuardianReviewSessionReuseKey :: from_spawn_config ( & params. spawn_config ) ;
280280 let mut stale_trunk_to_shutdown = None ;
281- let mut spawned_replacement_trunk = false ;
282281 let trunk_candidate = match run_before_review_deadline (
283282 deadline,
284283 params. external_cancel . as_ref ( ) ,
@@ -295,6 +294,7 @@ impl GuardianReviewSessionManager {
295294 }
296295
297296 if state. trunk . is_none ( ) {
297+ let parent_history_boundary = state. parent_history_boundary ;
298298 let spawn_cancel_token = CancellationToken :: new ( ) ;
299299 let review_session = match run_before_review_deadline_with_cancel (
300300 deadline,
@@ -306,6 +306,7 @@ impl GuardianReviewSessionManager {
306306 next_reuse_key. clone ( ) ,
307307 spawn_cancel_token. clone ( ) ,
308308 /*initial_history*/ None ,
309+ parent_history_boundary,
309310 ) ) ,
310311 )
311312 . await
@@ -316,7 +317,6 @@ impl GuardianReviewSessionManager {
316317 }
317318 Err ( outcome) => return outcome,
318319 } ;
319- spawned_replacement_trunk = true ;
320320 state. trunk = Some ( Arc :: clone ( & review_session) ) ;
321321 }
322322
@@ -325,14 +325,6 @@ impl GuardianReviewSessionManager {
325325 Err ( outcome) => return outcome,
326326 } ;
327327
328- if spawned_replacement_trunk
329- && let ( Some ( stale_trunk) , Some ( trunk) ) =
330- ( stale_trunk_to_shutdown. as_ref ( ) , trunk_candidate. as_ref ( ) )
331- {
332- let boundary = stale_trunk. parent_history_boundary ( ) . await ;
333- trunk. set_parent_history_boundary ( boundary) . await ;
334- }
335-
336328 if let Some ( review_session) = stale_trunk_to_shutdown {
337329 review_session. shutdown_in_background ( ) ;
338330 }
@@ -386,14 +378,15 @@ impl GuardianReviewSessionManager {
386378 let reuse_key = GuardianReviewSessionReuseKey :: from_spawn_config (
387379 codex. session . get_config ( ) . await . as_ref ( ) ,
388380 ) ;
389- self . state . lock ( ) . await . trunk = Some ( Arc :: new ( GuardianReviewSession {
381+ let mut state = self . state . lock ( ) . await ;
382+ state. trunk = Some ( Arc :: new ( GuardianReviewSession {
390383 reuse_key,
391384 codex,
392385 cancel_token : CancellationToken :: new ( ) ,
393386 has_prior_review : AtomicBool :: new ( false ) ,
394387 review_lock : Mutex :: new ( ( ) ) ,
395388 last_committed_rollout_items : Mutex :: new ( None ) ,
396- parent_history_boundary : Mutex :: new ( None ) ,
389+ parent_history_boundary : Mutex :: new ( state . parent_history_boundary ) ,
397390 } ) ) ;
398391 }
399392
@@ -473,6 +466,7 @@ impl GuardianReviewSessionManager {
473466 reuse_key,
474467 spawn_cancel_token. clone ( ) ,
475468 initial_history,
469+ /*parent_history_boundary*/ None ,
476470 ) ) ,
477471 )
478472 . await
@@ -501,6 +495,7 @@ async fn spawn_guardian_review_session(
501495 reuse_key : GuardianReviewSessionReuseKey ,
502496 cancel_token : CancellationToken ,
503497 initial_history : Option < InitialHistory > ,
498+ parent_history_boundary : Option < usize > ,
504499) -> anyhow:: Result < GuardianReviewSession > {
505500 let has_prior_review = initial_history. is_some ( ) ;
506501 let codex = run_codex_thread_interactive (
@@ -522,7 +517,7 @@ async fn spawn_guardian_review_session(
522517 has_prior_review : AtomicBool :: new ( has_prior_review) ,
523518 review_lock : Mutex :: new ( ( ) ) ,
524519 last_committed_rollout_items : Mutex :: new ( None ) ,
525- parent_history_boundary : Mutex :: new ( None ) ,
520+ parent_history_boundary : Mutex :: new ( parent_history_boundary ) ,
526521 } )
527522}
528523
@@ -911,4 +906,27 @@ mod tests {
911906 assert_eq ! ( outcome. unwrap( ) , 42 ) ;
912907 assert ! ( !cancel_token. is_cancelled( ) ) ;
913908 }
909+
910+ #[ tokio:: test( flavor = "current_thread" ) ]
911+ async fn parent_history_boundary_persists_without_cached_trunk ( ) {
912+ let manager = GuardianReviewSessionManager :: default ( ) ;
913+
914+ manager. set_parent_history_boundary ( Some ( 7 ) ) . await ;
915+ assert_eq ! ( manager. parent_history_boundary( ) . await , Some ( 7 ) ) ;
916+
917+ let state = manager. state . lock ( ) . await ;
918+ assert_eq ! ( state. parent_history_boundary, Some ( 7 ) ) ;
919+ assert ! ( state. trunk. is_none( ) ) ;
920+ }
921+
922+ #[ tokio:: test( flavor = "current_thread" ) ]
923+ async fn clearing_parent_history_boundary_without_cached_trunk_updates_manager_state ( ) {
924+ let manager = GuardianReviewSessionManager :: default ( ) ;
925+
926+ manager. set_parent_history_boundary ( Some ( 7 ) ) . await ;
927+ manager. set_parent_history_boundary ( None ) . await ;
928+
929+ assert_eq ! ( manager. parent_history_boundary( ) . await , None ) ;
930+ assert_eq ! ( manager. state. lock( ) . await . parent_history_boundary, None ) ;
931+ }
914932}
0 commit comments