@@ -531,15 +531,24 @@ pub fn default_config_path() -> PathBuf {
531531/// uses it for the server; RFC-007 §D1 extends it to the CLI).
532532pub const CONFIG_PATH_ENV : & str = "OMNIGRAPH_CONFIG" ;
533533
534+ /// RFC-008 stage 4 — opt-in strict mode: when set, loading a legacy
535+ /// `omnigraph.yaml` is a hard error instead of a warning. For teams that
536+ /// finished migrating and want regressions caught (a stray legacy file
537+ /// would otherwise silently outrank operator config during the window).
538+ /// The rehearsal for stage 5's removal.
539+ pub const NO_LEGACY_CONFIG_ENV : & str = "OMNIGRAPH_NO_LEGACY_CONFIG" ;
540+
534541pub fn load_config ( config_path : Option < & PathBuf > ) -> Result < OmnigraphConfig > {
535542 let env_path = env:: var_os ( CONFIG_PATH_ENV ) . map ( PathBuf :: from) ;
536- load_config_in ( & env:: current_dir ( ) ?, config_path, env_path. as_ref ( ) )
543+ let strict = env:: var_os ( NO_LEGACY_CONFIG_ENV ) . is_some ( ) ;
544+ load_config_in ( & env:: current_dir ( ) ?, config_path, env_path. as_ref ( ) , strict)
537545}
538546
539547fn load_config_in (
540548 cwd : & Path ,
541549 config_path : Option < & PathBuf > ,
542550 env_path : Option < & PathBuf > ,
551+ strict_no_legacy : bool ,
543552) -> Result < OmnigraphConfig > {
544553 // Precedence: explicit --config flag > $OMNIGRAPH_CONFIG > ./omnigraph.yaml.
545554 let explicit_path = config_path. or ( env_path) . cloned ( ) ;
@@ -549,6 +558,14 @@ fn load_config_in(
549558 } ) ;
550559
551560 let mut config = if let Some ( path) = & config_path {
561+ if strict_no_legacy {
562+ // Strict refuses the FILE, not its absence — flag-less
563+ // invocations on migrated setups keep working.
564+ bail ! (
565+ "legacy config '{}' refused: {NO_LEGACY_CONFIG_ENV} is set (RFC-008 strict mode); run `omnigraph config migrate`, then remove the file — or unset the variable" ,
566+ path. display( )
567+ ) ;
568+ }
552569 let text = fs:: read_to_string ( path) ?;
553570 warn_yaml_deprecation_once ( path, & text) ;
554571 serde_yaml:: from_str :: < OmnigraphConfig > ( & text) ?
@@ -665,19 +682,38 @@ mod tests {
665682 fs:: write ( & env_path, "cli:\n actor: act-env\n " ) . unwrap ( ) ;
666683
667684 // $OMNIGRAPH_CONFIG used when no flag…
668- let config = load_config_in ( temp. path ( ) , None , Some ( & env_path) ) . unwrap ( ) ;
685+ let config = load_config_in ( temp. path ( ) , None , Some ( & env_path) , false ) . unwrap ( ) ;
669686 assert_eq ! ( config. cli. actor. as_deref( ) , Some ( "act-env" ) ) ;
670687
671688 // …loses to an explicit --config…
672- let config = load_config_in ( temp. path ( ) , Some ( & flag_path) , Some ( & env_path) ) . unwrap ( ) ;
689+ let config = load_config_in ( temp. path ( ) , Some ( & flag_path) , Some ( & env_path) , false ) . unwrap ( ) ;
673690 assert_eq ! ( config. cli. actor. as_deref( ) , Some ( "act-flag" ) ) ;
674691
675692 // …and beats the cwd default file.
676693 fs:: write ( temp. path ( ) . join ( "omnigraph.yaml" ) , "cli:\n actor: act-cwd\n " ) . unwrap ( ) ;
677- let config = load_config_in ( temp. path ( ) , None , Some ( & env_path) ) . unwrap ( ) ;
694+ let config = load_config_in ( temp. path ( ) , None , Some ( & env_path) , false ) . unwrap ( ) ;
678695 assert_eq ! ( config. cli. actor. as_deref( ) , Some ( "act-env" ) ) ;
679696 }
680697
698+ #[ test]
699+ fn strict_mode_refuses_the_file_not_its_absence ( ) {
700+ let temp = tempdir ( ) . unwrap ( ) ;
701+ // No file: strict mode changes nothing (defaults load).
702+ let config = load_config_in ( temp. path ( ) , None , None , true ) . unwrap ( ) ;
703+ assert ! ( config. cli. actor. is_none( ) ) ;
704+
705+ // File present: strict refuses with the migrate pointer.
706+ fs:: write ( temp. path ( ) . join ( "omnigraph.yaml" ) , "cli:\n actor: a\n " ) . unwrap ( ) ;
707+ let err = load_config_in ( temp. path ( ) , None , None , true ) . unwrap_err ( ) ;
708+ let message = err. to_string ( ) ;
709+ assert ! (
710+ message. contains( "OMNIGRAPH_NO_LEGACY_CONFIG" ) && message. contains( "config migrate" ) ,
711+ "{message}"
712+ ) ;
713+ // Without strict, the same file loads.
714+ assert ! ( load_config_in( temp. path( ) , None , None , false ) . is_ok( ) ) ;
715+ }
716+
681717 #[ test]
682718 fn yaml_deprecation_lines_name_present_keys_only ( ) {
683719 let lines = super :: yaml_deprecation_lines (
@@ -717,7 +753,7 @@ policy: {}
717753 )
718754 . unwrap ( ) ;
719755
720- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
756+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
721757 assert_eq ! ( config. cli_graph_name( ) , Some ( "local" ) ) ;
722758 assert_eq ! ( config. cli_branch( ) , "main" ) ;
723759 assert_eq ! ( config. cli_output_format( ) , ReadOutputFormat :: Kv ) ;
@@ -752,7 +788,7 @@ policy: {}
752788 )
753789 . unwrap ( ) ;
754790
755- let config = load_config_in ( & child, None , None ) . unwrap ( ) ;
791+ let config = load_config_in ( & child, None , None , false ) . unwrap ( ) ;
756792 assert ! ( config. graphs. is_empty( ) ) ;
757793 }
758794
@@ -776,7 +812,7 @@ policy: {}
776812 "graphs:\n local:\n uri: ./demo.omni\n " ,
777813 )
778814 . unwrap ( ) ;
779- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
815+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
780816
781817 // A known graph passes through unchanged.
782818 assert_eq ! ( config. resolve_graph_selection( Some ( "local" ) ) . unwrap( ) , Some ( "local" ) ) ;
@@ -799,7 +835,7 @@ policy: {}
799835 "graphs:\n local:\n uri: ./demo.omni\n policy:\n file: ./top.yaml\n " ,
800836 )
801837 . unwrap ( ) ;
802- let incoherent = load_config_in ( temp2. path ( ) , None , None ) . unwrap ( ) ;
838+ let incoherent = load_config_in ( temp2. path ( ) , None , None , false ) . unwrap ( ) ;
803839 let err = incoherent
804840 . resolve_graph_selection ( Some ( "local" ) )
805841 . unwrap_err ( )
@@ -824,7 +860,7 @@ policy: {}
824860 server:\n graph: local\n cli:\n graph: prod\n ",
825861 )
826862 . unwrap ( ) ;
827- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
863+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
828864 assert_eq ! (
829865 config. resolve_policy_tooling_graph_selection( ) . unwrap( ) ,
830866 Some ( "prod" )
@@ -836,15 +872,15 @@ policy: {}
836872 "graphs:\n local:\n uri: ./local.omni\n server:\n graph: local\n " ,
837873 )
838874 . unwrap ( ) ;
839- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
875+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
840876 assert_eq ! (
841877 config. resolve_policy_tooling_graph_selection( ) . unwrap( ) ,
842878 Some ( "local" )
843879 ) ;
844880
845881 let temp = tempdir ( ) . unwrap ( ) ;
846882 fs:: write ( temp. path ( ) . join ( "omnigraph.yaml" ) , "policy: {}\n " ) . unwrap ( ) ;
847- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
883+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
848884 assert_eq ! ( config. resolve_policy_tooling_graph_selection( ) . unwrap( ) , None ) ;
849885
850886 let temp = tempdir ( ) . unwrap ( ) ;
@@ -853,7 +889,7 @@ policy: {}
853889 "graphs:\n local:\n uri: ./local.omni\n server:\n graph: ghost\n " ,
854890 )
855891 . unwrap ( ) ;
856- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
892+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
857893 let err = config
858894 . resolve_policy_tooling_graph_selection ( )
859895 . unwrap_err ( )
@@ -879,7 +915,7 @@ policy: {}
879915 )
880916 . unwrap ( ) ;
881917
882- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
918+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
883919 let resolved = config. resolve_query_path ( Path :: new ( "test.gq" ) ) . unwrap ( ) ;
884920 assert_eq ! ( resolved, temp. path( ) . join( "queries" ) . join( "test.gq" ) ) ;
885921 }
@@ -896,7 +932,7 @@ policy: {}
896932 fs:: write ( ambient_dir. join ( "local.gq" ) , "query ambient { return {} }" ) . unwrap ( ) ;
897933
898934 let config =
899- load_config_in ( & ambient_dir, Some ( & config_dir. join ( "omnigraph.yaml" ) ) , None ) . unwrap ( ) ;
935+ load_config_in ( & ambient_dir, Some ( & config_dir. join ( "omnigraph.yaml" ) ) , None , false ) . unwrap ( ) ;
900936 let resolved = config. resolve_query_path ( Path :: new ( "local.gq" ) ) . unwrap ( ) ;
901937
902938 assert_eq ! ( resolved, config_dir. join( "local.gq" ) ) ;
@@ -926,7 +962,7 @@ queries:
926962 )
927963 . unwrap ( ) ;
928964
929- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
965+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
930966
931967 // Per-graph registry (multi-graph mode).
932968 let prod = config. target_query_entries ( "prod" ) . unwrap ( ) ;
@@ -967,7 +1003,7 @@ queries:
9671003 policy:\n file: ./prod.yaml\n bare:\n uri: s3://b/bare\n ",
9681004 )
9691005 . unwrap ( ) ;
970- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
1006+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
9711007
9721008 // Named graph with its own policy → per-graph (not top-level).
9731009 assert ! (
@@ -1003,7 +1039,7 @@ queries:
10031039 )
10041040 . unwrap ( ) ;
10051041
1006- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
1042+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
10071043 // Additive: no `queries:` anywhere → empty registries everywhere.
10081044 assert ! ( config. query_entries( ) . is_empty( ) ) ;
10091045 assert ! (
@@ -1023,7 +1059,7 @@ queries:
10231059 )
10241060 . unwrap ( ) ;
10251061
1026- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
1062+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
10271063 assert_eq ! (
10281064 config. resolve_policy_file( ) . unwrap( ) ,
10291065 temp. path( ) . join( "policy.yaml" )
@@ -1046,7 +1082,7 @@ cli:
10461082 )
10471083 . unwrap ( ) ;
10481084
1049- let config = load_config_in ( temp. path ( ) , None , None ) . unwrap ( ) ;
1085+ let config = load_config_in ( temp. path ( ) , None , None , false ) . unwrap ( ) ;
10501086 assert_eq ! (
10511087 config. graph_bearer_token_env(
10521088 Some ( "https://override.example.com" ) ,
0 commit comments