@@ -2300,10 +2300,19 @@ async fn handle_supervisor_message(
23002300 state
23012301 . supervisor_sessions
23022302 . finalize_config_update ( sandbox_id, session_id, & completed, false ) ;
2303+ // Either projection can arrive first. Once the supervisor has
2304+ // staged one half, rebuild both current projections so the
2305+ // matching half and the deferred admission result are driven
2306+ // to a fixed point even when no further mutation occurs.
2307+ crate :: config_delivery:: publish_sandbox_components (
2308+ state,
2309+ sandbox_id,
2310+ crate :: config_delivery:: ConfigComponents :: SANDBOX_AND_PROVIDER ,
2311+ ) ;
23032312 debug ! (
23042313 sandbox_id,
23052314 session_id,
2306- "supervisor configuration generation is waiting for its matching component"
2315+ "supervisor configuration generation is waiting for its matching component; scheduled reconciliation "
23072316 ) ;
23082317 return ;
23092318 }
@@ -3104,11 +3113,19 @@ mod tests {
31043113 }
31053114
31063115 #[ tokio:: test]
3107- async fn generation_pending_result_is_not_persisted_or_suppressed ( ) {
3116+ async fn generation_pending_result_schedules_reconciliation_without_persistence ( ) {
31083117 use openshell_core:: proto:: { ConfigApplyFailure , ConfigurationAdmissionState } ;
31093118
31103119 let sandbox_id = "sb-generation-pending-retry" ;
31113120 let state = state_with_sandbox ( sandbox_id) . await ;
3121+ let mut stored_sandbox = state
3122+ . store
3123+ . get_message :: < Sandbox > ( sandbox_id)
3124+ . await
3125+ . unwrap ( )
3126+ . expect ( "sandbox is persisted" ) ;
3127+ stored_sandbox. spec = Some ( SandboxSpec :: default ( ) ) ;
3128+ state. store . put_message ( & stored_sandbox) . await . unwrap ( ) ;
31123129 let ( tx, mut rx) = mpsc:: channel ( 4 ) ;
31133130 let ( shutdown_tx, _shutdown_rx) = oneshot:: channel ( ) ;
31143131 state. supervisor_sessions . register (
@@ -3199,12 +3216,30 @@ mod tests {
31993216 . is_none( ) ,
32003217 "a transient generation mismatch must not persist rejected admission"
32013218 ) ;
3202- assert_eq ! (
3203- state
3204- . supervisor_sessions
3205- . deliver_config( sandbox_id, message) ,
3206- DeliveryDisposition :: Enqueued
3207- ) ;
3219+ let redriven_components = tokio:: time:: timeout ( Duration :: from_secs ( 2 ) , async {
3220+ let mut components = Vec :: new ( ) ;
3221+ while components. len ( ) < 2 {
3222+ let Some ( gateway_message:: Payload :: ConfigUpdate ( update) ) = rx
3223+ . recv ( )
3224+ . await
3225+ . expect ( "redriven configuration update" )
3226+ . payload
3227+ else {
3228+ continue ;
3229+ } ;
3230+ components. push ( match update. component . expect ( "redriven component" ) {
3231+ config_update:: Component :: SandboxConfig ( _) => ConfigComponent :: SandboxConfig ,
3232+ config_update:: Component :: ProviderEnvironment ( _) => {
3233+ ConfigComponent :: ProviderEnvironment
3234+ }
3235+ } ) ;
3236+ }
3237+ components
3238+ } )
3239+ . await
3240+ . expect ( "generation mismatch schedules prompt reconciliation" ) ;
3241+ assert ! ( redriven_components. contains( & ConfigComponent :: SandboxConfig ) ) ;
3242+ assert ! ( redriven_components. contains( & ConfigComponent :: ProviderEnvironment ) ) ;
32083243 }
32093244
32103245 #[ tokio:: test]
0 commit comments