@@ -405,6 +405,20 @@ const strategyProfiles = __test.normalizeStrategyProfilesPayload(
405405 can_switch_live : true ,
406406 allowed_execution_modes : [ "live" , "dry_run" ] ,
407407 } ,
408+ {
409+ profile : "legacy_continuity_profile" ,
410+ label : "Legacy continuity profile" ,
411+ domain : "us_equity" ,
412+ runtime_enabled : false ,
413+ lifecycle_stage : "research_active" ,
414+ can_switch_live : false ,
415+ allowed_execution_modes : [ "dry_run" ] ,
416+ blocked_live_reason : "candidate_gate_remains_closed" ,
417+ live_continuity : {
418+ eligible : true ,
419+ allowed_platforms : [ "ibkr" ] ,
420+ } ,
421+ } ,
408422 {
409423 profile : "us_equity_combo_leveraged" ,
410424 label : "US Alpha Combo" ,
@@ -470,17 +484,19 @@ assert.equal(strategyProfiles[0].option_growth_overlay_nav_budget_ratio, "0.03")
470484assert . equal ( strategyProfiles [ 0 ] . option_income_overlay_enabled , false ) ;
471485assert . equal ( strategyProfiles [ 0 ] . latest_evidence_status , "live_allowed" ) ;
472486assert . equal ( strategyProfiles [ 0 ] . plugin_gate_status , "live_allowed" ) ;
473- assert . equal ( strategyProfiles [ 2 ] . lifecycle_stage , "research_active" ) ;
474- assert . equal ( strategyProfiles [ 2 ] . can_switch_live , false ) ;
475- assert . deepEqual ( strategyProfiles [ 2 ] . allowed_execution_modes , [ "dry_run" ] ) ;
476- assert . equal ( strategyProfiles [ 2 ] . blocked_live_reason , "promotion_required" ) ;
477- assert . equal ( strategyProfiles [ 2 ] . latest_evidence_status , "research_only" ) ;
478- assert . equal ( strategyProfiles [ 2 ] . plugin_gate_status , "blocked" ) ;
479- assert . equal ( strategyProfiles [ 3 ] . dca_enabled , true ) ;
480- assert . equal ( strategyProfiles [ 3 ] . dca_default_mode , "fixed" ) ;
481- assert . equal ( strategyProfiles [ 3 ] . dca_default_base_investment_usd , "1000" ) ;
482- assert . equal ( strategyProfiles [ 4 ] . lifecycle_stage , "shadow_active" ) ;
483- assert . equal ( strategyProfiles [ 5 ] . lifecycle_stage , "live_enabled" ) ;
487+ const legacyContinuityProfile = strategyProfiles . find ( ( item ) => item . profile === "legacy_continuity_profile" ) ;
488+ assert . deepEqual ( legacyContinuityProfile . live_continuity , { eligible : true , allowed_platforms : [ "ibkr" ] } ) ;
489+ assert . equal ( strategyProfiles [ 3 ] . lifecycle_stage , "research_active" ) ;
490+ assert . equal ( strategyProfiles [ 3 ] . can_switch_live , false ) ;
491+ assert . deepEqual ( strategyProfiles [ 3 ] . allowed_execution_modes , [ "dry_run" ] ) ;
492+ assert . equal ( strategyProfiles [ 3 ] . blocked_live_reason , "promotion_required" ) ;
493+ assert . equal ( strategyProfiles [ 3 ] . latest_evidence_status , "research_only" ) ;
494+ assert . equal ( strategyProfiles [ 3 ] . plugin_gate_status , "blocked" ) ;
495+ assert . equal ( strategyProfiles [ 4 ] . dca_enabled , true ) ;
496+ assert . equal ( strategyProfiles [ 4 ] . dca_default_mode , "fixed" ) ;
497+ assert . equal ( strategyProfiles [ 4 ] . dca_default_base_investment_usd , "1000" ) ;
498+ assert . equal ( strategyProfiles [ 5 ] . lifecycle_stage , "shadow_active" ) ;
499+ assert . equal ( strategyProfiles [ 6 ] . lifecycle_stage , "live_enabled" ) ;
484500
485501assert . doesNotThrow ( ( ) =>
486502 __test . assertStrategyAllowedForAccount (
@@ -644,6 +660,92 @@ assert.deepEqual(kvUnboundSyncBody.account_options_sync, {
644660 skipped : true ,
645661} ) ;
646662
663+ const legacyContinuityKv = new Map ( [
664+ [ "account_options" , JSON . stringify ( accountOptions ) ] ,
665+ [ "strategy_profiles" , JSON . stringify ( strategyProfiles ) ] ,
666+ ] ) ;
667+ const legacyContinuityEnv = {
668+ STRATEGY_SWITCH_SYNC_TOKEN : "test-sync-token" ,
669+ STRATEGY_SWITCH_CONFIG : {
670+ get : async ( key ) => legacyContinuityKv . get ( key ) || null ,
671+ put : async ( key , value ) => legacyContinuityKv . set ( key , value ) ,
672+ } ,
673+ } ;
674+ const legacyContinuityPayload = {
675+ platform : "ibkr" ,
676+ target_name : "legacy-ibkr-route" ,
677+ account_selector : "LEGACY_IBKR" ,
678+ deployment_selector : "legacy-ibkr-route" ,
679+ account_scope : "legacy-ibkr-route" ,
680+ service_name : "interactive-brokers-legacy-ibkr-route-service" ,
681+ strategy_profile : "legacy_continuity_profile" ,
682+ execution_mode : "live" ,
683+ live_continuity_state : "RECONCILE_ONLY" ,
684+ live_continuity_baseline_id : "legacy-ibkr-lkg-20260830" ,
685+ live_continuity_captured_at : "2026-08-30" ,
686+ variable_scope : "default" ,
687+ plugin_mode : "current" ,
688+ option_overlay_mode : "current" ,
689+ cash_only_execution_mode : "current" ,
690+ } ;
691+ const legacyContinuitySyncResponse = await worker . fetch (
692+ new Request ( "https://switch.example/api/internal/sync-account-default" , {
693+ method : "POST" ,
694+ headers : {
695+ Authorization : "Bearer test-sync-token" ,
696+ "Content-Type" : "application/json" ,
697+ } ,
698+ body : JSON . stringify ( legacyContinuityPayload ) ,
699+ } ) ,
700+ legacyContinuityEnv ,
701+ ) ;
702+ assert . equal ( legacyContinuitySyncResponse . status , 200 ) ;
703+ const legacyContinuitySyncBody = await legacyContinuitySyncResponse . json ( ) ;
704+ assert . equal ( legacyContinuitySyncBody . ok , true ) ;
705+ assert . equal ( legacyContinuitySyncBody . legacy_continuity_account_registered , true ) ;
706+ const registeredLegacyAccount = JSON . parse ( legacyContinuityKv . get ( "account_options" ) ) . ibkr . find (
707+ ( option ) => option . target_name === "legacy-ibkr-route" ,
708+ ) ;
709+ assert . equal ( registeredLegacyAccount . service_name , "interactive-brokers-legacy-ibkr-route-service" ) ;
710+ assert . deepEqual ( registeredLegacyAccount . supported_domains , [ "us_equity" ] ) ;
711+ assert . equal ( "plugin_mode" in registeredLegacyAccount , false ) ;
712+ const repeatedLegacyContinuitySyncResponse = await worker . fetch (
713+ new Request ( "https://switch.example/api/internal/sync-account-default" , {
714+ method : "POST" ,
715+ headers : {
716+ Authorization : "Bearer test-sync-token" ,
717+ "Content-Type" : "application/json" ,
718+ } ,
719+ body : JSON . stringify ( legacyContinuityPayload ) ,
720+ } ) ,
721+ legacyContinuityEnv ,
722+ ) ;
723+ assert . equal ( repeatedLegacyContinuitySyncResponse . status , 200 ) ;
724+ assert . equal ( ( await repeatedLegacyContinuitySyncResponse . json ( ) ) . legacy_continuity_account_registered , false ) ;
725+ assert . equal (
726+ JSON . parse ( legacyContinuityKv . get ( "account_options" ) ) . ibkr . filter (
727+ ( option ) => option . target_name === "legacy-ibkr-route" ,
728+ ) . length ,
729+ 1 ,
730+ ) ;
731+ const normalizedLegacyContinuityInputs = __test . normalizeSwitchInputs ( legacyContinuityPayload ) ;
732+ assert . doesNotThrow ( ( ) =>
733+ __test . assertStrategyAllowedForAccount (
734+ normalizedLegacyContinuityInputs ,
735+ registeredLegacyAccount ,
736+ strategyProfiles ,
737+ ) ,
738+ ) ;
739+ assert . throws (
740+ ( ) =>
741+ __test . assertStrategyAllowedForAccount (
742+ { ...normalizedLegacyContinuityInputs , live_continuity_state : "NONE" } ,
743+ registeredLegacyAccount ,
744+ strategyProfiles ,
745+ ) ,
746+ / n o t l i v e - e n a b l e d / ,
747+ ) ;
748+
647749const kvUnboundProfileSyncResponse = await worker . fetch (
648750 new Request ( "https://switch.example/api/internal/sync-strategy-profiles" , {
649751 method : "POST" ,
@@ -722,6 +824,9 @@ assert.deepEqual(JSON.parse(normalizedCashOnlyInputs.extra_variables_json), {
722824assert . equal ( "cash_only_execution_mode" in normalizedCashOnlyInputs , false ) ;
723825
724826const workflowYaml = readFileSync ( resolve ( root , ".github/workflows/manual-strategy-switch.yml" ) , "utf8" ) ;
827+ assert . ok ( workflowYaml . includes ( '"live_continuity_state": continuity.get("state", "NONE")' ) ) ;
828+ assert . ok ( workflowYaml . includes ( 'payload["live_continuity_baseline_id"]' ) ) ;
829+ assert . ok ( workflowYaml . includes ( '"cash_only_execution_mode": "current"' ) ) ;
725830const workflowInputs = [ ...workflowYaml . matchAll ( / ^ ( [ A - Z a - z 0 - 9 _ ] + ) : \n d e s c r i p t i o n : / gm) ] . map ( ( match ) => match [ 1 ] ) ;
726831const dispatchInputs = __test . normalizeSwitchInputs ( {
727832 platform : "ibkr" ,
0 commit comments