migrate remaining run settings consumers to dense snapshots

This commit is contained in:
Bryan Helmkamp 2026-04-23 11:55:12 -04:00
parent b2bcf0d5a8
commit 9220af6e80
No known key found for this signature in database
53 changed files with 373 additions and 367 deletions

View file

@ -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::*;
}

View file

@ -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;

View file

@ -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"),

View file

@ -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()

View file

@ -84,9 +84,10 @@ pub(crate) async fn attach_run_with_client(
printer: Printer,
) -> Result<ExitCode> {
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));

View file

@ -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

View file

@ -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<PathBuf> {
storage_dir_with_lookup(settings, &|name| std::env::var(name).ok())
@ -50,9 +50,11 @@ pub(crate) fn auth_methods(settings: &SettingsLayer) -> Vec<ServerAuthMethod> {
}
pub(crate) fn config_log_level(settings: &SettingsLayer) -> Option<String> {
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<ServerSettings> {

View file

@ -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;

View file

@ -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",

View file

@ -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"])
);
}

View file

@ -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"

View file

@ -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",

View file

@ -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);

View file

@ -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()),

View file

@ -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<WorkflowSettings, ResolveErrors> {
pub fn from_layer(
layer: &SettingsLayer,
) -> std::result::Result<WorkflowSettings, ResolveErrors> {
let layer = apply_builtin_defaults(layer.clone());
let mut errors = Vec::new();
let project = resolve_project(&layer.project.clone().unwrap_or_default(), &mut errors);

View file

@ -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;

View file

@ -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;

View file

@ -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")

View file

@ -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]

View file

@ -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");

View file

@ -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,

View file

@ -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?;

View file

@ -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<String> {
@ -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 }

View file

@ -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

View file

@ -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,
)
}

View file

@ -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::<Vec<_>>()
.join(", ")
))
.unwrap();
fabro_config::resolve_server_from_file(&settings).unwrap()
.unwrap()
.server
}
#[test]

View file

@ -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()),

View file

@ -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, .. } => {

View file

@ -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()),

View file

@ -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()),

View file

@ -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)]

View file

@ -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")]

View file

@ -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 {

View file

@ -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,

View file

@ -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};

View file

@ -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)?;
}

View file

@ -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": {

View file

@ -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"),

View file

@ -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(".")),

View file

@ -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(),

View file

@ -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(),

View file

@ -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()),

View file

@ -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(),

View file

@ -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(),

View file

@ -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"),

View file

@ -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"),

View file

@ -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};

View file

@ -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"),

View file

@ -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,

View file

@ -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,

View file

@ -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(),

View file

@ -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"),

View file

@ -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