@@ -842,14 +842,19 @@ fn apply_once(
842842 let mutation_exempt_mid =
843843 latest_assistant_mutation_exempt_mid ( & req. messages , serializer_profile, req. mid_turn ) ;
844844 let cc_u1_active = crate :: cc_u1_active ( serializer_profile, req. tool_present ) ;
845+ let tagging_surface_requested =
846+ crate :: tagging_surface_active ( serializer_profile, req. tool_present ) ;
845847 let transform_snapshot = store. load_transform_snapshot ( & req. session_id ) ?;
846848 let loaded = transform_snapshot. loaded ;
847849 let overlay_frontier = transform_snapshot. overlay_frontier ;
848- let surface_transition = serializer_profile == Some ( SerializerProfile :: ClaudeCodeAnthropic )
849- && loaded. meta . cc_u1_active != cc_u1_active;
850+ // Legacy sessions stored the CC latch before the generic surface latch existed.
851+ // Treat that old true value as the generic latch so an upgrade does not repeat a fold.
852+ let persisted_tagging_surface_active =
853+ loaded. meta . tagging_surface_active || loaded. meta . cc_u1_active ;
854+ let surface_transition = persisted_tagging_surface_active != tagging_surface_requested;
850855 let surface_state = if surface_transition {
851856 SurfaceState :: Transition
852- } else if cc_u1_active {
857+ } else if tagging_surface_requested {
853858 SurfaceState :: Active
854859 } else {
855860 SurfaceState :: Inactive
@@ -874,7 +879,7 @@ fn apply_once(
874879 . filter ( |epoch| * epoch != 0 )
875880 . map ( |epoch| format ! ( "mpe{epoch}" ) )
876881 . unwrap_or_default ( ) ;
877- let tagger_feature_epoch = match crate :: tagger_feature_epoch ( cc_u1_active ) {
882+ let tagger_feature_epoch = match crate :: tagger_feature_epoch ( tagging_surface_requested ) {
878883 0 => String :: new ( ) ,
879884 epoch => format ! ( "tfe{epoch}" ) ,
880885 } ;
@@ -890,7 +895,7 @@ fn apply_once(
890895 tagger_feature_epoch : tagger_feature_epoch. clone ( ) ,
891896 } ,
892897 ) ;
893- let tagging_active = cc_u1_active && loaded . meta . cc_u1_active ;
898+ let tagging_active = tagging_surface_requested && persisted_tagging_surface_active ;
894899 // Previously stored overlay rows may still replay when boundary-lineage validation
895900 // later forces pass-through. Decisions from this request stay in memory until the
896901 // final cache-state compare-and-swap accepts the pass.
@@ -1152,10 +1157,10 @@ fn apply_once(
11521157 || reconcile_hard_due
11531158 || emergency_arm_engaged;
11541159 // Profile defaults remain conservative, while the request-local tool signal enables
1155- // full-array tail reclaim for the Claude Code profile . A false request therefore
1160+ // full-array tail reclaim for an active tagging surface . A false request therefore
11561161 // retains the exact pre-capability behavior without changing the global profile table.
1157- let tail_reclaim_enabled =
1158- serializer_profile . is_none_or ( |profile| healing:: tail_reclaim ( profile) || cc_u1_active ) ;
1162+ let tail_reclaim_enabled = serializer_profile
1163+ . is_none_or ( |profile| healing:: tail_reclaim ( profile) || tagging_surface_requested ) ;
11591164 let producer_gate = tail_reclaim_enabled
11601165 && producer_gate (
11611166 scheduler_outcome. pass ,
@@ -1170,7 +1175,7 @@ fn apply_once(
11701175 PassClass :: Defer
11711176 } ;
11721177 let tail_for_selection = tail_sel_items ( & live, loaded. meta . coverage_ordinal ) ;
1173- let mut protected_block_ids = if cc_u1_active {
1178+ let mut protected_block_ids = if tagging_surface_requested {
11741179 newest_active_tag_block_ids (
11751180 & loaded. core ,
11761181 & loaded. meta ,
@@ -1298,6 +1303,7 @@ fn apply_once(
12981303 }
12991304 apply_ingress_meta ( & mut meta, req, & projection, provisional_tail_mid) ;
13001305 meta. cc_u1_active = cc_u1_active;
1306+ meta. tagging_surface_active = tagging_surface_requested;
13011307 if cc_u1_active {
13021308 meta. last_serializer_profile = req. serializer_profile . clone ( ) ;
13031309 }
@@ -9136,6 +9142,97 @@ mod tests {
91369142 assert ! ( s. load_tags_for_session( "inert" ) . unwrap( ) . is_empty( ) ) ;
91379143 }
91389144
9145+ #[ test]
9146+ fn opencode_tagging_surface_tags_tool_results_and_replays_byte_stably ( ) {
9147+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
9148+ let s = store ( dir. path ( ) ) ;
9149+ let messages = vec ! [
9150+ wire_tool_call( "call-1" , 1 , "call_result-1" ) ,
9151+ wire_tool_result(
9152+ "result-1" ,
9153+ 2 ,
9154+ json!( { "kind" : { "type" : "text" , "text" : "tool output" } } ) ,
9155+ ) ,
9156+ ] ;
9157+ let request = active_opencode_req ( "opencode-tags" , "cfg0" , messages) ;
9158+
9159+ let transition = run ( & s, & request, & spine ( ) ) ;
9160+ assert_eq ! ( transition. surface_state, SurfaceState :: Transition ) ;
9161+ assert ! ( !serde_json:: to_string( transition. messages( ) )
9162+ . unwrap( )
9163+ . contains( "§1§" ) ) ;
9164+
9165+ let active = run ( & s, & request, & spine ( ) ) ;
9166+ assert_eq ! ( active. surface_state, SurfaceState :: Active ) ;
9167+ let active_bytes = serde_json:: to_vec ( active. messages ( ) ) . unwrap ( ) ;
9168+ assert ! ( serde_json:: to_string( active. messages( ) )
9169+ . unwrap( )
9170+ . contains( "§1§ tool output" ) ) ;
9171+ assert_eq ! ( s. load_tags_for_session( "opencode-tags" ) . unwrap( ) . len( ) , 1 ) ;
9172+
9173+ let replay = run ( & s, & request, & spine ( ) ) ;
9174+ assert_eq ! ( serde_json:: to_vec( replay. messages( ) ) . unwrap( ) , active_bytes) ;
9175+ }
9176+
9177+ #[ test]
9178+ fn opencode_tool_absent_keeps_overlay_bytes_disabled ( ) {
9179+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
9180+ let s = store ( dir. path ( ) ) ;
9181+ let request = opencode_req (
9182+ "opencode-inactive" ,
9183+ "cfg0" ,
9184+ vec ! [ item( "m1" , 1 , "plain output" ) ] ,
9185+ ) ;
9186+
9187+ let first = run ( & s, & request, & spine ( ) ) ;
9188+ let replay = run ( & s, & request, & spine ( ) ) ;
9189+ assert_eq ! ( first. surface_state, SurfaceState :: Inactive ) ;
9190+ assert_eq ! (
9191+ serde_json:: to_vec( first. messages( ) ) . unwrap( ) ,
9192+ serde_json:: to_vec( replay. messages( ) ) . unwrap( )
9193+ ) ;
9194+ assert ! ( !serde_json:: to_string( first. messages( ) )
9195+ . unwrap( )
9196+ . contains( "§1§" ) ) ;
9197+ assert ! ( s
9198+ . load_tags_for_session( "opencode-inactive" )
9199+ . unwrap( )
9200+ . is_empty( ) ) ;
9201+ }
9202+
9203+ #[ test]
9204+ fn opencode_surface_flip_folds_once_before_rendering_tags ( ) {
9205+ let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
9206+ let s = store ( dir. path ( ) ) ;
9207+ let mut request =
9208+ opencode_req ( "opencode-flip" , "cfg0" , vec ! [ item( "m1" , 1 , "stable bytes" ) ] ) ;
9209+
9210+ let before = run ( & s, & request, & spine ( ) ) ;
9211+ let before_config = s. load ( "opencode-flip" ) . unwrap ( ) . meta . last_render_config ;
9212+ assert_eq ! ( before. action, "HARD" ) ;
9213+ assert ! ( !before_config. contains( "tfe:" ) ) ;
9214+
9215+ request. tool_present = true ;
9216+ let transition = run ( & s, & request, & spine ( ) ) ;
9217+ let transitioned_config = s. load ( "opencode-flip" ) . unwrap ( ) . meta . last_render_config ;
9218+ assert_eq ! ( transition. action, "HARD" ) ;
9219+ assert_ne ! ( transitioned_config, before_config) ;
9220+ assert ! ( transitioned_config. contains( "tfe:4:tfe3" ) ) ;
9221+ assert ! ( !serde_json:: to_string( transition. messages( ) )
9222+ . unwrap( )
9223+ . contains( "§1§" ) ) ;
9224+
9225+ let active = run ( & s, & request, & spine ( ) ) ;
9226+ assert_ne ! ( active. action, "HARD" ) ;
9227+ assert_eq ! (
9228+ s. load( "opencode-flip" ) . unwrap( ) . meta. last_render_config,
9229+ transitioned_config
9230+ ) ;
9231+ assert ! ( serde_json:: to_string( active. messages( ) )
9232+ . unwrap( )
9233+ . contains( "§1§ stable bytes" ) ) ;
9234+ }
9235+
91399236 #[ test]
91409237 fn tagger_flip_hards_before_committed_identity_can_render_tags ( ) {
91419238 let dir = tempfile:: tempdir ( ) . unwrap ( ) ;
@@ -11617,6 +11714,20 @@ mod tests {
1161711714 request
1161811715 }
1161911716
11717+ fn opencode_req ( session : & str , cfg : & str , messages : Vec < CkIngressMessage > ) -> TransformRequest {
11718+ profile_req ( SerializerProfile :: OpencodeAiSdk , session, cfg, messages)
11719+ }
11720+
11721+ fn active_opencode_req (
11722+ session : & str ,
11723+ cfg : & str ,
11724+ messages : Vec < CkIngressMessage > ,
11725+ ) -> TransformRequest {
11726+ let mut request = opencode_req ( session, cfg, messages) ;
11727+ request. tool_present = true ;
11728+ request
11729+ }
11730+
1162011731 fn effective_render_config_with_epochs (
1162111732 store : & McStore ,
1162211733 cfg : & str ,
0 commit comments