diff --git a/fabro.toml b/fabro.toml index e823e6866..52c92cce5 100644 --- a/fabro.toml +++ b/fabro.toml @@ -1,29 +1,26 @@ -version = 1 +_version = 1 -[fabro] -root = "fabro/" +[project] +directory = "fabro/" -[features] -retros = false - -[pull_request] +[run.pull_request] enabled = true draft = false -[sandbox] +[run.sandbox] provider = "daytona" -[sandbox.daytona] +[run.sandbox.daytona] auto_stop_interval = 30 -[sandbox.daytona.labels] +[run.sandbox.daytona.labels] repo = "fabro-sh/fabro" -[sandbox.daytona.snapshot] +[run.sandbox.daytona.snapshot] name = "fabro-v6" cpu = 4 -memory = 8 -disk = 20 +memory = "8GB" +disk = "20GB" dockerfile = """ FROM ubuntu:24.04 @@ -52,9 +49,10 @@ ENV PATH="/root/.bun/bin:${PATH}" WORKDIR /root """ -[[hooks]] +[[run.hooks]] +id = "cargo-fmt" name = "cargo-fmt" event = "post_tool_use" matcher = "write_file|edit_file|apply_patch" -command = "cargo fmt" +script = "cargo fmt" blocking = true diff --git a/fabro/workflows/gh-triage/workflow.toml b/fabro/workflows/gh-triage/workflow.toml index 3e0093cb8..8fafbcab1 100644 --- a/fabro/workflows/gh-triage/workflow.toml +++ b/fabro/workflows/gh-triage/workflow.toml @@ -1,4 +1,7 @@ -version = 1 +_version = 1 -[github] -permissions = { pull_requests = "read", issues = "read" } +[server.integrations.github] + +[server.integrations.github.permissions] +pull_requests = "read" +issues = "read" diff --git a/fabro/workflows/implement-issue/workflow.toml b/fabro/workflows/implement-issue/workflow.toml index 0ec3f59de..2a0f45f16 100644 --- a/fabro/workflows/implement-issue/workflow.toml +++ b/fabro/workflows/implement-issue/workflow.toml @@ -1,4 +1,7 @@ -version = 1 +_version = 1 -[github] -permissions = { issues = "read", pull_requests = "write" } +[server.integrations.github] + +[server.integrations.github.permissions] +issues = "read" +pull_requests = "write" diff --git a/fabro/workflows/implement-plan/workflow.toml b/fabro/workflows/implement-plan/workflow.toml index 2ebc2a05d..9e79c2378 100644 --- a/fabro/workflows/implement-plan/workflow.toml +++ b/fabro/workflows/implement-plan/workflow.toml @@ -1 +1 @@ -version = 1 \ No newline at end of file +_version = 1 diff --git a/fabro/workflows/smoke/workflow.toml b/fabro/workflows/smoke/workflow.toml index d9914dfa6..9e79c2378 100644 --- a/fabro/workflows/smoke/workflow.toml +++ b/fabro/workflows/smoke/workflow.toml @@ -1 +1 @@ -version = 1 +_version = 1 diff --git a/lib/crates/fabro-cli/src/manifest_builder.rs b/lib/crates/fabro-cli/src/manifest_builder.rs index 58bbf317e..2f50af38f 100644 --- a/lib/crates/fabro-cli/src/manifest_builder.rs +++ b/lib/crates/fabro-cli/src/manifest_builder.rs @@ -562,10 +562,10 @@ mod tests { std::fs::create_dir_all(workflow_dir.join("prompts")).unwrap(); std::fs::create_dir_all(workflow_dir.join("imports")).unwrap(); std::fs::create_dir_all(&child_dir).unwrap(); - std::fs::write(project.join("fabro.toml"), "version = 1\n").unwrap(); + std::fs::write(project.join("fabro.toml"), "_version = 1\n").unwrap(); std::fs::write( workflow_dir.join("workflow.toml"), - "version = 1\ngraph = \"workflow.fabro\"\n", + "_version = 1\n\n[workflow]\ngraph = \"workflow.fabro\"\n", ) .unwrap(); std::fs::write( diff --git a/lib/crates/fabro-cli/tests/it/cmd/config.rs b/lib/crates/fabro-cli/tests/it/cmd/config.rs index c9d9a8396..346442220 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/config.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/config.rs @@ -82,37 +82,44 @@ fn setup_settings_fixture(context: &fabro_test::TestContext) -> tempfile::TempDi context.write_home( ".fabro/settings.toml", r#" -verbose = true +_version = 1 -[llm] -model = "cli-model" +[cli.output] +verbosity = "verbose" + +[run.model] +name = "cli-model" provider = "openai" -[vars] +[run.inputs] cli_only = "1" shared = "cli" -[checkpoint] +[run.checkpoint] exclude_globs = ["cli-only", "shared"] -[[hooks]] +[[run.hooks]] +id = "shared" name = "shared" event = "run_start" -command = "echo cli" +script = "echo cli" -[mcp_servers.shared] +[run.agent.mcps.shared] type = "stdio" command = ["echo", "cli"] -[sandbox] +[run.sandbox] provider = "daytona" -[sandbox.daytona] -labels = { cli_only = "1", shared = "cli" } - -[sandbox.env] +[run.sandbox.env] CLI_ONLY = "1" SHARED = "cli" + +[run.sandbox.daytona] + +[run.sandbox.daytona.labels] +cli_only = "1" +shared = "cli" "#, ); @@ -120,22 +127,23 @@ SHARED = "cli" std::fs::write( project.path().join("fabro.toml"), r#" -version = 1 +_version = 1 -[fabro] -root = "fabro" +[project] +directory = "fabro" -[llm] -model = "project-model" +[run.model] +name = "project-model" -[vars] +[run.inputs] project_only = "1" shared = "project" -[[hooks]] +[[run.hooks]] +id = "project" name = "project" event = "run_complete" -command = "echo project" +script = "echo project" "#, ) .unwrap(); @@ -145,44 +153,51 @@ command = "echo project" std::fs::write( workflow_dir.join("workflow.toml"), r#" -version = 1 +_version = 1 + +[run] goal = "demo goal" -[llm] -model = "run-model" +[run.model] +name = "run-model" provider = "anthropic" -[vars] +[run.inputs] run_only = "1" shared = "run" -[checkpoint] +[run.checkpoint] exclude_globs = ["run-only", "shared"] -[[hooks]] +[[run.hooks]] +id = "shared" name = "shared" event = "run_start" -command = "echo run" +script = "echo run" -[[hooks]] +[[run.hooks]] +id = "run-only" name = "run-only" event = "run_complete" -command = "echo run-only" +script = "echo run-only" -[mcp_servers.shared] +[run.agent.mcps.shared] type = "stdio" command = ["echo", "run"] -[mcp_servers.run_only] +[run.agent.mcps.run_only] type = "stdio" command = ["echo", "run-only"] -[sandbox.daytona] -labels = { run_only = "1", shared = "run" } - -[sandbox.env] +[run.sandbox.env] RUN_ONLY = "1" SHARED = "run" + +[run.sandbox.daytona] + +[run.sandbox.daytona.labels] +run_only = "1" +shared = "run" "#, ) .unwrap(); @@ -208,11 +223,16 @@ fn setup_external_workflow_fixture( ".fabro/settings.toml", format!( r#" -storage_dir = "{}" -auto_approve = true +_version = 1 -[setup] -commands = ["cli-setup"] +[server.storage] +root = "{}" + +[run.execution] +approval = "auto" + +[[run.prepare.steps]] +script = "cli-setup" "#, storage_dir.display() ), @@ -222,12 +242,12 @@ commands = ["cli-setup"] std::fs::write( project.path().join("fabro.toml"), r#" -version = 1 +_version = 1 -[setup] -commands = ["project-setup"] +[[run.prepare.steps]] +script = "project-setup" -[sandbox] +[run.sandbox] preserve = true "#, ) @@ -248,15 +268,19 @@ digraph Test { std::fs::write( project.path().join("workflow.toml"), r#" -version = 1 -goal = "Ship it" +_version = 1 + +[workflow] graph = "workflow.fabro" -[llm] -model = "claude-sonnet-4-6" +[run] +goal = "Ship it" -[setup] -commands = ["workflow-setup"] +[run.model] +name = "claude-sonnet-4-6" + +[[run.prepare.steps]] +script = "workflow-setup" "#, ) .unwrap(); diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo.rs b/lib/crates/fabro-cli/tests/it/cmd/repo.rs index e3ef64050..78b56a56f 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/repo.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/repo.rs @@ -2,9 +2,9 @@ use fabro_test::{fabro_snapshot, test_context}; fn init_fabro_project(context: &fabro_test::TestContext) { context - .write_temp("fabro.toml", "version = 1\n") + .write_temp("fabro.toml", "_version = 1\n") .write_temp("fabro/workflows/hello/workflow.fabro", "digraph {}") - .write_temp("fabro/workflows/hello/workflow.toml", "version = 1\n"); + .write_temp("fabro/workflows/hello/workflow.toml", "_version = 1\n"); } #[test] diff --git a/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs b/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs index 125802476..5857e5638 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/repo_init.rs @@ -58,16 +58,13 @@ fn repo_init_creates_fabro_toml_and_hello_workflow() { # Fabro project configuration # https://docs.fabro.computer/getting-started/quick-start - version = 1 + _version = 1 - [fabro] - root = "fabro/" - - # Disable retrospective analysis after workflow runs: - # retro = false + [project] + directory = "fabro/" # Auto-create pull requests on successful workflow runs. - [pull_request] + [run.pull_request] enabled = true draft = true # auto_merge = true @@ -94,10 +91,12 @@ fn repo_init_creates_fabro_toml_and_hello_workflow() { std::fs::read_to_string(context.temp_dir.join("fabro/workflows/hello/workflow.toml")) .unwrap(), @r###" - version = 1 + _version = 1 + + [workflow] graph = "workflow.fabro" - [sandbox] + [run.sandbox] provider = "local" "### ); @@ -107,7 +106,7 @@ fn repo_init_creates_fabro_toml_and_hello_workflow() { fn repo_init_rejects_already_initialized_repo() { let context = test_context!(); context.git_init(); - std::fs::write(context.temp_dir.join("fabro.toml"), "version = 1\n").unwrap(); + std::fs::write(context.temp_dir.join("fabro.toml"), "_version = 1\n").unwrap(); let mut cmd = context.command(); cmd.args(["repo", "init"]); diff --git a/lib/crates/fabro-cli/tests/it/cmd/run.rs b/lib/crates/fabro-cli/tests/it/cmd/run.rs index ce90bd159..11ebc5ad9 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/run.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/run.rs @@ -454,18 +454,22 @@ fn local_foreground_run_prints_artifact_paths_from_server_artifact_list() { ); context.write_temp( "artifact-summary/run.toml", - r#"version = 1 + r#"_version = 1 + +[workflow] graph = "workflow.fabro" + +[run] goal = "Show stored artifacts" -[sandbox] +[run.sandbox] provider = "local" preserve = true -[sandbox.local] +[run.sandbox.local] worktree_mode = "never" -[artifacts] +[run.artifacts] include = ["assets/**"] "#, ); diff --git a/lib/crates/fabro-cli/tests/it/cmd/runner.rs b/lib/crates/fabro-cli/tests/it/cmd/runner.rs index 894ee01b1..58016cc76 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/runner.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/runner.rs @@ -177,9 +177,9 @@ fn runner_uses_snapshotted_app_id_for_github_credentials() { context.write_home( ".fabro/settings.toml", "\ -version = 1 +_version = 1 -[git] +[server.integrations.github] app_id = \"snapshotted-app-id\" ", ); @@ -222,7 +222,7 @@ digraph GitHubApp { "# ); - context.write_home(".fabro/settings.toml", "version = 1\n"); + context.write_home(".fabro/settings.toml", "_version = 1\n"); let server = server_target(&context.storage_dir); let mut cmd = context.command(); diff --git a/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs b/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs index db88197d3..8fbebe8ec 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/store_dump.rs @@ -137,18 +137,22 @@ fn store_dump_exports_blob_refs_and_artifacts_together() { .unwrap(); fs::write( workspace_dir.join("run.toml"), - r#"version = 1 + r#"_version = 1 + +[workflow] graph = "mixed-export.fabro" + +[run] goal = "Generate oversized command output and artifacts" -[sandbox] +[run.sandbox] provider = "local" preserve = true -[sandbox.local] +[run.sandbox.local] worktree_mode = "never" -[artifacts] +[run.artifacts] include = ["assets/**"] "#, ) diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow.rs b/lib/crates/fabro-cli/tests/it/cmd/workflow.rs index 3c81517fd..89143c2aa 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/workflow.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/workflow.rs @@ -34,10 +34,13 @@ fn list() { let context = test_context!(); context - .write_temp("fabro.toml", "version = 1\n") + .write_temp( + "fabro.toml", + "_version = 1\n\n[project]\ndirectory = \".\"\n", + ) .write_temp( "workflows/my_test_wf/workflow.toml", - "version = 1\ngoal = \"A test workflow\"\n", + "_version = 1\n\n[run]\ngoal = \"A test workflow\"\n", ); let mut cmd = context.command(); diff --git a/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs b/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs index c737206c8..1079604b0 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/workflow_create.rs @@ -79,7 +79,7 @@ fn workflow_create_writes_scaffold_files() { std::fs::read_to_string(project.fabro_root.join("workflows/hello-world/workflow.toml")) .unwrap(), @r###" - version = 1 + _version = 1 "### ); } @@ -125,7 +125,7 @@ fn workflow_create_rejects_existing_workflow() { std::fs::create_dir_all(project.fabro_root.join("workflows/existing")).unwrap(); std::fs::write( project.fabro_root.join("workflows/existing/workflow.toml"), - "version = 1\n", + "_version = 1\n", ) .unwrap(); @@ -163,7 +163,7 @@ fn workflow_create_json_uses_resolved_custom_root_paths() { let project_dir = context.temp_dir.join("project"); context.write_temp( "project/fabro.toml", - "version = 1\n[fabro]\nroot = \"custom/fabro-data\"\n", + "_version = 1\n\n[project]\ndirectory = \"custom/fabro-data\"\n", ); let output = context diff --git a/lib/crates/fabro-server/src/run_manifest.rs b/lib/crates/fabro-server/src/run_manifest.rs index 2915e44ca..3ead8c357 100644 --- a/lib/crates/fabro-server/src/run_manifest.rs +++ b/lib/crates/fabro-server/src/run_manifest.rs @@ -871,10 +871,10 @@ app_id = "snapshotted-app-id" Some(types::ManifestWorkflowConfig { path: "workflow.toml".to_string(), source: r#" -version = 1 +_version = 1 -[setup] -commands = ["workflow-setup"] +[[run.prepare.steps]] +script = "workflow-setup" "# .to_string(), }); @@ -882,10 +882,12 @@ commands = ["workflow-setup"] path: Some("/tmp/home/.fabro/settings.toml".to_string()), source: Some( r#" -[setup] -commands = ["cli-setup"] +_version = 1 -[git] +[[run.prepare.steps]] +script = "cli-setup" + +[server.integrations.github] app_id = "snapshotted-app-id" "# .to_string(), @@ -895,13 +897,15 @@ app_id = "snapshotted-app-id" let prepared = prepare_manifest_with_mode(&server_settings, &manifest, true).unwrap(); + // v2 merge matrix: run.prepare.steps replaces the whole list across + // layers, so the higher-precedence workflow layer wins over cli. assert_eq!( prepared .settings .setup .as_ref() .map(|setup| setup.commands.clone()), - Some(vec!["workflow-setup".to_string(), "cli-setup".to_string(),]) + Some(vec!["workflow-setup".to_string()]) ); assert_eq!(prepared.settings.app_id(), Some("snapshotted-app-id")); assert_eq!(