Rename .dot files to .fabro and update all references

Rename 79 workflow files from .dot to .fabro extension across
fabro/workflows/, test/, test/docs/, and files-internal/demo/.
Update TOML configs, Rust production code, test code, and shell
scripts. Backward compat tests in test/attractor/ are unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-13 22:27:19 -04:00
parent f12f261ddd
commit 85db69fdc1
122 changed files with 199 additions and 175 deletions

View file

@ -1,6 +1,6 @@
version = 1
goal = "Run cargo check in a Daytona cloud sandbox"
graph = "check.dot"
graph = "check.fabro"
[llm]
model = "claude-sonnet"

View file

@ -1,6 +1,6 @@
version = 1
goal = "Create a hyperrealistic interactive 3D Golden Gate Bridge flight experience"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "gpt-5.4"

View file

@ -1,5 +1,5 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "local"

View file

@ -1,5 +1,5 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"

View file

@ -1,5 +1,5 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"

View file

@ -1,5 +1,5 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "local"

View file

@ -1,6 +1,6 @@
version = 1
goal = "Browse Hacker News with Playwright MCP and capture screenshots"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "claude-sonnet-4-6"

View file

@ -1,5 +1,5 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "local"

View file

@ -1,2 +1,2 @@
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"

View file

@ -1,6 +1,6 @@
version = 1
goal = "Browse the web with Playwright while user watches via VNC"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "claude-sonnet-4-6"

View file

@ -1,6 +1,6 @@
version = 1
goal = "Verify the develop-web-game skill is properly set up with its asset files"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "claude-sonnet-4-6"

View file

@ -1,6 +1,6 @@
version = 1
goal = "Search the web for a famous landmark, then generate an image of it"
graph = "14-search-imagegen.dot"
graph = "14-search-imagegen.fabro"
[sandbox]
provider = "daytona"

View file

