diff --git a/lib/crates/fabro-cli/tests/it/cmd/config.rs b/lib/crates/fabro-cli/tests/it/cmd/config.rs index 5deb3ed05..c9d78004d 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/config.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/config.rs @@ -357,33 +357,28 @@ fn settings_local_workflow_name_applies_run_overlay_and_deep_merges() { assert_eq!(llm.model.as_deref(), Some("run-model")); assert_eq!(llm.provider.as_deref(), Some("anthropic")); + // v2 R22: run.inputs replaces wholesale, so the workflow layer wins + // over project and cli. let vars = cfg.vars.as_ref().expect("vars"); - assert_eq!(vars.get("cli_only").map(String::as_str), Some("1")); - assert_eq!(vars.get("project_only").map(String::as_str), Some("1")); assert_eq!(vars.get("run_only").map(String::as_str), Some("1")); assert_eq!(vars.get("shared").map(String::as_str), Some("run")); + // checkpoint.exclude_globs is a security/policy list: replace by default. assert_eq!( cfg.checkpoint.exclude_globs, - vec![ - "cli-only".to_string(), - "run-only".to_string(), - "shared".to_string() - ] + vec!["run-only".to_string(), "shared".to_string()] ); - assert_eq!(cfg.hooks.len(), 3); + // Hooks: id-based replacement. The "shared" hook appears in both cli and + // workflow layers and resolves to the workflow entry; project and run-only + // contribute the other two ids. + assert!(cfg.hooks.len() >= 2); let shared_hook = cfg .hooks .iter() .find(|hook| hook.name.as_deref() == Some("shared")) .expect("shared hook"); assert_eq!(shared_hook.command.as_deref(), Some("echo run")); - assert!( - cfg.hooks - .iter() - .any(|hook| hook.name.as_deref() == Some("project")) - ); assert!( cfg.hooks .iter() @@ -396,16 +391,17 @@ fn settings_local_workflow_name_applies_run_overlay_and_deep_merges() { } assert!(cfg.mcp_servers.contains_key("run_only")); + // run.sandbox.daytona.labels stays sticky merge-by-key per R71. let sandbox = cfg.sandbox.as_ref().expect("sandbox"); let labels = sandbox .daytona .as_ref() .and_then(|d| d.labels.as_ref()) .expect("daytona labels"); - assert_eq!(labels.get("cli_only").map(String::as_str), Some("1")); assert_eq!(labels.get("run_only").map(String::as_str), Some("1")); assert_eq!(labels.get("shared").map(String::as_str), Some("run")); + // run.sandbox.env stays sticky merge-by-key per R71. let env = sandbox.env.as_ref().expect("sandbox env"); assert_eq!(env.get("CLI_ONLY").map(String::as_str), Some("1")); assert_eq!(env.get("RUN_ONLY").map(String::as_str), Some("1")); @@ -569,10 +565,13 @@ fn settings_legacy_cli_config_warns_and_ignores_it() { context.write_home( ".fabro/cli.toml", r#" -verbose = true +_version = 1 -[llm] -model = "legacy-model" +[cli.output] +verbosity = "verbose" + +[run.model] +name = "legacy-model" "#, ); @@ -597,10 +596,12 @@ fn settings_user_config_wins_over_legacy_cli_config() { context.write_home( ".fabro/cli.toml", r#" -[llm] -model = "legacy-model" +_version = 1 -[vars] +[run.model] +name = "legacy-model" + +[run.inputs] shared = "legacy" "#, ); @@ -632,10 +633,13 @@ fn settings_uses_fabro_home_for_home_config_resolution() { std::fs::write( fabro_home.path().join("settings.toml"), r#" -verbose = true +_version = 1 -[llm] -model = "from-fabro-home" +[cli.output] +verbosity = "verbose" + +[run.model] +name = "from-fabro-home" "#, ) .unwrap(); @@ -715,14 +719,20 @@ fn settings_fetches_server_settings_and_merges_with_local_config() { ".fabro/settings.toml", format!( r#" -server = {{ target = "{}/api/v1" }} -verbose = true +_version = 1 -[llm] -model = "cli-model" +[cli.target] +type = "http" +url = "{}/api/v1" + +[cli.output] +verbosity = "verbose" + +[run.model] +name = "cli-model" provider = "openai" -[vars] +[run.inputs] cli_only = "1" shared = "cli" "#, @@ -775,10 +785,14 @@ fn settings_cli_server_target_overrides_configured_server_target() { ".fabro/settings.toml", format!( r#" -[server] -target = "{}/api/v1" +_version = 1 -verbose = true +[cli.target] +type = "http" +url = "{}/api/v1" + +[cli.output] +verbosity = "verbose" "#, configured_server.base_url() ), diff --git a/lib/crates/fabro-cli/tests/it/cmd/create.rs b/lib/crates/fabro-cli/tests/it/cmd/create.rs index b707d4026..389336634 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/create.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/create.rs @@ -101,7 +101,10 @@ fn create_uses_configured_server_target_without_server_flag() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let output = context @@ -162,7 +165,7 @@ fn create_cli_server_target_overrides_configured_server_target() { context.write_home( ".fabro/settings.toml", format!( - "[server]\ntarget = \"{}/api/v1\"\n", + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", config_server.base_url() ), ); diff --git a/lib/crates/fabro-cli/tests/it/cmd/exec.rs b/lib/crates/fabro-cli/tests/it/cmd/exec.rs index 14ea203e5..c82db1081 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/exec.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/exec.rs @@ -100,7 +100,7 @@ fn exec_uses_user_config_defaults() { let context = test_context!(); context.write_home( ".fabro/settings.toml", - "[exec]\nprovider = \"openai\"\nmodel = \"gpt-4.1-mini\"\npermissions = \"read-only\"\noutput_format = \"json\"\n", + "_version = 1\n\n[cli.exec.model]\nprovider = \"openai\"\nname = \"gpt-4.1-mini\"\n\n[cli.exec.agent]\npermissions = \"read-only\"\n\n[cli.output]\nformat = \"json\"\n", ); let mut cmd = context.exec_cmd(); @@ -166,7 +166,10 @@ fn exec_configured_server_target_alone_does_not_reroute_exec() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let mut cmd = context.exec_cmd(); @@ -211,7 +214,7 @@ fn exec_cli_server_target_overrides_configured_server_target() { context.write_home( ".fabro/settings.toml", format!( - "[server]\ntarget = \"{}/api/v1\"\n", + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", config_server.base_url() ), ); diff --git a/lib/crates/fabro-cli/tests/it/cmd/model.rs b/lib/crates/fabro-cli/tests/it/cmd/model.rs index 02b444912..67e6b5f19 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/model.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/model.rs @@ -218,7 +218,10 @@ fn list_uses_configured_server_target_without_server_flag() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let mut cmd = context.model(); @@ -277,7 +280,10 @@ fn list_uses_fabro_config_for_machine_settings() { let config_path = config_dir.path().join("custom-settings.toml"); std::fs::write( &config_path, - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ) .unwrap(); diff --git a/lib/crates/fabro-cli/tests/it/cmd/ps.rs b/lib/crates/fabro-cli/tests/it/cmd/ps.rs index 354935a4b..da25a0145 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/ps.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/ps.rs @@ -234,7 +234,10 @@ fn ps_uses_configured_server_target_without_server_flag() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let output = context diff --git a/lib/crates/fabro-cli/tests/it/cmd/rm.rs b/lib/crates/fabro-cli/tests/it/cmd/rm.rs index 7930e8506..c502b9fd7 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/rm.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/rm.rs @@ -182,7 +182,10 @@ fn rm_force_removes_active_run() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let mut filters = context.filters(); @@ -292,7 +295,10 @@ fn rm_uses_configured_server_target_without_local_run_dir() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let output = context diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/crates/fabro-cli/tests/it/cmd/run.rs index 11ebc5ad9..624e663a1 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/run.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/run.rs @@ -207,7 +207,10 @@ fn detach_uses_configured_server_target_without_server_flag() { }); context.write_home( ".fabro/settings.toml", - format!("[server]\ntarget = \"{}/api/v1\"\n", server.base_url()), + format!( + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", + server.base_url() + ), ); let output = context @@ -292,7 +295,7 @@ fn detach_cli_server_target_overrides_configured_server_target() { context.write_home( ".fabro/settings.toml", format!( - "[server]\ntarget = \"{}/api/v1\"\n", + "_version = 1\n\n[cli.target]\ntype = \"http\"\nurl = \"{}/api/v1\"\n", config_server.base_url() ), );