diff --git a/lib/crates/fabro-api/src/lib.rs b/lib/crates/fabro-api/src/lib.rs index 1f0858261..7655ace4d 100644 --- a/lib/crates/fabro-api/src/lib.rs +++ b/lib/crates/fabro-api/src/lib.rs @@ -14,8 +14,6 @@ mod generated { include!(concat!(env!("OUT_DIR"), "/codegen.rs")); } pub mod types { - pub use fabro_types::WorkflowSettings; - pub use fabro_types::ServerSettings; pub use fabro_types::settings::server::{ DiscordIntegrationSettings, GithubIntegrationSettings, GithubIntegrationStrategy, IntegrationWebhooksSettings, IpAllowEntry, ObjectStoreSettings, ServerApiSettings, @@ -29,6 +27,7 @@ pub mod types { pub use fabro_types::status::{ BlockedReason, FailureReason, RunControlAction, RunStatus, SuccessReason, TerminalStatus, }; + pub use fabro_types::{ServerSettings, WorkflowSettings}; pub use crate::generated::types::*; } diff --git a/lib/crates/fabro-cli/src/command_context.rs b/lib/crates/fabro-cli/src/command_context.rs index f4895d763..e844d2cbb 100644 --- a/lib/crates/fabro-cli/src/command_context.rs +++ b/lib/crates/fabro-cli/src/command_context.rs @@ -3,9 +3,9 @@ use std::sync::Arc; use anyhow::{Context as _, Result, bail}; use fabro_config::UserSettingsBuilder; +use fabro_types::UserSettings; use fabro_types::settings::cli::{CliLayer, OutputFormat, OutputVerbosity}; use fabro_types::settings::{Combine, SettingsLayer}; -use fabro_types::UserSettings; use fabro_util::printer::Printer; use tokio::sync::OnceCell; diff --git a/lib/crates/fabro-cli/src/commands/dump.rs b/lib/crates/fabro-cli/src/commands/dump.rs index 0748ae671..342dbd3cd 100644 --- a/lib/crates/fabro-cli/src/commands/dump.rs +++ b/lib/crates/fabro-cli/src/commands/dump.rs @@ -307,11 +307,10 @@ mod tests { use chrono::{DateTime, Utc}; use fabro_store::{Database, EventEnvelope, EventPayload}; - use fabro_types::settings::SettingsLayer; use fabro_types::{ AggregateStats, AttrValue, BilledTokenCounts, Checkpoint, Conclusion, Graph, NodeStatusRecord, Retro, RunId, RunSpec, RunStatus, SandboxRecord, StageStatus, - StartRecord, SuccessReason, fixtures, + StartRecord, SuccessReason, WorkflowSettings, fixtures, }; use fabro_workflow::event::{Event, append_event}; use object_store::ObjectStore; @@ -349,7 +348,7 @@ mod tests { ); RunSpec { run_id, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph, workflow_slug: Some("night-sky".to_string()), working_directory: PathBuf::from("/tmp/night-sky"), diff --git a/lib/crates/fabro-cli/src/commands/exec.rs b/lib/crates/fabro-cli/src/commands/exec.rs index c8b218daf..8c0fcc8b4 100644 --- a/lib/crates/fabro-cli/src/commands/exec.rs +++ b/lib/crates/fabro-cli/src/commands/exec.rs @@ -3,6 +3,7 @@ use std::sync::Arc; use anyhow::Result as AnyResult; use fabro_agent::cli::{OutputFormat, run_with_args, run_with_args_and_client}; +use fabro_config::WorkflowSettingsBuilder; use fabro_llm::client::Client; use fabro_llm::error::{ Error as LlmError, ProviderErrorDetail, ProviderErrorKind, error_from_status_code, @@ -414,9 +415,10 @@ pub(crate) async fn execute(mut args: ExecArgs, ctx: &CommandContext) -> AnyResu .map(|(name, entry)| runtime_mcp_server(name, entry)) .collect() } else { - fabro_config::resolve_run_from_file(&raw_settings) + WorkflowSettingsBuilder::from_layer(&raw_settings) .map(|settings| { settings + .run .agent .mcps .values() diff --git a/lib/crates/fabro-cli/src/commands/run/attach.rs b/lib/crates/fabro-cli/src/commands/run/attach.rs index 350b7fcd5..c0d302b77 100644 --- a/lib/crates/fabro-cli/src/commands/run/attach.rs +++ b/lib/crates/fabro-cli/src/commands/run/attach.rs @@ -84,9 +84,10 @@ pub(crate) async fn attach_run_with_client( printer: Printer, ) -> Result { let state = client.get_run_state(run_id).await?; - let auto_approve = state.spec.as_ref().is_some_and(|record| { - record.settings.run.execution.approval == ApprovalMode::Auto - }); + let auto_approve = state + .spec + .as_ref() + .is_some_and(|record| record.settings.run.execution.approval == ApprovalMode::Auto); let events = client.list_run_events(run_id, None, None).await?; let replay_events = events.clone(); let next_seq = events.last().map_or(1, |event| event.seq.saturating_add(1)); diff --git a/lib/crates/fabro-cli/src/commands/run/runner.rs b/lib/crates/fabro-cli/src/commands/run/runner.rs index 45a747a5e..f61a85a5a 100644 --- a/lib/crates/fabro-cli/src/commands/run/runner.rs +++ b/lib/crates/fabro-cli/src/commands/run/runner.rs @@ -16,8 +16,8 @@ use async_trait::async_trait; use fabro_config::{ServerSettingsBuilder, Storage}; use fabro_interview::{ControlInterviewer, WorkerControlEnvelope, WorkerControlMessage}; use fabro_store::{EventEnvelope, RunProjection, RunProjectionReducer}; -use fabro_types::settings::run::RunMode; use fabro_types::settings::InterpString; +use fabro_types::settings::run::RunMode; use fabro_types::{ ArtifactUpload, EventBody, FailureReason, RunBlobId, RunEvent, RunId, WorkflowSettings, }; @@ -513,8 +513,8 @@ fn maybe_build_github_credentials( let required_github_credentials = (resolved_run.execution.mode != RunMode::DryRun && resolved_run.sandbox.provider == "daytona") || resolved_server - .as_ref() - .is_some_and(|settings| !settings.server.integrations.github.permissions.is_empty()); + .as_ref() + .is_some_and(|settings| !settings.server.integrations.github.permissions.is_empty()); let pull_request_enabled = resolved_run.execution.mode != RunMode::DryRun && resolved_run.pull_request.is_some(); let strategy = resolved_server diff --git a/lib/crates/fabro-cli/src/local_server.rs b/lib/crates/fabro-cli/src/local_server.rs index 88f2288b6..997e41e18 100644 --- a/lib/crates/fabro-cli/src/local_server.rs +++ b/lib/crates/fabro-cli/src/local_server.rs @@ -7,10 +7,10 @@ use std::path::PathBuf; use anyhow::Result; -use fabro_config::bind::BindRequest; use fabro_config::ServerSettingsBuilder; -use fabro_types::settings::{ServerAuthMethod, SettingsLayer}; +use fabro_config::bind::BindRequest; use fabro_types::ServerSettings; +use fabro_types::settings::{ServerAuthMethod, SettingsLayer}; pub(crate) fn storage_dir(settings: &SettingsLayer) -> Result { storage_dir_with_lookup(settings, &|name| std::env::var(name).ok()) @@ -50,9 +50,11 @@ pub(crate) fn auth_methods(settings: &SettingsLayer) -> Vec { } pub(crate) fn config_log_level(settings: &SettingsLayer) -> Option { - resolved_server_settings(settings) - .ok() - .and_then(|settings| settings.server.logging.level) + settings + .server + .as_ref() + .and_then(|server| server.logging.as_ref()) + .and_then(|logging| logging.level.clone()) } fn resolved_server_settings(settings: &SettingsLayer) -> Result { diff --git a/lib/crates/fabro-cli/src/user_config.rs b/lib/crates/fabro-cli/src/user_config.rs index 36394143c..9907aa709 100644 --- a/lib/crates/fabro-cli/src/user_config.rs +++ b/lib/crates/fabro-cli/src/user_config.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use anyhow::Result; pub(crate) use fabro_client::ServerTarget; -pub(crate) use fabro_config::user::*; use fabro_config::UserSettingsBuilder; +pub(crate) use fabro_config::user::*; use fabro_types::settings::cli::CliTargetSettings; use fabro_types::settings::{CliNamespace, SettingsLayer}; use fabro_util::version::FABRO_VERSION; diff --git a/lib/crates/fabro-cli/tests/it/cmd/attach.rs b/lib/crates/fabro-cli/tests/it/cmd/attach.rs index 68af2b229..31ff0536b 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/attach.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/attach.rs @@ -570,53 +570,78 @@ fn attach_json_errors_without_prompting_for_human_input() { }, "run_dir": "[RUN_DIR]", "settings": { - "workflow": { - "graph": "workflow.fabro" - }, - "cli": { - "exec": { - "prevent_idle_sleep": false - }, - "output": { - "format": "text", - "verbosity": "normal" - }, - "target": { - "path": "[CLI_SOCKET]", - "type": "unix" - }, - "updates": { - "check": true - } - }, - "features": { - "session_sandboxes": false - }, "project": { - "directory": "." + "description": null, + "directory": ".", + "metadata": {}, + "name": null }, "run": { + "agent": { + "mcps": {}, + "permissions": null + }, + "artifacts": { + "include": [] + }, + "checkpoint": { + "exclude_globs": [] + }, "execution": { "approval": "prompt", "mode": "normal", "retros": false }, - "goal": "Wait for approval", + "git": { + "author": null + }, + "goal": { + "type": "inline", + "value": "Wait for approval" + }, + "hooks": [], + "inputs": {}, + "interviews": { + "discord": null, + "provider": null, + "slack": null, + "teams": null + }, + "metadata": {}, "model": { + "fallbacks": [], "name": "gpt-5.4", "provider": "openai" }, + "notifications": {}, "prepare": { - "timeout": "5m" + "commands": [], + "timeout_ms": 300000 }, + "pull_request": null, "sandbox": { + "daytona": null, "devcontainer": false, + "env": {}, "local": { "worktree_mode": "clean" }, "preserve": false, "provider": "local" - } + }, + "scm": { + "github": null, + "owner": null, + "provider": null, + "repository": null + }, + "working_dir": null + }, + "workflow": { + "description": null, + "graph": "workflow.fabro", + "metadata": {}, + "name": null } }, "workflow_slug": "human-gate", diff --git a/lib/crates/fabro-cli/tests/it/cmd/config.rs b/lib/crates/fabro-cli/tests/it/cmd/config.rs index a12a1b76d..d06395688 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/config.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/config.rs @@ -74,8 +74,9 @@ shared = "server" } fn resolved_server_settings_fixture() -> serde_json::Value { - let settings = fabro_config::ServerSettingsBuilder::from_layer(&server_settings_layer_fixture()) - .expect("server settings fixture should resolve"); + let settings = + fabro_config::ServerSettingsBuilder::from_layer(&server_settings_layer_fixture()) + .expect("server settings fixture should resolve"); serde_json::to_value(settings).expect("resolved settings payload should serialize") } @@ -357,10 +358,6 @@ fn create_explicit_workflow_path_uses_project_config_relative_to_workflow() { run_spec["settings"]["run"]["execution"]["approval"].as_str(), Some("auto") ); - assert_eq!( - run_spec["settings"]["server"]["storage"]["root"].as_str(), - Some(storage_dir.to_str().unwrap()) - ); assert_eq!( run_spec["settings"]["run"]["sandbox"]["preserve"].as_bool(), Some(true) @@ -371,8 +368,8 @@ fn create_explicit_workflow_path_uses_project_config_relative_to_workflow() { ); // v2 R30: run.prepare.steps replaces the whole ordered list across layers. assert_eq!( - run_spec["settings"]["run"]["prepare"]["steps"], - serde_json::json!([{"script": "workflow-setup"}]) + run_spec["settings"]["run"]["prepare"]["commands"], + serde_json::json!(["workflow-setup"]) ); } diff --git a/lib/crates/fabro-cli/tests/it/cmd/create.rs b/lib/crates/fabro-cli/tests/it/cmd/create.rs index d1d0bb551..e47b50e51 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/create.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/create.rs @@ -6,10 +6,8 @@ use serde_json::json; use super::support::{fixture, output_stdout, resolve_run, run_count_for_test_case, run_state}; use crate::support::{fabro_json_snapshot, unique_run_id}; -fn resolved_run( - settings: &fabro_types::settings::SettingsLayer, -) -> fabro_types::settings::RunNamespace { - fabro_config::resolve_run_from_file(settings).expect("run settings should resolve") +fn resolved_run(settings: &fabro_types::WorkflowSettings) -> fabro_types::settings::RunNamespace { + settings.run.clone() } fn run_status_response(run_id: &str, status: &str) -> serde_json::Value { @@ -365,7 +363,6 @@ fn create_persists_requested_overrides_into_store() { }); let settings = &run_spec.settings; let resolved_run = resolved_run(settings); - let cli_settings = fabro_config::resolve_cli_from_file(settings).expect("cli settings"); let compact = json!({ "workflow_slug": run_spec.workflow_slug, "settings": { @@ -376,7 +373,6 @@ fn create_persists_requested_overrides_into_store() { "dry_run": resolved_run.execution.mode == fabro_types::settings::run::RunMode::DryRun, "auto_approve": resolved_run.execution.approval == fabro_types::settings::run::ApprovalMode::Auto, "no_retro": !resolved_run.execution.retros, - "verbose": cli_settings.output.verbosity == fabro_types::settings::cli::OutputVerbosity::Verbose, "llm": { "model": resolved_run.model.name.as_ref().map(fabro_types::settings::InterpString::as_source), "provider": resolved_run.model.provider.as_ref().map(fabro_types::settings::InterpString::as_source), @@ -397,7 +393,6 @@ fn create_persists_requested_overrides_into_store() { "dry_run": true, "auto_approve": true, "no_retro": true, - "verbose": true, "llm": { "model": "gpt-5", "provider": "openai" diff --git a/lib/crates/fabro-cli/tests/it/cmd/inspect.rs b/lib/crates/fabro-cli/tests/it/cmd/inspect.rs index aa4aced16..111866e17 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/inspect.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/inspect.rs @@ -133,7 +133,10 @@ fn inspect_created_run_shows_run_spec_without_start_or_conclusion() { "kind": "submitted" }, "run_spec": { - "goal": "Run tests and report results", + "goal": { + "type": "inline", + "value": "Run tests and report results" + }, "workflow_name": "Simple", "workflow_slug": "simple", "sandbox_provider": "local", @@ -169,7 +172,10 @@ fn inspect_completed_run_shows_run_start_conclusion_checkpoint() { "reason": "completed" }, "run_spec": { - "goal": "Run tests and report results", + "goal": { + "type": "inline", + "value": "Run tests and report results" + }, "workflow_name": "Simple", "workflow_slug": "simple", "sandbox_provider": "local", @@ -238,7 +244,10 @@ fn inspect_completed_run_reads_store_without_disk_metadata_files() { "reason": "completed" }, "run_spec": { - "goal": "Run tests and report results", + "goal": { + "type": "inline", + "value": "Run tests and report results" + }, "workflow_name": "Simple", "workflow_slug": "simple", "sandbox_provider": "local", @@ -292,7 +301,10 @@ fn inspect_git_backed_run_exposes_checkpoint_and_sandbox_state() { "reason": "completed" }, "run_spec": { - "goal": "Edit a tracked file", + "goal": { + "type": "inline", + "value": "Edit a tracked file" + }, "workflow_name": "Flow", "workflow_slug": "flow", "llm_provider": "openai", diff --git a/lib/crates/fabro-cli/tests/it/cmd/runner.rs b/lib/crates/fabro-cli/tests/it/cmd/runner.rs index 337c5b213..de9d52b8e 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/runner.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/runner.rs @@ -257,7 +257,7 @@ digraph CachedGraph { } #[test] -fn runner_uses_snapshotted_app_id_for_github_credentials() { +fn runner_local_dry_runs_ignore_github_app_configuration() { let context = auth_context(); let run_id = unique_run_id(); let workflow_path = context.temp_dir.join("workflow.fabro"); @@ -299,21 +299,6 @@ digraph GitHubApp { .success(); let run_dir = context.find_run_dir(&run_id); - let state = run_state(&run_dir); - let run = state.spec.as_ref().expect("run spec should exist"); - let resolved_server = fabro_config::resolve_server_from_file(&run.settings).unwrap(); - fabro_json_snapshot!( - context, - serde_json::json!({ - "app_id": resolved_server.integrations.github.app_id.map(|value| value.as_source()), - }), - @r#" - { - "app_id": "snapshotted-app-id" - } - "# - ); - context.write_home(".fabro/settings.toml", "_version = 1\n"); let server = server_target(&context.storage_dir); diff --git a/lib/crates/fabro-cli/tests/it/support/auth_harness.rs b/lib/crates/fabro-cli/tests/it/support/auth_harness.rs index 6f69c096f..110b7ab79 100644 --- a/lib/crates/fabro-cli/tests/it/support/auth_harness.rs +++ b/lib/crates/fabro-cli/tests/it/support/auth_harness.rs @@ -17,7 +17,7 @@ use axum::extract::{Request, State as AxumState}; use axum::middleware::{self, Next}; use axum::response::Response as AxumResponse; use chrono::{Duration as ChronoDuration, Utc}; -use fabro_config::{parse_settings_layer, resolve_server_from_file}; +use fabro_config::{ServerSettingsBuilder, parse_settings_layer}; use fabro_server::auth::GithubEndpoints; use fabro_server::ip_allowlist::IpAllowlistConfig; use fabro_server::jwt_auth::resolve_auth_mode_with_lookup; @@ -71,7 +71,9 @@ impl RealAuthHarness { let (api_listener, api_base_url) = bind_listener().await; let settings = auth_settings(&api_base_url, &github_client_id, auth_methods); - let resolved = resolve_server_from_file(&settings).expect("settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&settings) + .expect("settings should resolve") + .server; let dev_token = dev_token.map(str::to_string); let auth_mode = resolve_auth_mode_with_lookup(&resolved, |name| match name { "SESSION_SECRET" => Some(TEST_SESSION_SECRET.to_string()), diff --git a/lib/crates/fabro-config/src/builders.rs b/lib/crates/fabro-config/src/builders.rs index b7479b1eb..9643055f1 100644 --- a/lib/crates/fabro-config/src/builders.rs +++ b/lib/crates/fabro-config/src/builders.rs @@ -187,7 +187,7 @@ impl WorkflowSettingsBuilder { pub fn build_layer(self) -> SettingsLayer { let server_defaults = SettingsLayer { version: self.server.version, - run: self.server.run, + run: self.server.run, ..SettingsLayer::default() }; let mut layer = self @@ -207,7 +207,9 @@ impl WorkflowSettingsBuilder { Self::from_layer(&self.build_layer()) } - pub fn from_layer(layer: &SettingsLayer) -> std::result::Result { + pub fn from_layer( + layer: &SettingsLayer, + ) -> std::result::Result { let layer = apply_builtin_defaults(layer.clone()); let mut errors = Vec::new(); let project = resolve_project(&layer.project.clone().unwrap_or_default(), &mut errors); diff --git a/lib/crates/fabro-config/src/lib.rs b/lib/crates/fabro-config/src/lib.rs index 22f4252a6..c00f31dd8 100644 --- a/lib/crates/fabro-config/src/lib.rs +++ b/lib/crates/fabro-config/src/lib.rs @@ -24,10 +24,10 @@ pub mod user; use std::path::Path; -pub(crate) use defaults::apply_builtin_defaults; pub use builders::{ ResolveErrors, ServerSettingsBuilder, UserSettingsBuilder, WorkflowSettingsBuilder, }; +pub(crate) use defaults::apply_builtin_defaults; pub use error::{Error, Result}; pub use fabro_util::path::expand_tilde; pub use home::Home; diff --git a/lib/crates/fabro-config/src/resolve/mod.rs b/lib/crates/fabro-config/src/resolve/mod.rs index 91825e748..088a8f530 100644 --- a/lib/crates/fabro-config/src/resolve/mod.rs +++ b/lib/crates/fabro-config/src/resolve/mod.rs @@ -9,8 +9,8 @@ mod workflow; pub use cli::resolve_cli; pub use error::ResolveError; use fabro_types::settings::{ - CliNamespace, FeaturesNamespace, InterpString, ProjectNamespace, RunNamespace, - ServerNamespace, SettingsLayer, WorkflowNamespace, + CliNamespace, FeaturesNamespace, InterpString, ProjectNamespace, RunNamespace, ServerNamespace, + SettingsLayer, WorkflowNamespace, }; pub use features::resolve_features; pub use project::resolve_project; diff --git a/lib/crates/fabro-config/tests/defaults.rs b/lib/crates/fabro-config/tests/defaults.rs index 853bda4c0..e72b97ce1 100644 --- a/lib/crates/fabro-config/tests/defaults.rs +++ b/lib/crates/fabro-config/tests/defaults.rs @@ -2,10 +2,10 @@ use fabro_config::{ parse_settings_layer, resolve_run_from_file, resolve_server_from_file, resolve_workflow_from_file, }; -use fabro_types::settings::{Combine, SettingsLayer}; use fabro_types::settings::cli::OutputFormat; use fabro_types::settings::run::{ApprovalMode, RunMode, WorktreeMode}; use fabro_types::settings::server::ObjectStoreProvider; +use fabro_types::settings::{Combine, SettingsLayer}; fn parse(source: &str) -> SettingsLayer { parse_settings_layer(source).expect("fixture should parse") diff --git a/lib/crates/fabro-config/tests/resolve_server.rs b/lib/crates/fabro-config/tests/resolve_server.rs index 3f8fe5f4d..c8d172aa5 100644 --- a/lib/crates/fabro-config/tests/resolve_server.rs +++ b/lib/crates/fabro-config/tests/resolve_server.rs @@ -3,8 +3,8 @@ reason = "sync test fixture setup; not on a Tokio path" )] -use fabro_config::{ServerSettingsBuilder, parse_settings_layer}; use fabro_config::user::default_storage_dir; +use fabro_config::{ServerSettingsBuilder, parse_settings_layer}; use fabro_types::settings::server::{ GithubIntegrationStrategy, IpAllowEntry, ObjectStoreSettings, ServerListenSettings, }; @@ -556,7 +556,10 @@ root = "{{ env.FABRO_STORAGE_ROOT }}" let settings = ServerSettingsBuilder::from_layer(&file).expect("server settings should resolve"); - assert_eq!(settings.server.storage.root, InterpString::parse("{{ env.FABRO_STORAGE_ROOT }}")); + assert_eq!( + settings.server.storage.root, + InterpString::parse("{{ env.FABRO_STORAGE_ROOT }}") + ); } #[test] diff --git a/lib/crates/fabro-install/src/lib.rs b/lib/crates/fabro-install/src/lib.rs index 29d03911a..e75d73768 100644 --- a/lib/crates/fabro-install/src/lib.rs +++ b/lib/crates/fabro-install/src/lib.rs @@ -468,7 +468,7 @@ pub fn persist_install_outputs_direct( #[cfg(test)] mod tests { - use fabro_config::{Storage, envfile}; + use fabro_config::{ServerSettingsBuilder, Storage, envfile}; use fabro_vault::{SecretType as VaultSecretType, Vault}; use super::{ @@ -661,8 +661,9 @@ name = "custom" &toml::to_string_pretty(&doc).expect("settings should serialize"), ) .expect("settings should parse"); - let resolved = - fabro_config::resolve_server_from_file(&settings).expect("settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&settings) + .expect("settings should resolve") + .server; match resolved.listen { ServerListenSettings::Tcp { address, .. } => { assert_eq!(address.to_string(), "0.0.0.0:32276"); diff --git a/lib/crates/fabro-server/src/demo/mod.rs b/lib/crates/fabro-server/src/demo/mod.rs index 16e9a84a7..fbf83b4ee 100644 --- a/lib/crates/fabro-server/src/demo/mod.rs +++ b/lib/crates/fabro-server/src/demo/mod.rs @@ -755,11 +755,11 @@ mod runs { use fabro_api::types::*; use fabro_types::WorkflowSettings; - use fabro_types::settings::{InterpString, ProjectNamespace, WorkflowNamespace}; use fabro_types::settings::run::{ - DaytonaSettings, DaytonaSnapshotSettings, LocalSandboxSettings, RunGoal, - RunModelSettings, RunNamespace, RunPrepareSettings, RunSandboxSettings, + DaytonaSettings, DaytonaSnapshotSettings, LocalSandboxSettings, RunGoal, RunModelSettings, + RunNamespace, RunPrepareSettings, RunSandboxSettings, }; + use fabro_types::settings::{InterpString, ProjectNamespace, WorkflowNamespace}; use super::ts; use crate::server::truncate_goal; @@ -1354,20 +1354,20 @@ mod runs { ..WorkflowNamespace::default() }, run: RunNamespace { - goal: Some(RunGoal::Inline(InterpString::parse( + goal: Some(RunGoal::Inline(InterpString::parse( "Add rate limiting to auth endpoints", ))), working_dir: Some(InterpString::parse("/workspace/api-server")), - model: RunModelSettings { + model: RunModelSettings { provider: Some(InterpString::parse("anthropic")), - name: Some(InterpString::parse("claude-opus-4-6")), + name: Some(InterpString::parse("claude-opus-4-6")), ..RunModelSettings::default() }, - prepare: RunPrepareSettings { + prepare: RunPrepareSettings { commands: vec!["bun install".into(), "bun run typecheck".into()], timeout_ms: 120_000, }, - sandbox: RunSandboxSettings { + sandbox: RunSandboxSettings { provider: "daytona".into(), preserve: false, devcontainer: false, diff --git a/lib/crates/fabro-server/src/install.rs b/lib/crates/fabro-server/src/install.rs index 68f090f34..0bbc3186c 100644 --- a/lib/crates/fabro-server/src/install.rs +++ b/lib/crates/fabro-server/src/install.rs @@ -1902,8 +1902,7 @@ async fn write_artifact_store_metadata( storage.root = Some(InterpString::parse(&storage_dir.display().to_string())); let resolved = - fabro_config::ServerSettingsBuilder::from_layer(&settings) - .map_err(anyhow::Error::from)?; + fabro_config::ServerSettingsBuilder::from_layer(&settings).map_err(anyhow::Error::from)?; let (object_store, prefix) = serve::build_artifact_object_store(&resolved.server)?; let artifact_store = ArtifactStore::new(object_store, prefix); artifact_store.write_metadata(FABRO_VERSION).await?; diff --git a/lib/crates/fabro-server/src/jwt_auth.rs b/lib/crates/fabro-server/src/jwt_auth.rs index b332cb797..1b210c63e 100644 --- a/lib/crates/fabro-server/src/jwt_auth.rs +++ b/lib/crates/fabro-server/src/jwt_auth.rs @@ -376,7 +376,7 @@ mod tests { use axum::{Json, Router}; use base64::Engine; use base64::engine::general_purpose::URL_SAFE_NO_PAD; - use fabro_config::{parse_settings_layer, resolve_server_from_file}; + use fabro_config::{Error as ConfigError, ServerSettingsBuilder, parse_settings_layer}; use fabro_types::IdpIdentity; use fabro_types::settings::ServerAuthMethod; use tower::ServiceExt; @@ -387,8 +387,9 @@ mod tests { use super::*; fn settings(source: &str) -> ServerNamespace { - let file = parse_settings_layer(source).expect("fixture should parse"); - resolve_server_from_file(&file).expect("fixture should resolve") + ServerSettingsBuilder::from_toml(source) + .expect("fixture should resolve") + .server } fn empty_lookup(_name: &str) -> Option { @@ -557,7 +558,11 @@ methods = [] ", ) .expect("fixture should parse"); - let errors = resolve_server_from_file(&file).expect_err("empty auth methods should fail"); + let ConfigError::Resolve { errors, .. } = + ServerSettingsBuilder::from_layer(&file).expect_err("empty auth methods should fail") + else { + panic!("expected settings resolution error"); + }; assert!(errors.iter().any(|err| matches!( err, fabro_config::ResolveError::Invalid { path, reason } diff --git a/lib/crates/fabro-server/src/run_manifest.rs b/lib/crates/fabro-server/src/run_manifest.rs index 6a2399e63..dc63b7c66 100644 --- a/lib/crates/fabro-server/src/run_manifest.rs +++ b/lib/crates/fabro-server/src/run_manifest.rs @@ -4,10 +4,9 @@ use std::sync::Arc; use anyhow::{Result, anyhow, bail}; use fabro_api::types; -use fabro_config::WorkflowSettingsBuilder; use fabro_config::project::resolve_working_directory; use fabro_config::run::parse_run_config; -use fabro_config::parse_settings_layer; +use fabro_config::{WorkflowSettingsBuilder, parse_settings_layer}; use fabro_graphviz::graph::{Graph, is_llm_handler_type}; use fabro_graphviz::render::apply_direction; use fabro_llm::Provider; @@ -367,9 +366,8 @@ async fn build_preflight_report( let server_settings = state.server_settings(); let github_integration = &server_settings.server.integrations.github; let sandbox_provider = resolve_sandbox_provider(&resolved_run.run)?; - let sandbox_provider = if resolved_run.run.execution.mode == RunMode::DryRun - && !sandbox_provider.is_local() - { + let sandbox_provider = + if resolved_run.run.execution.mode == RunMode::DryRun && !sandbox_provider.is_local() { SandboxProvider::Local } else { sandbox_provider diff --git a/lib/crates/fabro-server/src/server.rs b/lib/crates/fabro-server/src/server.rs index 02899a2b9..1c7da9d1d 100644 --- a/lib/crates/fabro-server/src/server.rs +++ b/lib/crates/fabro-server/src/server.rs @@ -40,7 +40,7 @@ pub use fabro_api::types::{ }; use fabro_auth::parse_credential_secret; use fabro_config::daemon::ServerDaemon; -use fabro_config::{ServerSettingsBuilder, Storage}; +use fabro_config::{ServerSettingsBuilder, Storage, WorkflowSettingsBuilder}; use fabro_interview::{ Answer, ControlInterviewer, Interviewer, Question, QuestionType, WorkerControlEnvelope, }; @@ -1362,8 +1362,9 @@ async fn get_system_info( fn system_features(settings: &SettingsLayer) -> SystemFeatures { let session_sandboxes = - fabro_config::resolve_features_from_file(settings).is_ok_and(|s| s.session_sandboxes); - let retros = fabro_config::resolve_run_from_file(settings).is_ok_and(|s| s.execution.retros); + ServerSettingsBuilder::from_layer(settings).is_ok_and(|s| s.features.session_sandboxes); + let retros = + WorkflowSettingsBuilder::from_layer(settings).is_ok_and(|s| s.run.execution.retros); SystemFeatures { session_sandboxes: Some(session_sandboxes), retros: Some(retros), @@ -1656,9 +1657,9 @@ fn build_prune_plan( } fn system_sandbox_provider(settings: &SettingsLayer) -> String { - fabro_config::resolve_run_from_file(settings).map_or_else( + WorkflowSettingsBuilder::from_layer(settings).map_or_else( |_| SandboxProvider::default().to_string(), - |settings| settings.sandbox.provider, + |settings| settings.run.sandbox.provider, ) } diff --git a/lib/crates/fabro-server/src/startup.rs b/lib/crates/fabro-server/src/startup.rs index 6bd2c957f..6507d86e0 100644 --- a/lib/crates/fabro-server/src/startup.rs +++ b/lib/crates/fabro-server/src/startup.rs @@ -28,13 +28,13 @@ pub fn validate_startup( mod tests { use std::collections::HashMap; - use fabro_config::parse_settings_layer; + use fabro_config::ServerSettingsBuilder; use fabro_types::settings::ServerNamespace; use super::validate_startup; fn resolved_settings(auth_methods: &[&str]) -> ServerNamespace { - let settings = parse_settings_layer(&format!( + ServerSettingsBuilder::from_toml(&format!( r" _version = 1 @@ -47,8 +47,8 @@ methods = [{}] .collect::>() .join(", ") )) - .unwrap(); - fabro_config::resolve_server_from_file(&settings).unwrap() + .unwrap() + .server } #[test] diff --git a/lib/crates/fabro-server/tests/it/api/cli_auth_token.rs b/lib/crates/fabro-server/tests/it/api/cli_auth_token.rs index 1581fc988..57ca32e2b 100644 --- a/lib/crates/fabro-server/tests/it/api/cli_auth_token.rs +++ b/lib/crates/fabro-server/tests/it/api/cli_auth_token.rs @@ -4,7 +4,7 @@ use std::time::Duration; use axum::body::Body; use axum::http::{Request, StatusCode, header}; use base64::Engine; -use fabro_config::{parse_settings_layer, resolve_server_from_file}; +use fabro_config::{ServerSettingsBuilder, parse_settings_layer}; use fabro_server::ip_allowlist::IpAllowlistConfig; use fabro_server::jwt_auth::resolve_auth_mode_with_lookup; use fabro_server::server::{RouterOptions, build_router_with_options, create_app_state_with_store}; @@ -29,7 +29,9 @@ fn test_app(settings: fabro_types::settings::SettingsLayer) -> (axum::Router, Ar None, )); let artifact_store = ArtifactStore::new(object_store, "artifacts"); - let resolved = resolve_server_from_file(&settings).expect("settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&settings) + .expect("settings should resolve") + .server; let auth_mode = resolve_auth_mode_with_lookup(&resolved, |name| match name { "SESSION_SECRET" => Some("0123456789abcdef0123456789abcdef".to_string()), "GITHUB_APP_CLIENT_SECRET" => Some("test-client-secret".to_string()), diff --git a/lib/crates/fabro-server/tests/it/api/install.rs b/lib/crates/fabro-server/tests/it/api/install.rs index 95bc8d4d6..4e25dc447 100644 --- a/lib/crates/fabro-server/tests/it/api/install.rs +++ b/lib/crates/fabro-server/tests/it/api/install.rs @@ -10,7 +10,7 @@ use std::time::Duration; use axum::body::Body; use axum::http::{Request, StatusCode}; -use fabro_config::{Storage, parse_settings_layer, resolve_server_from_file}; +use fabro_config::{ServerSettingsBuilder, Storage, parse_settings_layer}; use fabro_install::OBJECT_STORE_MANAGED_COMMENT; use fabro_model::Provider; use fabro_server::install::{InstallAppState, build_install_router}; @@ -745,7 +745,9 @@ async fn token_install_finish_persists_settings_env_and_vault() { assert!(settings.contains("https://fabro.example.com")); assert!(settings.contains("strategy = \"token\"")); let parsed = parse_settings_layer(&settings).expect("settings should parse"); - let resolved = resolve_server_from_file(&parsed).expect("settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&parsed) + .expect("settings should resolve") + .server; assert_eq!( match resolved.listen { fabro_types::settings::server::ServerListenSettings::Tcp { address, .. } => { diff --git a/lib/crates/fabro-server/tests/it/api/routing.rs b/lib/crates/fabro-server/tests/it/api/routing.rs index 4deb779a5..8475711cf 100644 --- a/lib/crates/fabro-server/tests/it/api/routing.rs +++ b/lib/crates/fabro-server/tests/it/api/routing.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use axum::body::Body; use axum::extract::ConnectInfo; use axum::http::{Method, Request, StatusCode}; -use fabro_config::{parse_settings_layer, resolve_server_from_file}; +use fabro_config::{ServerSettingsBuilder, parse_settings_layer}; use fabro_server::ip_allowlist::{IpAllowlist, IpAllowlistConfig}; use fabro_server::jwt_auth::{AuthMode, resolve_auth_mode_with_lookup}; use fabro_server::server::{ @@ -30,7 +30,9 @@ methods = ["dev-token"] "#, ) .expect("settings fixture should parse"); - let resolved = resolve_server_from_file(&settings).expect("settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&settings) + .expect("settings should resolve") + .server; resolve_auth_mode_with_lookup(&resolved, |name| match name { "SESSION_SECRET" => Some(SESSION_SECRET.to_string()), "FABRO_DEV_TOKEN" => Some(DEV_TOKEN.to_string()), diff --git a/lib/crates/fabro-server/tests/it/api/tcp.rs b/lib/crates/fabro-server/tests/it/api/tcp.rs index 24d35e242..0fef8bc06 100644 --- a/lib/crates/fabro-server/tests/it/api/tcp.rs +++ b/lib/crates/fabro-server/tests/it/api/tcp.rs @@ -10,7 +10,7 @@ use std::time::Duration; use axum::http::StatusCode; use fabro_config::bind::Bind; -use fabro_config::{RuntimeDirectory, parse_settings_layer, resolve_server_from_file}; +use fabro_config::{RuntimeDirectory, ServerSettingsBuilder, parse_settings_layer}; use fabro_server::ip_allowlist::{IpAllowlist, IpAllowlistConfig}; use fabro_server::jwt_auth::{AuthMode, resolve_auth_mode_with_lookup}; use fabro_server::serve::{ServeArgs, serve_command}; @@ -168,7 +168,9 @@ methods = ["dev-token"] "#, ) .expect("test settings should parse"); - let resolved = resolve_server_from_file(&settings).expect("test settings should resolve"); + let resolved = ServerSettingsBuilder::from_layer(&settings) + .expect("test settings should resolve") + .server; let auth_mode = resolve_auth_mode_with_lookup(&resolved, |name| match name { "SESSION_SECRET" => Some(TEST_SESSION_SECRET.to_string()), "FABRO_DEV_TOKEN" => Some(TEST_DEV_TOKEN.to_string()), diff --git a/lib/crates/fabro-types/src/dense.rs b/lib/crates/fabro-types/src/dense.rs index 1be00a998..2cc6fda8b 100644 --- a/lib/crates/fabro-types/src/dense.rs +++ b/lib/crates/fabro-types/src/dense.rs @@ -4,8 +4,8 @@ use std::path::Path; use serde::{Deserialize, Serialize}; use crate::settings::{ - CliNamespace, FeaturesNamespace, InterpString, ProjectNamespace, RunNamespace, - ServerNamespace, WorkflowNamespace, + CliNamespace, FeaturesNamespace, InterpString, ProjectNamespace, RunNamespace, ServerNamespace, + WorkflowNamespace, }; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/lib/crates/fabro-types/src/run.rs b/lib/crates/fabro-types/src/run.rs index e014fd872..104547330 100644 --- a/lib/crates/fabro-types/src/run.rs +++ b/lib/crates/fabro-types/src/run.rs @@ -3,10 +3,10 @@ use std::path::{Path, PathBuf}; use serde::{Deserialize, Serialize}; +use crate::WorkflowSettings; use crate::graph::Graph; use crate::run_blob_id::RunBlobId; use crate::run_id::RunId; -use crate::WorkflowSettings; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "snake_case")] diff --git a/lib/crates/fabro-types/src/settings/run.rs b/lib/crates/fabro-types/src/settings/run.rs index bbd7c5404..26d1c1aa3 100644 --- a/lib/crates/fabro-types/src/settings/run.rs +++ b/lib/crates/fabro-types/src/settings/run.rs @@ -148,12 +148,8 @@ pub enum DockerfileSource { #[derive(Deserialize)] #[serde(tag = "type", rename_all = "snake_case")] enum DockerfileSourceRepr { - Inline { - value: String, - }, - Path { - path: String, - }, + Inline { value: String }, + Path { path: String }, } impl Serialize for DockerfileSource { diff --git a/lib/crates/fabro-workflow/src/event.rs b/lib/crates/fabro-workflow/src/event.rs index 969cbd7ac..48ad45738 100644 --- a/lib/crates/fabro-workflow/src/event.rs +++ b/lib/crates/fabro-workflow/src/event.rs @@ -3492,8 +3492,9 @@ mod tests { #[test] fn run_created_populates_user_actor_from_provenance() { - use ::fabro_types::settings::SettingsLayer; - use ::fabro_types::{Graph, RunAuthMethod, RunSubjectProvenance, fixtures}; + use ::fabro_types::{ + Graph, RunAuthMethod, RunSubjectProvenance, WorkflowSettings, fixtures, + }; let provenance = RunProvenance { server: None, @@ -3506,7 +3507,7 @@ mod tests { let stored = to_run_event(&fixtures::RUN_1, &Event::RunCreated { run_id: fixtures::RUN_1, - settings: serde_json::to_value(SettingsLayer::default()).unwrap(), + settings: serde_json::to_value(WorkflowSettings::default()).unwrap(), graph: serde_json::to_value(Graph::new("test")).unwrap(), workflow_source: None, workflow_config: None, diff --git a/lib/crates/fabro-workflow/src/handler/manager_loop.rs b/lib/crates/fabro-workflow/src/handler/manager_loop.rs index cc575789a..589962f43 100644 --- a/lib/crates/fabro-workflow/src/handler/manager_loop.rs +++ b/lib/crates/fabro-workflow/src/handler/manager_loop.rs @@ -7,8 +7,8 @@ use std::time::Duration; use async_trait::async_trait; use fabro_graphviz::graph::{AttrValue, Graph, Node}; use fabro_store::{ArtifactStore, Database}; -use fabro_types::settings::SettingsLayer; use fabro_types::WorkflowSettings; +use fabro_types::settings::SettingsLayer; use object_store::memory::InMemory; use tokio::fs; use tokio::time::{sleep, timeout}; diff --git a/lib/crates/fabro-workflow/src/operations/create.rs b/lib/crates/fabro-workflow/src/operations/create.rs index 91c826ea5..7fed063fc 100644 --- a/lib/crates/fabro-workflow/src/operations/create.rs +++ b/lib/crates/fabro-workflow/src/operations/create.rs @@ -15,8 +15,8 @@ use fabro_sandbox::SandboxProvider; use fabro_sandbox::daytona::detect_repo_info; use fabro_store::Database; use fabro_template::{TemplateContext, render as render_template}; -use fabro_types::settings::run::RunMode; use fabro_types::settings::SettingsLayer; +use fabro_types::settings::run::RunMode; use fabro_types::{RunId, RunProvenance}; use fabro_util::json::normalize_json_value; use tokio::task::spawn_blocking; @@ -85,9 +85,9 @@ pub async fn create( }) .map_err(|err| Error::Parse(err.to_string()))?; - if WorkflowSettingsBuilder::from_layer(&resolved.settings) - .map_or(true, |settings| settings.run.execution.mode != RunMode::DryRun) - { + if WorkflowSettingsBuilder::from_layer(&resolved.settings).map_or(true, |settings| { + settings.run.execution.mode != RunMode::DryRun + }) { validate_sandbox_provider(&resolved.settings)?; } diff --git a/lib/crates/fabro-workflow/src/operations/fork.rs b/lib/crates/fabro-workflow/src/operations/fork.rs index 3b6116ed3..003114363 100644 --- a/lib/crates/fabro-workflow/src/operations/fork.rs +++ b/lib/crates/fabro-workflow/src/operations/fork.rs @@ -164,7 +164,7 @@ mod tests { use std::str::FromStr; use fabro_store::RunProjection; - use fabro_types::RunId; + use fabro_types::{RunId, WorkflowSettings}; use git2::Oid; use super::super::test_support::*; @@ -177,11 +177,12 @@ mod tests { fn make_run_projection(run_id: &RunId) -> RunProjection { let mut projection = RunProjection::default(); + let settings = serde_json::to_value(WorkflowSettings::default()).unwrap(); projection.spec = Some( serde_json::from_value(serde_json::json!({ "run_id": run_id.to_string(), "created_at": "2025-01-01T00:00:00Z", - "settings": {}, + "settings": settings, "graph": { "name": "test_workflow", "nodes": { diff --git a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs index 29970863b..7f66c797b 100644 --- a/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs +++ b/lib/crates/fabro-workflow/src/operations/rebuild_meta.rs @@ -310,8 +310,7 @@ mod tests { use chrono::{TimeZone, Utc}; use fabro_graphviz::graph::Graph; use fabro_store::{Database, RunProjection, StageId}; - use fabro_types::settings::SettingsLayer; - use fabro_types::{RunId, RunSpec, SandboxRecord, StartRecord, fixtures}; + use fabro_types::{RunId, RunSpec, SandboxRecord, StartRecord, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::*; @@ -343,7 +342,7 @@ mod tests { fn sample_run_spec(run_id: RunId, host_repo_path: Option<&str>) -> RunSpec { RunSpec { run_id, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: None, working_directory: PathBuf::from("/tmp/project"), diff --git a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs index a3c61ac30..6ec2b8f5c 100644 --- a/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs +++ b/lib/crates/fabro-workflow/src/pipeline/execute/tests.rs @@ -17,8 +17,7 @@ use fabro_hooks::HookSettings; use fabro_interview::AutoApproveInterviewer; use fabro_sandbox::SandboxSpec; use fabro_store::Database; -use fabro_types::settings::SettingsLayer; -use fabro_types::{RunId, fixtures}; +use fabro_types::{RunId, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::*; @@ -95,7 +94,7 @@ fn test_run_options(run_dir: &Path, run_id: &str) -> RunOptions { run_dir: run_dir.to_path_buf(), cancel_token: None, run_id: test_run_id(run_id), - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), git: None, host_repo_path: None, labels: HashMap::new(), @@ -137,7 +136,7 @@ fn persisted_workflow(graph: Graph, source: String, run_dir: &Path, run_id: RunI run_dir.to_path_buf(), RunSpec { run_id, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph, workflow_slug: Some("test".to_string()), working_directory: std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")), diff --git a/lib/crates/fabro-workflow/src/pipeline/finalize.rs b/lib/crates/fabro-workflow/src/pipeline/finalize.rs index 67dd9011c..203741121 100644 --- a/lib/crates/fabro-workflow/src/pipeline/finalize.rs +++ b/lib/crates/fabro-workflow/src/pipeline/finalize.rs @@ -304,8 +304,7 @@ mod tests { use fabro_graphviz::graph::Graph; use fabro_store::Database; - use fabro_types::settings::SettingsLayer; - use fabro_types::{RunId, fixtures}; + use fabro_types::{RunId, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::*; @@ -319,7 +318,7 @@ mod tests { fn test_run_options(run_dir: &std::path::Path) -> RunOptions { RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.to_path_buf(), cancel_token: None, run_id: test_run_id(), diff --git a/lib/crates/fabro-workflow/src/pipeline/initialize.rs b/lib/crates/fabro-workflow/src/pipeline/initialize.rs index a3ee44810..a22fc3a6f 100644 --- a/lib/crates/fabro-workflow/src/pipeline/initialize.rs +++ b/lib/crates/fabro-workflow/src/pipeline/initialize.rs @@ -715,12 +715,7 @@ pub async fn initialize( Ok(Initialized { graph, source, - inputs: options - .run_options - .settings - .run - .inputs - .clone(), + inputs: options.run_options.settings.run.inputs.clone(), run_options: options.run_options, workflow_path: options.workflow_path, workflow_bundle: options.workflow_bundle, @@ -754,8 +749,7 @@ mod tests { use fabro_interview::AutoApproveInterviewer; use fabro_sandbox::SandboxSpec; use fabro_store::Database; - use fabro_types::settings::SettingsLayer; - use fabro_types::{RunId, fixtures}; + use fabro_types::{RunId, WorkflowSettings, fixtures}; use fabro_vault::{SecretType, Vault}; use object_store::memory::InMemory; use tokio::sync::RwLock as AsyncRwLock; @@ -837,7 +831,7 @@ mod tests { fn test_settings(run_dir: &std::path::Path) -> RunOptions { RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.to_path_buf(), cancel_token: None, run_id: test_run_id(), @@ -859,7 +853,7 @@ mod tests { run_dir.to_path_buf(), RunSpec { run_id: test_run_id(), - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph, workflow_slug: Some("test".to_string()), working_directory: std::env::current_dir().unwrap(), diff --git a/lib/crates/fabro-workflow/src/pipeline/persist.rs b/lib/crates/fabro-workflow/src/pipeline/persist.rs index db513c7d8..604bd05a4 100644 --- a/lib/crates/fabro-workflow/src/pipeline/persist.rs +++ b/lib/crates/fabro-workflow/src/pipeline/persist.rs @@ -63,9 +63,6 @@ mod tests { use fabro_graphviz::graph::{AttrValue, Edge, Graph, Node}; use fabro_store::{Database, RunDatabase}; use fabro_types::fixtures; - use fabro_types::settings::SettingsLayer; - use fabro_types::settings::cli::{CliLayer, CliOutputLayer, OutputVerbosity}; - use fabro_types::settings::run::{RunExecutionLayer, RunLayer, RunMode}; use object_store::memory::InMemory; use super::*; @@ -128,22 +125,15 @@ mod tests { fn sample_record(graph: Graph) -> RunSpec { RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer { - run: Some(RunLayer { - execution: Some(RunExecutionLayer { - mode: Some(RunMode::DryRun), - ..RunExecutionLayer::default() - }), - ..RunLayer::default() - }), - cli: Some(CliLayer { - output: Some(CliOutputLayer { - verbosity: Some(OutputVerbosity::Verbose), - ..CliOutputLayer::default() - }), - ..CliLayer::default() - }), - ..SettingsLayer::default() + settings: fabro_types::WorkflowSettings { + run: fabro_types::settings::RunNamespace { + execution: fabro_types::settings::run::RunExecutionSettings { + mode: fabro_types::settings::run::RunMode::DryRun, + ..fabro_types::settings::run::RunExecutionSettings::default() + }, + ..fabro_types::settings::RunNamespace::default() + }, + ..fabro_types::WorkflowSettings::default() }, graph, workflow_slug: Some("ship".to_string()), diff --git a/lib/crates/fabro-workflow/src/pipeline/pull_request.rs b/lib/crates/fabro-workflow/src/pipeline/pull_request.rs index fddfce471..734ca78af 100644 --- a/lib/crates/fabro-workflow/src/pipeline/pull_request.rs +++ b/lib/crates/fabro-workflow/src/pipeline/pull_request.rs @@ -596,7 +596,6 @@ mod tests { AggregateStats, FrictionKind, FrictionPoint, OpenItem, OpenItemKind, StageRetro, }; use fabro_store::Database; - use fabro_types::settings::SettingsLayer; use fabro_types::{BilledTokenCounts, RunSpec, SuccessReason, fixtures}; use futures::stream; use object_store::memory::InMemory; @@ -1088,7 +1087,7 @@ mod tests { let run_spec = RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: fabro_types::WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: Some("test".to_string()), working_directory: PathBuf::from("/tmp/project"), @@ -1153,7 +1152,7 @@ mod tests { let run_spec = RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: fabro_types::WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: Some("test".to_string()), working_directory: PathBuf::from("/tmp/project"), @@ -1371,7 +1370,7 @@ mod tests { let run_store = store.create_run(&fixtures::RUN_1).await.unwrap(); let run_spec = RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: fabro_types::WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: None, working_directory: tmp.path().to_path_buf(), diff --git a/lib/crates/fabro-workflow/src/pipeline/retro.rs b/lib/crates/fabro-workflow/src/pipeline/retro.rs index 74dd181a2..78f689486 100644 --- a/lib/crates/fabro-workflow/src/pipeline/retro.rs +++ b/lib/crates/fabro-workflow/src/pipeline/retro.rs @@ -184,8 +184,7 @@ mod tests { use fabro_graphviz::graph::Graph; use fabro_store::Database; - use fabro_types::settings::SettingsLayer; - use fabro_types::{RunId, fixtures}; + use fabro_types::{RunId, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::*; @@ -234,7 +233,7 @@ mod tests { let run_store = inner; let run_spec = RunSpec { run_id: test_run_id(), - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: None, working_directory: run_dir.to_path_buf(), @@ -297,7 +296,7 @@ mod tests { fn test_run_options(run_dir: &std::path::Path) -> RunOptions { RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.to_path_buf(), cancel_token: None, run_id: test_run_id(), diff --git a/lib/crates/fabro-workflow/src/run_dump.rs b/lib/crates/fabro-workflow/src/run_dump.rs index c4dd6c941..dbca35622 100644 --- a/lib/crates/fabro-workflow/src/run_dump.rs +++ b/lib/crates/fabro-workflow/src/run_dump.rs @@ -427,10 +427,9 @@ mod tests { use fabro_store::{NodeState, RunProjection, StageId}; use fabro_types::graph::Graph; use fabro_types::run::RunSpec; - use fabro_types::settings::SettingsLayer; use fabro_types::{ Checkpoint, Conclusion, NodeStatusRecord, RunStatus, SandboxRecord, StageStatus, - StartRecord, SuccessReason, fixtures, + StartRecord, SuccessReason, WorkflowSettings, fixtures, }; use super::RunDump; @@ -439,7 +438,7 @@ mod tests { fn sample_run_spec() -> RunSpec { RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph: Graph::new("ship"), workflow_slug: Some("demo".to_string()), working_directory: PathBuf::from("/tmp/project"), diff --git a/lib/crates/fabro-workflow/src/run_lookup.rs b/lib/crates/fabro-workflow/src/run_lookup.rs index 0e832624a..1421ddea8 100644 --- a/lib/crates/fabro-workflow/src/run_lookup.rs +++ b/lib/crates/fabro-workflow/src/run_lookup.rs @@ -402,8 +402,7 @@ mod tests { use fabro_graphviz::graph::Graph; use fabro_store::Database; - use fabro_types::settings::SettingsLayer; - use fabro_types::{RunStatus, fixtures}; + use fabro_types::{RunStatus, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::scan_runs_combined; @@ -423,7 +422,7 @@ mod tests { fn sample_run_spec() -> RunSpec { RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: Some("test".to_string()), working_directory: PathBuf::from("/tmp/project"), diff --git a/lib/crates/fabro-workflow/src/run_options.rs b/lib/crates/fabro-workflow/src/run_options.rs index 6b780c59f..5ee57fb05 100644 --- a/lib/crates/fabro-workflow/src/run_options.rs +++ b/lib/crates/fabro-workflow/src/run_options.rs @@ -3,8 +3,8 @@ use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use fabro_types::{RunId, WorkflowSettings}; use fabro_types::settings::run::RunMode; +use fabro_types::{RunId, WorkflowSettings}; use crate::git::{GitAuthor, git_author_from_settings}; diff --git a/lib/crates/fabro-workflow/src/runtime_store.rs b/lib/crates/fabro-workflow/src/runtime_store.rs index 1b15c7fe2..bdec544fc 100644 --- a/lib/crates/fabro-workflow/src/runtime_store.rs +++ b/lib/crates/fabro-workflow/src/runtime_store.rs @@ -112,8 +112,7 @@ mod tests { use fabro_graphviz::graph::Graph; use fabro_store::Database; use fabro_types::run_event::RunSubmittedProps; - use fabro_types::settings::SettingsLayer; - use fabro_types::{EventBody, RunEvent, fixtures}; + use fabro_types::{EventBody, RunEvent, WorkflowSettings, fixtures}; use object_store::memory::InMemory; use super::RunStoreHandle; @@ -133,7 +132,7 @@ mod tests { fn test_run_spec() -> RunSpec { RunSpec { run_id: fixtures::RUN_1, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), graph: Graph::new("test"), workflow_slug: Some("test".to_string()), working_directory: PathBuf::from("/tmp/test"), diff --git a/lib/crates/fabro-workflow/src/test_support.rs b/lib/crates/fabro-workflow/src/test_support.rs index ebebefc5f..f26e86293 100644 --- a/lib/crates/fabro-workflow/src/test_support.rs +++ b/lib/crates/fabro-workflow/src/test_support.rs @@ -115,11 +115,7 @@ async fn initialized( initialized: Initialized { graph: graph.clone(), source: String::new(), - inputs: run_options - .settings - .run - .inputs - .clone(), + inputs: run_options.settings.run.inputs.clone(), run_options: run_options.clone(), workflow_path: None, workflow_bundle: None, diff --git a/lib/crates/fabro-workflow/tests/it/daytona_integration.rs b/lib/crates/fabro-workflow/tests/it/daytona_integration.rs index 595b46ca4..66a4638e0 100644 --- a/lib/crates/fabro-workflow/tests/it/daytona_integration.rs +++ b/lib/crates/fabro-workflow/tests/it/daytona_integration.rs @@ -27,9 +27,7 @@ use fabro_graphviz::graph::{AttrValue, Edge, Graph, Node}; use fabro_llm::provider::Provider; use fabro_sandbox::daytona::{DaytonaConfig, DaytonaSandbox, DaytonaSnapshotConfig}; use fabro_store::{ArtifactStore, Database}; -use fabro_types::settings::SettingsLayer; -use fabro_types::settings::run::{RunArtifactsLayer, RunLayer}; -use fabro_types::{RunId, StageId}; +use fabro_types::{RunId, StageId, WorkflowSettings}; use fabro_workflow::artifact::sync_artifacts_to_env; use fabro_workflow::context::Context; use fabro_workflow::error::Error; @@ -511,7 +509,7 @@ async fn daytona_pipeline_artifact_offload_and_sync() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -689,7 +687,7 @@ async fn daytona_git_checkpoint_remote_emits_events() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("git-cp-test"), @@ -860,7 +858,7 @@ async fn daytona_parallel_git_branching_e2e() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), Arc::clone(&env)); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_tmp.path().to_path_buf(), cancel_token: None, run_id, @@ -1213,7 +1211,7 @@ async fn daytona_git_checkpoint_with_shadow_branch() { let meta_branch = MetadataStore::branch_name(&run_id.to_string()); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id, @@ -1347,14 +1345,14 @@ async fn daytona_asset_collection() { graph.edges.push(Edge::new("create_assets", "exit")); let run_options = RunOptions { - settings: SettingsLayer { - run: Some(RunLayer { - artifacts: Some(RunArtifactsLayer { + settings: WorkflowSettings { + run: fabro_types::settings::RunNamespace { + artifacts: fabro_types::settings::run::ArtifactsSettings { include: vec!["test-results/**".to_string()], - }), - ..RunLayer::default() - }), - ..SettingsLayer::default() + }, + ..fabro_types::settings::RunNamespace::default() + }, + ..WorkflowSettings::default() }, run_dir: dir.path().to_path_buf(), cancel_token: None, @@ -1614,7 +1612,7 @@ async fn daytona_git_push_run_branch_to_origin() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id, diff --git a/lib/crates/fabro-workflow/tests/it/git_integration.rs b/lib/crates/fabro-workflow/tests/it/git_integration.rs index caf03567a..ad09a6af9 100644 --- a/lib/crates/fabro-workflow/tests/it/git_integration.rs +++ b/lib/crates/fabro-workflow/tests/it/git_integration.rs @@ -10,8 +10,7 @@ use std::sync::Arc; use fabro_agent::Sandbox; use fabro_graphviz::graph::{AttrValue, Edge, Graph, Node}; -use fabro_types::settings::SettingsLayer; -use fabro_types::{RunEvent, fixtures}; +use fabro_types::{RunEvent, WorkflowSettings, fixtures}; use fabro_workflow::event::Emitter; use fabro_workflow::git::{ MetadataStore, add_worktree, branch_needs_push, create_branch, push_branch, push_ref, @@ -157,7 +156,7 @@ fn test_run_options(run_dir: &Path) -> RunOptions { run_dir: run_dir.to_path_buf(), cancel_token: None, run_id: fixtures::RUN_2, - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), git: None, host_repo_path: None, labels: HashMap::new(), diff --git a/lib/crates/fabro-workflow/tests/it/integration.rs b/lib/crates/fabro-workflow/tests/it/integration.rs index 9c174ea7a..0d5cc2c01 100644 --- a/lib/crates/fabro-workflow/tests/it/integration.rs +++ b/lib/crates/fabro-workflow/tests/it/integration.rs @@ -32,9 +32,7 @@ use fabro_interview::{ }; use fabro_llm::provider::Provider; use fabro_store::{ArtifactStore, Database}; -use fabro_types::settings::SettingsLayer; -use fabro_types::settings::run::{RunArtifactsLayer, RunLayer}; -use fabro_types::{RunEvent, RunId, StageId}; +use fabro_types::{RunEvent, RunId, StageId, WorkflowSettings}; use fabro_validate::{Severity, validate, validate_or_raise}; use fabro_workflow::context::Context; use fabro_workflow::error::{Error, FailureSignatureExt}; @@ -343,7 +341,7 @@ async fn end_to_end_linear_pipeline() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -472,7 +470,7 @@ async fn end_to_end_branching_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -591,7 +589,7 @@ async fn end_to_end_human_gate_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -686,7 +684,7 @@ async fn human_gate_interrupted_input_fails_closed_without_fail_route() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -796,7 +794,7 @@ async fn human_gate_interrupted_input_routes_via_outcome_fail_condition() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -909,7 +907,7 @@ async fn goal_gate_routes_to_retry_target_on_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1029,7 +1027,7 @@ async fn goal_gate_routes_to_retry_target_when_present() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1343,7 +1341,7 @@ async fn retry_on_failure_then_succeed() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1417,7 +1415,7 @@ async fn pipeline_with_many_nodes() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1763,7 +1761,7 @@ async fn smoke_test_with_mock_codergen_backend() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1864,7 +1862,7 @@ async fn end_to_end_parallel_fan_out_fan_in() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -1976,7 +1974,7 @@ async fn resume_from_checkpoint_completes_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2074,7 +2072,7 @@ async fn resume_from_checkpoint_preserves_goal_gate_outcomes() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2116,7 +2114,7 @@ async fn graph_goal_in_context() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2154,7 +2152,7 @@ async fn event_streaming_lifecycle() { let events = collect_events(&emitter); let engine = WorkflowRunner::new(make_linear_registry(), Arc::new(emitter), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2233,7 +2231,7 @@ async fn context_flow_between_stages() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2288,7 +2286,7 @@ async fn tool_handler_e2e() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2362,7 +2360,7 @@ async fn auto_approve_interviewer_e2e() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2401,7 +2399,7 @@ async fn codergen_without_backend_simulated() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2505,7 +2503,7 @@ async fn branching_loop_back_on_failure() { ); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2590,7 +2588,7 @@ async fn human_gate_loops_back() { registry.register("human", Box::new(HumanHandler::new(interviewer))); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2654,7 +2652,7 @@ async fn scenario_ship_a_feature() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2738,7 +2736,7 @@ async fn scenario_parallel_expert_review() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2824,7 +2822,7 @@ async fn scenario_node_retries_on_retry_status() { ); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2888,7 +2886,7 @@ async fn scenario_loop_restart_resets_context() { ); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -2955,7 +2953,7 @@ async fn scenario_bug_triage_router() { registry.register("conditional", Box::new(ConditionalHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3016,7 +3014,7 @@ async fn scenario_crash_recovery() { registry.register("exit", Box::new(ExitHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3125,7 +3123,7 @@ async fn manager_loop_stop_condition_satisfied_e2e() { registry.register("stack.manager_loop", Box::new(SubWorkflowHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3206,7 +3204,7 @@ async fn manager_loop_max_cycles_exceeded_e2e() { registry.register("stack.manager_loop", Box::new(SubWorkflowHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3346,7 +3344,7 @@ async fn conditional_branching_success_fail_paths() { registry.register("always_fail", Box::new(AlwaysFailHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3401,7 +3399,7 @@ async fn edge_selection_condition_match_wins_over_weight() { registry.register("exit", Box::new(ExitHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3450,7 +3448,7 @@ async fn edge_selection_weight_breaks_ties() { registry.register("exit", Box::new(ExitHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3491,7 +3489,7 @@ async fn edge_selection_lexical_tiebreak() { registry.register("exit", Box::new(ExitHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3551,7 +3549,7 @@ async fn context_updates_visible_across_nodes() { registry.register("context_setter", Box::new(ContextSetterHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3597,7 +3595,7 @@ async fn stylesheet_applies_model_override() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3652,7 +3650,7 @@ async fn custom_handler_registration_and_execution() { registry.register("my_custom", Box::new(CustomHandler)); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3729,7 +3727,7 @@ async fn integration_smoke_plan_implement_review_done() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3820,7 +3818,7 @@ async fn manager_loop_runs_child_engine_e2e() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -3953,7 +3951,7 @@ async fn manager_loop_context_flows_e2e() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4028,7 +4026,7 @@ async fn manager_loop_child_dotfile_e2e() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4131,7 +4129,7 @@ async fn import_e2e_through_engine() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4285,7 +4283,7 @@ async fn fidelity_default_is_compact() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4341,7 +4339,7 @@ async fn fidelity_graph_default_applied() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4393,7 +4391,7 @@ async fn fidelity_node_overrides_graph_default() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4451,7 +4449,7 @@ async fn fidelity_edge_overrides_node_and_graph() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4499,7 +4497,7 @@ async fn fidelity_full_produces_empty_preamble() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4557,7 +4555,7 @@ async fn fidelity_truncate_preamble_minimal() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4628,7 +4626,7 @@ async fn fidelity_summary_low_mode() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4694,7 +4692,7 @@ async fn fidelity_summary_medium_mode() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4760,7 +4758,7 @@ async fn fidelity_summary_high_mode() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4819,7 +4817,7 @@ async fn fidelity_full_sets_thread_id_in_context() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4889,7 +4887,7 @@ async fn fidelity_full_nodes_share_thread_id() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -4969,7 +4967,7 @@ async fn fidelity_resume_degrades_full_to_summary_high() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5065,7 +5063,7 @@ async fn fidelity_resume_degrade_only_affects_first_hop() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5148,7 +5146,7 @@ async fn fidelity_resume_no_degrade_when_not_full() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5189,7 +5187,7 @@ async fn fidelity_stored_in_checkpoint_context() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5281,7 +5279,7 @@ async fn fidelity_precedence_multi_node_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5348,7 +5346,7 @@ async fn fidelity_compact_preamble_includes_completed_stages_and_context() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5422,7 +5420,7 @@ async fn fidelity_summary_low_excludes_context_values_in_pipeline() { ); let engine_low = WorkflowRunner::new(registry_low, Arc::new(Emitter::default()), local_env()); let run_options_low = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir_low.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5488,7 +5486,7 @@ async fn fidelity_summary_low_excludes_context_values_in_pipeline() { ); let engine_med = WorkflowRunner::new(registry_med, Arc::new(Emitter::default()), local_env()); let run_options_med = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir_med.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5559,7 +5557,7 @@ async fn fidelity_thread_id_fallback_to_previous_node_in_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5612,7 +5610,7 @@ async fn fidelity_thread_id_from_node_class_in_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5668,7 +5666,7 @@ async fn fidelity_edge_thread_id_override_in_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5725,7 +5723,7 @@ async fn fidelity_full_without_explicit_thread_id_uses_previous_node() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5792,7 +5790,7 @@ async fn fidelity_from_parsed_dot_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5840,7 +5838,7 @@ async fn fidelity_checkpoint_roundtrip_preserves_fidelity() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5912,7 +5910,7 @@ async fn fidelity_node_thread_id_overrides_edge_thread_id_in_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -5998,7 +5996,7 @@ async fn fidelity_resume_preserves_context_values_across_checkpoint() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6043,7 +6041,7 @@ mod real_llm { use fabro_llm::client::Client; use fabro_llm::providers::OpenAiAdapter; use fabro_llm::types::{Message, Request}; - use fabro_types::settings::SettingsLayer; + use fabro_types::WorkflowSettings; use fabro_workflow::context::Context; use fabro_workflow::error::Error; use fabro_workflow::handler::agent::{AgentHandler, CodergenBackend, CodergenResult}; @@ -6234,7 +6232,7 @@ mod real_llm { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6342,7 +6340,7 @@ mod real_llm { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6474,7 +6472,7 @@ mod real_llm { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6574,7 +6572,7 @@ mod real_llm { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6667,7 +6665,7 @@ async fn human_gate_freeform_only_routes_text() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6797,7 +6795,7 @@ async fn human_gate_freeform_with_fixed_choice_match() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -6913,7 +6911,7 @@ async fn human_gate_freeform_fallback_on_unmatched_text() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -7040,7 +7038,7 @@ async fn human_gate_freeform_sets_allow_freeform_on_question() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -7148,7 +7146,7 @@ async fn human_gate_without_freeform_sets_allow_freeform_false() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -7450,7 +7448,7 @@ fn engine_with_hooks_and_events( fn make_run_options(dir: &std::path::Path) -> RunOptions { RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.to_path_buf(), cancel_token: None, run_id: test_run_id("hook-test-run"), @@ -8388,7 +8386,7 @@ async fn run_fidelity_prompt_pipeline(fidelity: &str) -> String { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -8589,7 +8587,7 @@ async fn large_context_values_are_offloaded_to_artifact_store() { let events = collect_events(&emitter); let engine = WorkflowRunner::new(registry, Arc::new(emitter), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -8793,7 +8791,7 @@ async fn artifact_pointers_rewritten_for_remote_sandbox() { let remote_env = Arc::new(RemoteMockEnv::new("/sandbox")); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), remote_env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -8880,7 +8878,7 @@ async fn downstream_local_execution_materializes_blob_refs_to_runtime_files() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -8967,7 +8965,7 @@ async fn downstream_remote_execution_materializes_blob_refs_to_sandbox_files() { let remote_env = Arc::new(RemoteMockEnv::new("/sandbox")); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), remote_env.clone()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -9097,7 +9095,7 @@ async fn node_dir_uses_visit_count_on_revisit() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -9967,7 +9965,7 @@ async fn full_pipeline_with_cli_backend_node() { let dir = tempfile::tempdir().unwrap(); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), env); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -10085,7 +10083,7 @@ async fn stylesheet_backend_property_routes_to_cli() { let dir = tempfile::tempdir().unwrap(); let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), env); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), @@ -10280,7 +10278,7 @@ async fn git_checkpoint_host_emits_events_and_diff_patch() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), env); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-docker"), @@ -10447,7 +10445,7 @@ async fn git_checkpoint_host_writes_shadow_branch() { let meta_branch = MetadataStore::branch_name(&run_id.to_string()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.path().to_path_buf(), cancel_token: None, run_id, @@ -10646,7 +10644,7 @@ async fn parallel_git_branching_host_e2e() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), env); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.path().to_path_buf(), cancel_token: None, run_id, @@ -10895,7 +10893,7 @@ async fn git_checkpoint_host_skips_empty_diff_patch() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), env); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: run_dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("empty-diff"), @@ -11265,7 +11263,7 @@ async fn e2e_circuit_breaker_deterministic_self_loop() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-circuit-breaker"), @@ -11311,7 +11309,7 @@ async fn e2e_circuit_breaker_custom_limit() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-custom-limit"), @@ -11350,7 +11348,7 @@ async fn e2e_circuit_breaker_ignores_transient_failures() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-transient-no-breaker"), @@ -11396,7 +11394,7 @@ async fn e2e_circuit_breaker_different_reasons_separate_counters() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-varying-reasons"), @@ -11435,7 +11433,7 @@ async fn e2e_circuit_breaker_loop_restart() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-breaker"), @@ -11497,7 +11495,7 @@ async fn e2e_failure_signature_persisted_in_context() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-sig-context"), @@ -11560,7 +11558,7 @@ async fn e2e_failure_signature_hint_overrides_reason_in_context() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-sig-hint"), @@ -11617,7 +11615,7 @@ async fn e2e_signature_maps_persist_in_checkpoint() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-sig-persist"), @@ -11744,7 +11742,7 @@ async fn e2e_circuit_breaker_emits_events_before_abort() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-events"), @@ -11810,7 +11808,7 @@ async fn e2e_circuit_breaker_does_not_fire_below_limit() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-below-limit"), @@ -11905,7 +11903,7 @@ async fn e2e_circuit_breaker_multi_stage_impl_verify_cycle() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-impl-verify-cycle"), @@ -12002,7 +12000,7 @@ async fn e2e_loop_restart_blocked_for_deterministic_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-blocked-det"), @@ -12041,7 +12039,7 @@ async fn e2e_loop_restart_blocked_for_structural_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-blocked-struct"), @@ -12080,7 +12078,7 @@ async fn e2e_loop_restart_blocked_for_budget_exhausted_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-blocked-budget"), @@ -12119,7 +12117,7 @@ async fn e2e_loop_restart_blocked_for_canceled_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-blocked-canceled"), @@ -12155,7 +12153,7 @@ async fn e2e_loop_restart_blocked_for_compilation_loop_failure() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-blocked-comploop"), @@ -12195,7 +12193,7 @@ async fn e2e_loop_restart_allowed_for_transient_infra() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("e2e-restart-allowed-transient"), @@ -12302,7 +12300,7 @@ async fn e2e_stall_watchdog_triggers_from_dot_parsed_pipeline() { let engine = WorkflowRunner::new(registry, Arc::new(emitter), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("stall-e2e"), @@ -12357,7 +12355,7 @@ async fn e2e_stall_watchdog_kept_alive_by_handler_events() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("stall-alive-e2e"), @@ -12402,7 +12400,7 @@ async fn e2e_stall_watchdog_disabled_with_zero_timeout() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("stall-disabled-e2e"), @@ -12467,7 +12465,7 @@ async fn e2e_stall_watchdog_with_explicit_timeout_override() { let engine = WorkflowRunner::new(registry, Arc::new(Emitter::default()), local_env()); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("stall-override-e2e"), @@ -12599,14 +12597,14 @@ async fn asset_collection_local_sandbox_success() { graph.edges.push(Edge::new("create_assets", "exit")); let run_options = RunOptions { - settings: SettingsLayer { - run: Some(RunLayer { - artifacts: Some(RunArtifactsLayer { + settings: WorkflowSettings { + run: fabro_types::settings::RunNamespace { + artifacts: fabro_types::settings::run::ArtifactsSettings { include: vec!["test-results/**".to_string()], - }), - ..RunLayer::default() - }), - ..SettingsLayer::default() + }, + ..fabro_types::settings::RunNamespace::default() + }, + ..WorkflowSettings::default() }, run_dir: run_dir.path().to_path_buf(), cancel_token: None, @@ -12731,14 +12729,14 @@ async fn asset_collection_local_sandbox_on_failure() { graph.edges.push(Edge::new("create_assets", "exit")); let run_options = RunOptions { - settings: SettingsLayer { - run: Some(RunLayer { - artifacts: Some(RunArtifactsLayer { + settings: WorkflowSettings { + run: fabro_types::settings::RunNamespace { + artifacts: fabro_types::settings::run::ArtifactsSettings { include: vec!["test-results/**".to_string()], - }), - ..RunLayer::default() - }), - ..SettingsLayer::default() + }, + ..fabro_types::settings::RunNamespace::default() + }, + ..WorkflowSettings::default() }, run_dir: run_dir.path().to_path_buf(), cancel_token: None, @@ -12836,14 +12834,14 @@ async fn asset_collection_docker_sandbox() { graph.edges.push(Edge::new("create_assets", "exit")); let run_options = RunOptions { - settings: SettingsLayer { - run: Some(RunLayer { - artifacts: Some(RunArtifactsLayer { + settings: WorkflowSettings { + run: fabro_types::settings::RunNamespace { + artifacts: fabro_types::settings::run::ArtifactsSettings { include: vec!["test-results/**".to_string()], - }), - ..RunLayer::default() - }), - ..SettingsLayer::default() + }, + ..fabro_types::settings::RunNamespace::default() + }, + ..WorkflowSettings::default() }, run_dir: run_dir.path().to_path_buf(), cancel_token: None, @@ -12912,7 +12910,7 @@ async fn wait_timer_e2e() { local_env(), ); let run_options = RunOptions { - settings: SettingsLayer::default(), + settings: WorkflowSettings::default(), run_dir: dir.path().to_path_buf(), cancel_token: None, run_id: test_run_id("test-run"), diff --git a/lib/crates/fabro-workflow/tests/materialize_run.rs b/lib/crates/fabro-workflow/tests/materialize_run.rs index 52486dbce..c15702e70 100644 --- a/lib/crates/fabro-workflow/tests/materialize_run.rs +++ b/lib/crates/fabro-workflow/tests/materialize_run.rs @@ -1,3 +1,4 @@ +use fabro_config::WorkflowSettingsBuilder; use fabro_graphviz::graph::Graph; use fabro_graphviz::parser; use fabro_model::{Catalog, Provider}; @@ -34,7 +35,9 @@ fn materialize_run_applies_graph_and_catalog_defaults() { }; let materialized = materialize_run(settings, &graph(source), Catalog::builtin(), &[]); - let resolved = fabro_config::resolve_run_from_file(&materialized).unwrap(); + let resolved = WorkflowSettingsBuilder::from_layer(&materialized) + .unwrap() + .run; assert_eq!( resolved @@ -76,7 +79,9 @@ fn materialize_run_uses_configured_provider_defaults() { Catalog::builtin(), &[Provider::OpenAi], ); - let resolved = fabro_config::resolve_run_from_file(&materialized).unwrap(); + let resolved = WorkflowSettingsBuilder::from_layer(&materialized) + .unwrap() + .run; assert_eq!( resolved