@ -1303,7 +1303,7 @@ mod runs {
serde_json::to_value(fabro_workflows::cli::run_config::WorkflowRunConfig {
version: 1,
goal: Some("Add rate limiting to auth endpoints".into()),
graph: "implement.dot".into(),
graph: "implement.fabro".into(),
directory: Some("/workspace/api-server".into()),
llm: Some(fabro_workflows::cli::run_config::LlmConfig {
model: Some("claude-opus-4-6".into()),
@ -1415,7 +1415,7 @@ mod workflows {
WorkflowListItem {
name: "Fix Build".into(),
slug: "fix_build".into(),
filename: "fix_build.dot".into(),
filename: "fix_build.fabro".into(),
last_run: Some(WorkflowLastRun {
ran_at: ts("2025-09-15T12:00:00Z"),
}),
@ -1424,7 +1424,7 @@ mod workflows {
WorkflowListItem {
name: "Implement Feature".into(),
slug: "implement".into(),
filename: "implement.dot".into(),
filename: "implement.fabro".into(),
last_run: Some(WorkflowLastRun {
ran_at: ts("2025-09-11T10:00:00Z"),
}),
@ -1433,7 +1433,7 @@ mod workflows {
WorkflowListItem {
name: "Sync Drift".into(),
slug: "sync_drift".into(),
filename: "sync_drift.dot".into(),
filename: "sync_drift.fabro".into(),
last_run: Some(WorkflowLastRun {
ran_at: ts("2025-09-14T14:00:00Z"),
}),
@ -1442,7 +1442,7 @@ mod workflows {
WorkflowListItem {
name: "Expand Product".into(),
slug: "expand".into(),
filename: "expand.dot".into(),
filename: "expand.fabro".into(),
last_run: Some(WorkflowLastRun {
ran_at: ts("2025-09-01T08:00:00Z"),
}),
@ -1475,12 +1475,12 @@ mod workflows {
pub fn detail(name: &str) -> Option<WorkflowDetail> {
let items = [
WorkflowDetail {
name: "Fix Build".into(), slug: "fix_build".into(), filename: "fix_build.dot".into(),
name: "Fix Build".into(), slug: "fix_build".into(), filename: "fix_build.fabro".into(),
description: "Automatically diagnoses and fixes CI build failures by analyzing error logs, identifying root causes, and applying targeted code changes.".into(),
config: run_config_to_api(fabro_workflows::cli::run_config::WorkflowRunConfig {
version: 1,
goal: Some("Diagnose and fix CI build failures".into()),
graph: "fix_build.dot".into(),
graph: "fix_build.fabro".into(),
directory: None,
llm: Some(fabro_workflows::cli::run_config::LlmConfig {
model: Some("claude-sonnet".into()),
@ -1543,12 +1543,12 @@ mod workflows {
"#.into(),
},
WorkflowDetail {
name: "Implement Feature".into(), slug: "implement".into(), filename: "implement.dot".into(),
name: "Implement Feature".into(), slug: "implement".into(), filename: "implement.fabro".into(),
description: "Generates production-ready code from a technical blueprint, including tests, documentation, and a pull request ready for review.".into(),
config: run_config_to_api(fabro_workflows::cli::run_config::WorkflowRunConfig {
version: 1,
goal: Some("Implement feature from technical blueprint".into()),
graph: "implement.dot".into(),
graph: "implement.fabro".into(),
directory: None,
llm: Some(fabro_workflows::cli::run_config::LlmConfig {
model: Some("claude-sonnet".into()),
@ -1629,12 +1629,12 @@ mod workflows {
"#.into(),
},
WorkflowDetail {
name: "Sync Drift".into(), slug: "sync_drift".into(), filename: "sync_drift.dot".into(),
name: "Sync Drift".into(), slug: "sync_drift".into(), filename: "sync_drift.fabro".into(),
description: "Detects configuration and code drift between environments, then generates reconciliation patches to bring everything back in sync.".into(),
config: run_config_to_api(fabro_workflows::cli::run_config::WorkflowRunConfig {
version: 1,
goal: Some("Detect and reconcile configuration drift across environments".into()),
graph: "sync_drift.dot".into(),
graph: "sync_drift.fabro".into(),
directory: None,
llm: Some(fabro_workflows::cli::run_config::LlmConfig {
model: Some("claude-sonnet".into()),
@ -1703,12 +1703,12 @@ mod workflows {
"#.into(),
},
WorkflowDetail {
name: "Expand Product".into(), slug: "expand".into(), filename: "expand.dot".into(),
name: "Expand Product".into(), slug: "expand".into(), filename: "expand.fabro".into(),
description: "Evolves the product by analyzing usage patterns and specifications to propose and implement incremental improvements.".into(),
config: run_config_to_api(fabro_workflows::cli::run_config::WorkflowRunConfig {
version: 1,
goal: Some("Propose and implement incremental product improvements".into()),
graph: "expand.dot".into(),
graph: "expand.fabro".into(),
directory: None,
llm: Some(fabro_workflows::cli::run_config::LlmConfig {
model: Some("claude-sonnet".into()),

View file

@ -58,8 +58,8 @@ root = \"fabro/\"
std::fs::create_dir_all(&workflow_dir)
.with_context(|| format!("failed to create {}", workflow_dir.display()))?;
// Create workflow.dot
let dot_path = workflow_dir.join("workflow.dot");
// Create workflow.fabro
let dot_path = workflow_dir.join("workflow.fabro");
std::fs::write(
&dot_path,
r#"digraph Hello {
@ -79,14 +79,14 @@ root = \"fabro/\"
eprintln!(
" {} {}",
green.apply_to(""),
dim.apply_to("arc/workflows/hello/workflow.dot")
dim.apply_to("arc/workflows/hello/workflow.fabro")
);
// Create workflow.toml
let toml_path = workflow_dir.join("workflow.toml");
std::fs::write(
&toml_path,
"version = 1\ngraph = \"workflow.dot\"\n\n[sandbox]\nprovider = \"local\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n\n[sandbox]\nprovider = \"local\"\n",
)
.with_context(|| format!("failed to write {}", toml_path.display()))?;
eprintln!(

View file

@ -373,7 +373,7 @@ fn dry_run_writes_jsonl_and_live_json() {
"--auto-approve",
"--run-dir",
run_dir.to_str().unwrap(),
"../../../test/simple.dot",
"../../../test/simple.fabro",
])
.assert()
.success();

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/branching.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/branching.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/conditions.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/conditions.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/legacy_tool.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/legacy_tool.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/parallel.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/parallel.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/simple.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/simple.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["run", "--dry-run", "--auto-approve", "../../../test/styled.dot"]
args = ["run", "--dry-run", "--auto-approve", "../../../test/styled.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/branching.dot"]
args = ["validate", "../../../test/branching.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/conditions.dot"]
args = ["validate", "../../../test/conditions.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/invalid.dot"]
args = ["validate", "../../../test/invalid.fabro"]
status = "failed"
[fs]

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/legacy_tool.dot"]
args = ["validate", "../../../test/legacy_tool.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/parallel.dot"]
args = ["validate", "../../../test/parallel.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/simple.dot"]
args = ["validate", "../../../test/simple.fabro"]
[fs]
sandbox = false

View file

@ -1,5 +1,5 @@
bin.name = "fabro"
args = ["validate", "../../../test/styled.dot"]
args = ["validate", "../../../test/styled.fabro"]
[fs]
sandbox = false

View file

@ -11,7 +11,7 @@ use tracing::debug;
use crate::validation::Severity;
use crate::workflow::prepare_from_file;
use super::{print_diagnostics, read_dot_file, relative_path};
use super::{print_diagnostics, read_workflow_file, relative_path};
/// Output format for graph rendering.
#[derive(Debug, Clone, Copy, Default, ValueEnum)]
@ -34,7 +34,7 @@ impl fmt::Display for GraphFormat {
#[derive(Args)]
pub struct GraphArgs {
/// Path to the .dot workflow file, .toml task config, or project workflow name
/// Path to the .fabro workflow file, .toml task config, or project workflow name
pub workflow: PathBuf,
/// Output format
@ -58,7 +58,7 @@ pub fn graph_command(args: &GraphArgs, styles: &Styles) -> anyhow::Result<()> {
bail!("Validation failed");
}
let source = read_dot_file(&dot_path)?;
let source = read_workflow_file(&dot_path)?;
let rendered = render_dot(&source, args.format)?;
if let Some(ref output_path) = args.output {
@ -140,7 +140,7 @@ mod tests {
#[test]
fn graph_missing_file() {
let args = GraphArgs {
workflow: PathBuf::from("/tmp/nonexistent_workflow_99999.dot"),
workflow: PathBuf::from("/tmp/nonexistent_workflow_99999.fabro"),
format: GraphFormat::Svg,
output: None,
};
@ -151,7 +151,10 @@ mod tests {
#[test]
fn graph_invalid_syntax() {
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "not a valid dot file").unwrap();
let args = GraphArgs {
@ -171,7 +174,10 @@ mod tests {
return;
}
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "{VALID_DOT}").unwrap();
let output_dir = tempfile::tempdir().unwrap();
@ -197,7 +203,10 @@ mod tests {
return;
}
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "{VALID_DOT}").unwrap();
let output_dir = tempfile::tempdir().unwrap();
@ -227,7 +236,10 @@ mod tests {
return;
}
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "{VALID_DOT}").unwrap();
let output_dir = tempfile::tempdir().unwrap();
@ -258,10 +270,10 @@ mod tests {
std::fs::create_dir_all(&wf_dir).unwrap();
std::fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
std::fs::write(wf_dir.join("workflow.dot"), VALID_DOT).unwrap();
std::fs::write(wf_dir.join("workflow.fabro"), VALID_DOT).unwrap();
let output_dir = tempfile::tempdir().unwrap();
let output_path = output_dir.path().join("out.svg");

View file

@ -87,7 +87,7 @@ impl FromStr for SandboxProvider {
#[command(
name = "fabro-workflows",
version,
about = "DOT-based workflow runner for AI workflows"
about = "Workflow runner for AI workflows"
)]
pub struct Cli {
#[command(subcommand)]
@ -96,7 +96,7 @@ pub struct Cli {
#[derive(Subcommand)]
pub enum Command {
/// Launch a workflow from a .dot or .toml task file
/// Launch a workflow from a .fabro or .toml task file
Run(RunArgs),
/// Parse and validate a workflow without executing
Validate(ValidateArgs),
@ -104,7 +104,7 @@ pub enum Command {
#[derive(Args)]
pub struct RunArgs {
/// Path to a .dot workflow file or .toml task config (not required with --run-branch)
/// Path to a .fabro workflow file or .toml task config (not required with --run-branch)
#[arg(required_unless_present = "run_branch")]
pub workflow: Option<PathBuf>,
@ -175,22 +175,22 @@ pub struct RunArgs {
#[derive(Args)]
pub struct ValidateArgs {
/// Path to the .dot workflow file
/// Path to the .fabro workflow file
pub workflow: PathBuf,
}
#[derive(Args)]
pub struct ParseArgs {
/// Path to the .dot workflow file
/// Path to the .fabro workflow file
pub workflow: PathBuf,
}
/// Read a .dot file from disk.
/// Read a workflow file from disk.
///
/// # Errors
///
/// Returns an error if the file cannot be read.
pub fn read_dot_file(path: &Path) -> anyhow::Result<String> {
pub fn read_workflow_file(path: &Path) -> anyhow::Result<String> {
std::fs::read_to_string(path)
.map_err(|e| anyhow::anyhow!("Failed to read {}: {e}", path.display()))
}

View file

@ -1,6 +1,6 @@
use std::io::Write;
use super::{read_dot_file, ParseArgs};
use super::{read_workflow_file, ParseArgs};
/// Parse a DOT file and print its raw AST as JSON.
///
@ -14,7 +14,7 @@ pub fn parse_command(args: &ParseArgs) -> anyhow::Result<()> {
fn parse_command_to(args: &ParseArgs, mut out: impl Write) -> anyhow::Result<()> {
let (dot_path, _cfg) = super::project_config::resolve_workflow(&args.workflow)?;
let source = read_dot_file(&dot_path)?;
let source = read_workflow_file(&dot_path)?;
let ast = crate::parser::parse_ast(&source)?;
serde_json::to_writer_pretty(&mut out, &ast)?;
writeln!(out)?;
@ -30,7 +30,10 @@ mod tests {
#[test]
fn parse_command_outputs_json_ast() {
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(
tmp,
r#"digraph Hello {{
@ -54,7 +57,10 @@ mod tests {
#[test]
fn parse_command_rejects_invalid_dot() {
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "not a valid dot file").unwrap();
let args = ParseArgs {
@ -71,11 +77,11 @@ mod tests {
std::fs::create_dir_all(&wf_dir).unwrap();
std::fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
std::fs::write(
wf_dir.join("workflow.dot"),
wf_dir.join("workflow.fabro"),
r#"digraph Hello {
start [shape=Mdiamond]
exit [shape=Msquare]
@ -97,7 +103,7 @@ mod tests {
#[test]
fn parse_command_rejects_missing_file() {
let args = ParseArgs {
workflow: PathBuf::from("/tmp/nonexistent_parse_test_12345.dot"),
workflow: PathBuf::from("/tmp/nonexistent_parse_test_12345.fabro"),
};
let result = parse_command_to(&args, Vec::new());
assert!(result.is_err(), "expected Err for missing file");

View file

@ -80,7 +80,7 @@ pub fn discover_project_config(start: &Path) -> anyhow::Result<Option<(PathBuf,
/// Resolve a workflow argument to a path.
///
/// - If the arg has a file extension (`.toml`, `.dot`, etc.), return it as-is.
/// - If the arg has a file extension (`.toml`, `.fabro`, etc.), return it as-is.
/// - If no extension, attempt project-based resolution: find `fabro.toml`, resolve
/// `{fabro_root}/workflows/{name}/workflow.toml`. Returns an error with suggestions
/// if an `fabro.toml` exists but the workflow wasn't found.
@ -345,10 +345,10 @@ mod tests {
}
#[test]
fn resolve_workflow_arg_dot_extension_returned_as_is() {
fn resolve_workflow_arg_fabro_extension_returned_as_is() {
let tmp = TempDir::new().unwrap();
let result = resolve_workflow_arg_from(Path::new("my-workflow.dot"), tmp.path()).unwrap();
assert_eq!(result, Path::new("my-workflow.dot"));
let result = resolve_workflow_arg_from(Path::new("my-workflow.fabro"), tmp.path()).unwrap();
assert_eq!(result, Path::new("my-workflow.fabro"));
}
#[test]
@ -366,7 +366,7 @@ mod tests {
fs::create_dir_all(&wf_dir).unwrap();
fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
@ -382,7 +382,7 @@ mod tests {
fs::create_dir_all(&wf_dir).unwrap();
fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"w.dot\"\n",
"version = 1\ngraph = \"w.fabro\"\n",
)
.unwrap();
@ -400,7 +400,7 @@ mod tests {
fs::create_dir_all(&wf_dir).unwrap();
fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"w.dot\"\n",
"version = 1\ngraph = \"w.fabro\"\n",
)
.unwrap();
@ -433,7 +433,7 @@ mod tests {
fs::create_dir_all(&wf_dir).unwrap();
fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
@ -441,7 +441,7 @@ mod tests {
assert_eq!(result, wf_dir.join("workflow.toml"));
}
/// Helper: create a temp dir with fabro.toml + workflows/{name}/{workflow.toml, workflow.dot}
/// Helper: create a temp dir with fabro.toml + workflows/{name}/{workflow.toml, workflow.fabro}
/// and chdir into it so `resolve_workflow` (which uses cwd) can find the config.
fn setup_workflow_project(name: &str) -> (TempDir, PathBuf) {
let tmp = TempDir::new().unwrap();
@ -450,15 +450,15 @@ mod tests {
fs::create_dir_all(&wf_dir).unwrap();
fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
fs::write(
wf_dir.join("workflow.dot"),
wf_dir.join("workflow.fabro"),
"digraph G { start [shape=Mdiamond]; exit [shape=Msquare]; start -> exit }",
)
.unwrap();
let dot_path = wf_dir.join("workflow.dot");
let dot_path = wf_dir.join("workflow.fabro");
(tmp, dot_path)
}
@ -483,10 +483,10 @@ mod tests {
}
#[test]
fn resolve_workflow_dot_path() {
fn resolve_workflow_fabro_path() {
let (tmp, expected_dot) = setup_workflow_project("hello");
let (dot_path, cfg) = resolve_workflow_from(&expected_dot, tmp.path()).unwrap();
assert_eq!(dot_path, expected_dot);
assert!(cfg.is_none(), "expected None for .dot path");
assert!(cfg.is_none(), "expected None for .fabro path");
}
}

View file

@ -34,7 +34,7 @@ use indicatif::HumanDuration;
use std::time::Duration;
use super::{
compute_stage_cost, format_cost, format_tokens_human, print_diagnostics, read_dot_file,
compute_stage_cost, format_cost, format_tokens_human, print_diagnostics, read_workflow_file,
RunArgs, SandboxProvider,
};
@ -349,7 +349,7 @@ pub async fn run_command(
.unwrap_or_default();
// 1. Parse and validate workflow
let source = read_dot_file(&dot_path)?;
let source = read_workflow_file(&dot_path)?;
let vars = run_cfg
.as_ref()
.and_then(|c| c.vars.as_ref())
@ -2524,7 +2524,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".to_string()),
graph: "test.dot".to_string(),
graph: "test.fabro".to_string(),
directory: None,
llm: Some(run_config::LlmConfig {
model: Some("toml-model".to_string()),
@ -2568,7 +2568,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".to_string()),
graph: "test.dot".to_string(),
graph: "test.fabro".to_string(),
directory: None,
llm: Some(run_config::LlmConfig {
model: Some("toml-model".to_string()),
@ -2647,7 +2647,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".to_string()),
graph: "test.dot".to_string(),
graph: "test.fabro".to_string(),
directory: None,
llm: Some(run_config::LlmConfig {
model: Some("toml-model".to_string()),
@ -2673,7 +2673,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".to_string()),
graph: "w.dot".into(),
graph: "w.fabro".into(),
directory: None,
llm: None,
setup: None,
@ -2704,7 +2704,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".to_string()),
graph: "w.dot".into(),
graph: "w.fabro".into(),
directory: None,
llm: None,
setup: None,
@ -2782,7 +2782,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".into()),
graph: "w.dot".into(),
graph: "w.fabro".into(),
directory: None,
llm: None,
setup: None,
@ -2842,7 +2842,7 @@ mod tests {
let cfg = run_config::WorkflowRunConfig {
version: 1,
goal: Some("test".into()),
graph: "w.dot".into(),
graph: "w.fabro".into(),
directory: None,
llm: None,
setup: None,

View file

@ -441,7 +441,7 @@ mod tests {
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[vars]
repo_url = "https://github.com/org/repo"
@ -522,7 +522,7 @@ language = "python"
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"
@ -538,7 +538,7 @@ devcontainer = true
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"
@ -553,7 +553,7 @@ provider = "daytona"
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"
@ -569,7 +569,7 @@ provider = "daytona"
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"
@ -614,7 +614,7 @@ dockerfile = "FROM rust:1.85-slim-bookworm\nRUN apt-get update"
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "daytona"
@ -633,12 +633,12 @@ auto_stop_interval = 30
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
"#;
let config = parse_run_config(toml).unwrap();
assert_eq!(config.version, 1);
assert_eq!(config.goal.as_deref(), Some("Run tests"));
assert_eq!(config.graph, "workflow.dot");
assert_eq!(config.graph, "workflow.fabro");
assert!(config.directory.is_none());
assert!(config.llm.is_none());
assert!(config.setup.is_none());
@ -648,7 +648,7 @@ graph = "workflow.dot"
fn parse_toml_without_goal() {
let toml = r#"
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
"#;
let config = parse_run_config(toml).unwrap();
assert!(config.goal.is_none());
@ -659,7 +659,7 @@ graph = "workflow.dot"
let toml = r#"
version = 1
goal = "Full workflow"
graph = "workflow.dot"
graph = "workflow.fabro"
directory = "/tmp/repo"
[llm]
@ -688,7 +688,7 @@ timeout_ms = 60000
let toml = r#"
version = 2
goal = "x"
graph = "p.dot"
graph = "p.fabro"
"#;
let err = parse_run_config(toml).unwrap_err();
assert!(
@ -700,22 +700,22 @@ graph = "p.dot"
#[test]
fn graph_path_resolved_relative_to_toml() {
let toml_path = Path::new("/tmp/sub/task.toml");
let resolved = resolve_graph_path(toml_path, "p.dot");
assert_eq!(resolved, PathBuf::from("/tmp/sub/p.dot"));
let resolved = resolve_graph_path(toml_path, "p.fabro");
assert_eq!(resolved, PathBuf::from("/tmp/sub/p.fabro"));
}
#[test]
fn graph_path_absolute_unchanged() {
let toml_path = Path::new("/tmp/sub/task.toml");
let resolved = resolve_graph_path(toml_path, "/other/workflow.dot");
assert_eq!(resolved, PathBuf::from("/other/workflow.dot"));
let resolved = resolve_graph_path(toml_path, "/other/workflow.fabro");
assert_eq!(resolved, PathBuf::from("/other/workflow.fabro"));
}
#[test]
fn goal_is_optional() {
let no_goal = r#"
version = 1
graph = "p.dot"
graph = "p.fabro"
"#;
let config = parse_run_config(no_goal).unwrap();
assert!(config.goal.is_none());
@ -735,7 +735,7 @@ goal = "x"
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox.daytona.snapshot]
name = "my-snapshot"
@ -767,7 +767,7 @@ dockerfile = { path = "./Dockerfile" }
r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox.daytona.snapshot]
name = "my-snapshot"
@ -795,7 +795,7 @@ dockerfile = { path = "Dockerfile" }
r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox.daytona.snapshot]
name = "my-snapshot"
@ -867,7 +867,7 @@ key = "value"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#,
)
.unwrap();
@ -890,7 +890,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[llm]
model = "task-model"
@ -916,7 +916,7 @@ model = "task-model"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[vars]
task_key = "task_val"
@ -945,7 +945,7 @@ shared = "from_task"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#,
)
.unwrap();
@ -964,7 +964,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[llm]
model = "haiku"
@ -991,7 +991,7 @@ model = "haiku"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[setup]
commands = ["make test"]
@ -1016,7 +1016,7 @@ commands = ["make test"]
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "docker"
@ -1032,7 +1032,7 @@ preserve = true
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[sandbox]
provider = "docker"
@ -1047,7 +1047,7 @@ provider = "docker"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.local]
worktree_mode = "always"
@ -1062,7 +1062,7 @@ worktree_mode = "always"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "local"
@ -1077,7 +1077,7 @@ provider = "local"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.local]
"#;
@ -1092,7 +1092,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
preserve = true
@ -1123,7 +1123,7 @@ preserve = true
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "docker"
@ -1154,7 +1154,7 @@ provider = "docker"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "daytona"
@ -1191,7 +1191,7 @@ provider = "daytona"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "daytona"
@ -1231,7 +1231,7 @@ auto_stop_interval = 60
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona.labels]
project = "fabro"
@ -1272,7 +1272,7 @@ env = "from_task"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona.snapshot]
name = "task-snap"
@ -1316,7 +1316,7 @@ cpu = 2
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona]
auto_stop_interval = 60
@ -1358,7 +1358,7 @@ auto_stop_interval = 60
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "claude-opus-4-6"
@ -1380,7 +1380,7 @@ gemini = ["anthropic", "openai"]
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[llm]
model = "claude-opus-4-6"
@ -1397,7 +1397,7 @@ provider = "anthropic"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[llm]
model = "opus"
@ -1426,7 +1426,7 @@ anthropic = ["gemini"]
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[llm]
model = "opus"
@ -1489,7 +1489,7 @@ model = "opus"
let toml = r#"
version = 1
goal = "Test hooks"
graph = "test.dot"
graph = "test.fabro"
[[hooks]]
event = "stage_start"
@ -1518,7 +1518,7 @@ command = "echo done"
let toml = r#"
version = 1
goal = "No hooks"
graph = "test.dot"
graph = "test.fabro"
"#;
let cfg: WorkflowRunConfig = toml::from_str(toml).unwrap();
assert!(cfg.hooks.is_empty());
@ -1529,7 +1529,7 @@ graph = "test.dot"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona]
network = "block"
@ -1548,7 +1548,7 @@ network = "block"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona]
network = "block"
@ -1585,7 +1585,7 @@ network = "block"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.daytona]
auto_stop_interval = 60
@ -1625,7 +1625,7 @@ auto_stop_interval = 60
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[checkpoint]
exclude_globs = ["**/node_modules/**", "**/.cache/**"]
@ -1642,7 +1642,7 @@ exclude_globs = ["**/node_modules/**", "**/.cache/**"]
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#;
let config = parse_run_config(toml).unwrap();
assert!(config.checkpoint.exclude_globs.is_empty());
@ -1654,7 +1654,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[checkpoint]
exclude_globs = ["**/dist/**", "**/.cache/**"]
@ -1680,7 +1680,7 @@ exclude_globs = ["**/dist/**", "**/.cache/**"]
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#,
)
.unwrap();
@ -1700,7 +1700,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[checkpoint]
exclude_globs = ["**/dist/**"]
@ -1730,7 +1730,7 @@ exclude_globs = ["**/node_modules/**"]
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.env]
FOO = "bar"
@ -1748,7 +1748,7 @@ BAZ = "${env.HOME}"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "daytona"
@ -1800,7 +1800,7 @@ provider = "daytona"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.env]
TASK_KEY = "task_val"
@ -1838,7 +1838,7 @@ SHARED = "from_task"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox]
provider = "daytona"
@ -1874,7 +1874,7 @@ provider = "daytona"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.env]
LITERAL = "hello"
@ -1899,7 +1899,7 @@ FROM_HOST = "${env.FABRO_TEST_LOAD_ENV}"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[sandbox.env]
MISSING = "${env.FABRO_TEST_DEFINITELY_NOT_SET_67890}"
@ -1920,7 +1920,7 @@ MISSING = "${env.FABRO_TEST_DEFINITELY_NOT_SET_67890}"
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[pull_request]
enabled = true
@ -1935,7 +1935,7 @@ enabled = true
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#;
let config = parse_run_config(toml).unwrap();
assert!(config.pull_request.is_none());
@ -1947,7 +1947,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[pull_request]
enabled = true
@ -1971,7 +1971,7 @@ enabled = true
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#,
)
.unwrap();
@ -2001,7 +2001,7 @@ enabled = true
let toml = r#"
version = 1
goal = "Run tests"
graph = "workflow.dot"
graph = "workflow.fabro"
[assets]
include = ["test-results/**", "playwright-report/**", "*.trace.zip"]
@ -2018,7 +2018,7 @@ include = ["test-results/**", "playwright-report/**", "*.trace.zip"]
fn parse_toml_without_assets() {
let toml = r#"
version = 1
graph = "workflow.dot"
graph = "workflow.fabro"
"#;
let config = parse_run_config(toml).unwrap();
assert!(config.assets.is_none());
@ -2030,7 +2030,7 @@ graph = "workflow.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
"#,
)
.unwrap();
@ -2051,7 +2051,7 @@ graph = "w.dot"
r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[assets]
include = ["playwright-report/**"]
@ -2074,7 +2074,7 @@ include = ["playwright-report/**"]
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[pull_request]
enabled = true
@ -2091,7 +2091,7 @@ draft = true
let toml = r#"
version = 1
goal = "test"
graph = "w.dot"
graph = "w.fabro"
[pull_request]
enabled = true

View file

@ -46,7 +46,10 @@ mod tests {
#[test]
fn validate_valid_workflow() {
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(
tmp,
r#"digraph Simple {{
@ -74,7 +77,10 @@ mod tests {
#[test]
fn validate_invalid_syntax() {
let mut tmp = tempfile::Builder::new().suffix(".dot").tempfile().unwrap();
let mut tmp = tempfile::Builder::new()
.suffix(".fabro")
.tempfile()
.unwrap();
write!(tmp, "not a valid dot file").unwrap();
let args = ValidateArgs {
@ -101,8 +107,8 @@ mod tests {
std::fs::create_dir_all(&prompts_dir).unwrap();
std::fs::write(prompts_dir.join("plan.md"), "Plan the work carefully.").unwrap();
// Write a .dot file that uses @prompts/plan.md
let dot_path = dir.path().join("workflow.dot");
// Write a .fabro file that uses @prompts/plan.md
let dot_path = dir.path().join("workflow.fabro");
std::fs::write(
&dot_path,
r#"digraph FileRef {
@ -128,11 +134,11 @@ mod tests {
std::fs::create_dir_all(&wf_dir).unwrap();
std::fs::write(
wf_dir.join("workflow.toml"),
"version = 1\ngraph = \"workflow.dot\"\n",
"version = 1\ngraph = \"workflow.fabro\"\n",
)
.unwrap();
std::fs::write(
wf_dir.join("workflow.dot"),
wf_dir.join("workflow.fabro"),
r#"digraph Hello {
graph [goal="Test"]
start [shape=Mdiamond]
@ -154,7 +160,7 @@ mod tests {
#[test]
fn validate_missing_file() {
let args = ValidateArgs {
workflow: PathBuf::from("/tmp/nonexistent_workflow_12345.dot"),
workflow: PathBuf::from("/tmp/nonexistent_workflow_12345.fabro"),
};
let styles = Styles::new(false);
let result = validate_command(&args, &styles);

View file

@ -334,7 +334,7 @@ mod tests {
assert!(outcome
.failure_reason()
.unwrap()
.contains("No child DOT source"));
.contains("No child workflow source"));
}
#[tokio::test]

View file

@ -602,7 +602,7 @@ mod tests {
let dot = "digraph implement {\n plan [type=\"agent\"]\n code [type=\"agent\"]\n plan -> code\n}\n";
let section = format_arc_details_section(&conclusion, Some(dot));
assert!(section.contains("<code>implement.dot</code>"));
assert!(section.contains("<code>implement.fabro</code>"));
assert!(section.contains("2 nodes and 1 edge"));
assert!(section.contains("```dot"));
assert!(section.contains("digraph implement"));
@ -737,7 +737,7 @@ mod tests {
plan -> code
}"#;
let (name, nodes, edges) = parse_dot_summary(dot);
assert_eq!(name, "my_workflow.dot");
assert_eq!(name, "my_workflow.fabro");
assert_eq!(nodes, 2);
assert_eq!(edges, 1);
}
@ -745,7 +745,7 @@ mod tests {
#[test]
fn parse_dot_summary_empty() {
let (name, nodes, edges) = parse_dot_summary("");
assert_eq!(name, "workflow.dot");
assert_eq!(name, "workflow.fabro");
assert_eq!(nodes, 0);
assert_eq!(edges, 0);
}

View file

@ -997,7 +997,7 @@ impl LintRule for UnresolvedFileRefRule {
),
node_id: Some(node.id.clone()),
edge: None,
fix: Some("Check that the path is relative to the .dot file's directory and the file exists".to_string()),
fix: Some("Check that the path is relative to the workflow file's directory and the file exists".to_string()),
});
}
}
@ -1011,7 +1011,7 @@ impl LintRule for UnresolvedFileRefRule {
message: format!("Graph goal has unresolved file reference: {goal}"),
node_id: None,
edge: None,
fix: Some("Check that the path is relative to the .dot file's directory and the file exists".to_string()),
fix: Some("Check that the path is relative to the workflow file's directory and the file exists".to_string()),
});
}
}

View file

@ -1,6 +1,6 @@
version = 1
goal = "Run tests for $repo_name"
graph = "c-i.dot"
graph = "c-i.fabro"
[vars]
language = "test-language"

View file

@ -35,7 +35,7 @@ run_one() {
# Check for companion run.toml (run-<stem>.toml in same dir)
local stem
stem="$(basename "${dot%.dot}")"
stem="$(basename "${dot%.fabro}")"
local toml
toml="${dot_dir}/run-${stem}.toml"
@ -101,7 +101,7 @@ echo ""
dots=()
while IFS= read -r dot; do
dots+=("$dot")
done < <(find "$SCRIPT_DIR" -name '*.dot' | sort)
done < <(find "$SCRIPT_DIR" -name '*.fabro' | sort)
# Run with parallelism
active=0

Some files were not shown because too many files have changed in this diff Show more