mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-09-07 08:27:12 +00:00
Add run labels and arc runs list/prune CLI
- Add `labels: HashMap<String, String>` to RunConfig, written to manifest.json - Add `--label KEY=VALUE` flag to `arc run` (repeatable) - New `arc runs` command: list pipeline runs with table or --json output - New `arc runs prune` command: delete old runs with --before, --pipeline, --label, --orphans filters (dry-run by default, --yes to confirm) - 13 new tests covering scan_runs, filter_runs, prune, and manifest labels Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c860bc3e35
commit
95864ab788
8 changed files with 878 additions and 0 deletions
|
|
@ -248,6 +248,7 @@ async fn start_pipeline(
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = tokio::select! {
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ enum Command {
|
|||
Run(arc_workflows::cli::RunArgs),
|
||||
/// Validate a pipeline
|
||||
Validate(arc_workflows::cli::ValidateArgs),
|
||||
/// List and manage pipeline runs
|
||||
Runs(arc_workflows::cli::runs::RunsArgs),
|
||||
/// Start the HTTP API server
|
||||
Serve(arc_api::serve::ServeArgs),
|
||||
}
|
||||
|
|
@ -59,6 +61,7 @@ async fn main() -> Result<()> {
|
|||
Command::Agent(_) => "agent",
|
||||
Command::Run(_) => "run",
|
||||
Command::Validate(_) => "validate",
|
||||
Command::Runs(_) => "runs",
|
||||
Command::Serve(_) => "serve",
|
||||
};
|
||||
debug!(command = %command_name, "CLI command started");
|
||||
|
|
@ -78,6 +81,9 @@ async fn main() -> Result<()> {
|
|||
let styles = arc_util::terminal::Styles::detect_stderr();
|
||||
arc_workflows::cli::validate::validate_command(&args, &styles)?;
|
||||
}
|
||||
Command::Runs(args) => {
|
||||
arc_workflows::cli::runs::runs_command(args)?;
|
||||
}
|
||||
Command::Serve(args) => {
|
||||
let styles: &'static arc_util::terminal::Styles =
|
||||
Box::leak(Box::new(arc_util::terminal::Styles::detect_stderr()));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
pub mod backend;
|
||||
pub mod cli_backend;
|
||||
pub mod run;
|
||||
pub mod runs;
|
||||
pub mod task_config;
|
||||
pub mod validate;
|
||||
|
||||
|
|
@ -112,6 +113,10 @@ pub struct RunArgs {
|
|||
/// Execution environment for agent tools
|
||||
#[arg(long, value_enum)]
|
||||
pub execution_env: Option<ExecutionEnvKind>,
|
||||
|
||||
/// Attach a label to this run (repeatable, format: KEY=VALUE)
|
||||
#[arg(long = "label", value_name = "KEY=VALUE")]
|
||||
pub label: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::time::Instant;
|
||||
|
|
@ -561,6 +562,12 @@ pub async fn run_command(args: RunArgs, styles: &'static Styles) -> anyhow::Resu
|
|||
base_sha: worktree_base_sha.or(daytona_base_sha),
|
||||
run_branch: worktree_branch.or(daytona_branch),
|
||||
meta_branch,
|
||||
labels: args
|
||||
.label
|
||||
.iter()
|
||||
.filter_map(|s| s.split_once('='))
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect(),
|
||||
};
|
||||
|
||||
let run_start = Instant::now();
|
||||
|
|
@ -907,6 +914,7 @@ async fn run_from_branch(
|
|||
base_sha,
|
||||
run_branch: Some(run_branch.to_string()),
|
||||
meta_branch,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
|
||||
let run_start = Instant::now();
|
||||
|
|
|
|||
652
crates/arc-workflows/src/cli/runs.rs
Normal file
652
crates/arc-workflows/src/cli/runs.rs
Normal file
|
|
@ -0,0 +1,652 @@
|
|||
use std::collections::HashMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use anyhow::Result;
|
||||
use clap::{Args, Subcommand};
|
||||
use serde::Serialize;
|
||||
use tracing::{debug, info};
|
||||
|
||||
/// Arguments for the `arc runs` command.
|
||||
#[derive(Args)]
|
||||
pub struct RunsArgs {
|
||||
#[command(subcommand)]
|
||||
pub command: Option<RunsCommand>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum RunsCommand {
|
||||
/// List pipeline runs
|
||||
List(RunsListArgs),
|
||||
/// Delete old pipeline runs
|
||||
Prune(RunsPruneArgs),
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct RunsListArgs {
|
||||
/// Only show runs started before this date (YYYY-MM-DD prefix match)
|
||||
#[arg(long)]
|
||||
pub before: Option<String>,
|
||||
|
||||
/// Filter by pipeline name (substring match)
|
||||
#[arg(long)]
|
||||
pub pipeline: Option<String>,
|
||||
|
||||
/// Filter by label (KEY=VALUE, repeatable, AND semantics)
|
||||
#[arg(long = "label", value_name = "KEY=VALUE")]
|
||||
pub label: Vec<String>,
|
||||
|
||||
/// Include orphan directories (no manifest.json)
|
||||
#[arg(long)]
|
||||
pub orphans: bool,
|
||||
|
||||
/// Output as JSON
|
||||
#[arg(long)]
|
||||
pub json: bool,
|
||||
}
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct RunsPruneArgs {
|
||||
/// Only prune runs started before this date (YYYY-MM-DD prefix match)
|
||||
#[arg(long)]
|
||||
pub before: Option<String>,
|
||||
|
||||
/// Filter by pipeline name (substring match)
|
||||
#[arg(long)]
|
||||
pub pipeline: Option<String>,
|
||||
|
||||
/// Filter by label (KEY=VALUE, repeatable, AND semantics)
|
||||
#[arg(long = "label", value_name = "KEY=VALUE")]
|
||||
pub label: Vec<String>,
|
||||
|
||||
/// Include orphan directories (no manifest.json)
|
||||
#[arg(long)]
|
||||
pub orphans: bool,
|
||||
|
||||
/// Actually delete (default is dry-run)
|
||||
#[arg(long)]
|
||||
pub yes: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct RunInfo {
|
||||
pub run_id: String,
|
||||
pub dir_name: String,
|
||||
pub pipeline_name: String,
|
||||
pub status: String,
|
||||
pub start_time: String,
|
||||
pub labels: HashMap<String, String>,
|
||||
#[serde(skip)]
|
||||
pub path: PathBuf,
|
||||
#[serde(skip)]
|
||||
pub is_orphan: bool,
|
||||
}
|
||||
|
||||
/// Scan a logs base directory and return info about each run.
|
||||
pub fn scan_runs(base: &Path) -> Result<Vec<RunInfo>> {
|
||||
let entries = match std::fs::read_dir(base) {
|
||||
Ok(e) => e,
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => return Ok(Vec::new()),
|
||||
Err(e) => return Err(e.into()),
|
||||
};
|
||||
|
||||
let mut runs = Vec::new();
|
||||
for entry in entries {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
if !path.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let dir_name = entry
|
||||
.file_name()
|
||||
.to_string_lossy()
|
||||
.to_string();
|
||||
|
||||
debug!(dir = %dir_name, "scanning run directory");
|
||||
|
||||
let manifest_path = path.join("manifest.json");
|
||||
if manifest_path.exists() {
|
||||
let manifest_text = std::fs::read_to_string(&manifest_path)?;
|
||||
debug!(dir = %dir_name, "reading manifest");
|
||||
let manifest: serde_json::Value = serde_json::from_str(&manifest_text)?;
|
||||
|
||||
let run_id = manifest["run_id"]
|
||||
.as_str()
|
||||
.unwrap_or(&dir_name)
|
||||
.to_string();
|
||||
let pipeline_name = manifest["pipeline_name"]
|
||||
.as_str()
|
||||
.unwrap_or("unknown")
|
||||
.to_string();
|
||||
let start_time = manifest["start_time"]
|
||||
.as_str()
|
||||
.unwrap_or("")
|
||||
.to_string();
|
||||
let labels: HashMap<String, String> = manifest
|
||||
.get("labels")
|
||||
.and_then(|v| serde_json::from_value(v.clone()).ok())
|
||||
.unwrap_or_default();
|
||||
|
||||
let status = read_status(&path);
|
||||
|
||||
runs.push(RunInfo {
|
||||
run_id,
|
||||
dir_name,
|
||||
pipeline_name,
|
||||
status,
|
||||
start_time,
|
||||
labels,
|
||||
path,
|
||||
is_orphan: false,
|
||||
});
|
||||
} else {
|
||||
// Orphan directory — no manifest.json
|
||||
let mtime = entry
|
||||
.metadata()
|
||||
.ok()
|
||||
.and_then(|m| m.modified().ok())
|
||||
.map(|t| {
|
||||
let dt: chrono::DateTime<chrono::Utc> = t.into();
|
||||
dt.to_rfc3339()
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
runs.push(RunInfo {
|
||||
run_id: dir_name.clone(),
|
||||
dir_name,
|
||||
pipeline_name: "[no manifest]".to_string(),
|
||||
status: "unknown".to_string(),
|
||||
start_time: mtime,
|
||||
labels: HashMap::new(),
|
||||
path,
|
||||
is_orphan: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Sort by start_time descending (newest first)
|
||||
runs.sort_by(|a, b| b.start_time.cmp(&a.start_time));
|
||||
Ok(runs)
|
||||
}
|
||||
|
||||
fn read_status(run_dir: &Path) -> String {
|
||||
let final_path = run_dir.join("final.json");
|
||||
if final_path.exists() {
|
||||
if let Ok(text) = std::fs::read_to_string(&final_path) {
|
||||
if let Ok(val) = serde_json::from_str::<serde_json::Value>(&text) {
|
||||
if let Some(status) = val["status"].as_str() {
|
||||
return status.to_string();
|
||||
}
|
||||
}
|
||||
}
|
||||
"unknown".to_string()
|
||||
} else if run_dir.join("run.pid").exists() {
|
||||
"running".to_string()
|
||||
} else {
|
||||
"unknown".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// Filter runs by criteria. Orphans are excluded unless `include_orphans` is true.
|
||||
pub fn filter_runs(
|
||||
runs: &[RunInfo],
|
||||
before: Option<&str>,
|
||||
pipeline: Option<&str>,
|
||||
labels: &[(String, String)],
|
||||
include_orphans: bool,
|
||||
) -> Vec<RunInfo> {
|
||||
runs.iter()
|
||||
.filter(|r| {
|
||||
if r.is_orphan && !include_orphans {
|
||||
return false;
|
||||
}
|
||||
if let Some(before) = before {
|
||||
if !r.start_time.is_empty() && r.start_time.as_str() >= before {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if let Some(pat) = pipeline {
|
||||
if !r.pipeline_name.contains(pat) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (k, v) in labels {
|
||||
match r.labels.get(k) {
|
||||
Some(val) if val == v => {}
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
true
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_label_filters(label_args: &[String]) -> Vec<(String, String)> {
|
||||
label_args
|
||||
.iter()
|
||||
.filter_map(|s| s.split_once('='))
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn default_logs_base() -> PathBuf {
|
||||
dirs::home_dir()
|
||||
.expect("could not determine home directory")
|
||||
.join(".arc")
|
||||
.join("logs")
|
||||
}
|
||||
|
||||
pub fn list_command(args: &RunsListArgs) -> Result<()> {
|
||||
let base = default_logs_base();
|
||||
let runs = scan_runs(&base)?;
|
||||
let label_filters = parse_label_filters(&args.label);
|
||||
let filtered = filter_runs(
|
||||
&runs,
|
||||
args.before.as_deref(),
|
||||
args.pipeline.as_deref(),
|
||||
&label_filters,
|
||||
args.orphans,
|
||||
);
|
||||
|
||||
if args.json {
|
||||
println!("{}", serde_json::to_string_pretty(&filtered)?);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if filtered.is_empty() {
|
||||
eprintln!("No runs found.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Print table header
|
||||
let header = format!(
|
||||
"{:<30} {:<25} {:<10} {:<25} LABELS",
|
||||
"RUN ID", "PIPELINE", "STATUS", "STARTED"
|
||||
);
|
||||
println!("{header}");
|
||||
println!("{}", "-".repeat(100));
|
||||
|
||||
for run in &filtered {
|
||||
let labels_str = run
|
||||
.labels
|
||||
.iter()
|
||||
.map(|(k, v)| format!("{k}={v}"))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", ");
|
||||
let run_id_display = if run.run_id.len() > 28 {
|
||||
format!("{}...", &run.run_id[..25])
|
||||
} else {
|
||||
run.run_id.clone()
|
||||
};
|
||||
let start_display = if run.start_time.len() > 23 {
|
||||
run.start_time[..23].to_string()
|
||||
} else {
|
||||
run.start_time.clone()
|
||||
};
|
||||
println!(
|
||||
"{:<30} {:<25} {:<10} {:<25} {}",
|
||||
run_id_display, run.pipeline_name, run.status, start_display, labels_str
|
||||
);
|
||||
}
|
||||
eprintln!("\n{} run(s) listed.", filtered.len());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn prune_command(args: &RunsPruneArgs) -> Result<()> {
|
||||
let base = default_logs_base();
|
||||
prune_from(args, &base)
|
||||
}
|
||||
|
||||
pub fn prune_from(args: &RunsPruneArgs, base: &Path) -> Result<()> {
|
||||
let runs = scan_runs(base)?;
|
||||
let label_filters = parse_label_filters(&args.label);
|
||||
let filtered = filter_runs(
|
||||
&runs,
|
||||
args.before.as_deref(),
|
||||
args.pipeline.as_deref(),
|
||||
&label_filters,
|
||||
args.orphans,
|
||||
);
|
||||
|
||||
if filtered.is_empty() {
|
||||
eprintln!("No matching runs to prune.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if args.yes {
|
||||
for run in &filtered {
|
||||
info!(run_id = %run.run_id, path = %run.path.display(), "deleting run");
|
||||
std::fs::remove_dir_all(&run.path)?;
|
||||
}
|
||||
eprintln!("{} run(s) deleted.", filtered.len());
|
||||
} else {
|
||||
for run in &filtered {
|
||||
debug!(run_id = %run.run_id, "would delete run (dry-run)");
|
||||
println!(
|
||||
"would delete: {} ({})",
|
||||
run.dir_name, run.pipeline_name
|
||||
);
|
||||
}
|
||||
eprintln!(
|
||||
"\n{} run(s) would be deleted. Pass --yes to confirm.",
|
||||
filtered.len()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn runs_command(args: RunsArgs) -> Result<()> {
|
||||
match args.command {
|
||||
None => list_command(&RunsListArgs {
|
||||
before: None,
|
||||
pipeline: None,
|
||||
label: Vec::new(),
|
||||
orphans: false,
|
||||
json: false,
|
||||
}),
|
||||
Some(RunsCommand::List(args)) => list_command(&args),
|
||||
Some(RunsCommand::Prune(args)) => prune_command(&args),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use std::fs;
|
||||
|
||||
fn make_run_dir(
|
||||
base: &Path,
|
||||
dir_name: &str,
|
||||
manifest: Option<serde_json::Value>,
|
||||
final_json: Option<serde_json::Value>,
|
||||
pid_file: bool,
|
||||
) -> PathBuf {
|
||||
let dir = base.join(dir_name);
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
if let Some(m) = manifest {
|
||||
fs::write(dir.join("manifest.json"), serde_json::to_string_pretty(&m).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
if let Some(f) = final_json {
|
||||
fs::write(dir.join("final.json"), serde_json::to_string_pretty(&f).unwrap())
|
||||
.unwrap();
|
||||
}
|
||||
if pid_file {
|
||||
fs::write(dir.join("run.pid"), "12345").unwrap();
|
||||
}
|
||||
dir
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_runs_reads_manifest_and_final() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path();
|
||||
|
||||
make_run_dir(
|
||||
base,
|
||||
"arc-run-20260101-120000",
|
||||
Some(serde_json::json!({
|
||||
"run_id": "abc123",
|
||||
"pipeline_name": "my-pipeline",
|
||||
"start_time": "2026-01-01T12:00:00Z",
|
||||
"labels": { "env": "prod" }
|
||||
})),
|
||||
Some(serde_json::json!({ "status": "success" })),
|
||||
false,
|
||||
);
|
||||
|
||||
make_run_dir(base, "arc-run-orphan", None, None, false);
|
||||
|
||||
let runs = scan_runs(base).unwrap();
|
||||
assert_eq!(runs.len(), 2);
|
||||
|
||||
let completed = runs.iter().find(|r| r.run_id == "abc123").unwrap();
|
||||
assert_eq!(completed.pipeline_name, "my-pipeline");
|
||||
assert_eq!(completed.status, "success");
|
||||
assert_eq!(completed.labels.get("env").unwrap(), "prod");
|
||||
assert!(!completed.is_orphan);
|
||||
|
||||
let orphan = runs.iter().find(|r| r.is_orphan).unwrap();
|
||||
assert_eq!(orphan.pipeline_name, "[no manifest]");
|
||||
assert_eq!(orphan.status, "unknown");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_runs_detects_running_status() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path();
|
||||
|
||||
make_run_dir(
|
||||
base,
|
||||
"arc-run-running",
|
||||
Some(serde_json::json!({
|
||||
"run_id": "running-1",
|
||||
"pipeline_name": "pipeline-a",
|
||||
"start_time": "2026-01-15T10:00:00Z"
|
||||
})),
|
||||
None,
|
||||
true,
|
||||
);
|
||||
|
||||
let runs = scan_runs(base).unwrap();
|
||||
assert_eq!(runs.len(), 1);
|
||||
assert_eq!(runs[0].status, "running");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_runs_empty_dir() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let runs = scan_runs(tmp.path()).unwrap();
|
||||
assert!(runs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scan_runs_missing_dir() {
|
||||
let runs = scan_runs(Path::new("/tmp/nonexistent-arc-test-dir")).unwrap();
|
||||
assert!(runs.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_runs_before() {
|
||||
let runs = vec![
|
||||
RunInfo {
|
||||
run_id: "old".into(),
|
||||
dir_name: "d1".into(),
|
||||
pipeline_name: "p".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2025-06-01T00:00:00Z".into(),
|
||||
labels: HashMap::new(),
|
||||
path: PathBuf::from("/tmp/d1"),
|
||||
is_orphan: false,
|
||||
},
|
||||
RunInfo {
|
||||
run_id: "new".into(),
|
||||
dir_name: "d2".into(),
|
||||
pipeline_name: "p".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2026-03-01T00:00:00Z".into(),
|
||||
labels: HashMap::new(),
|
||||
path: PathBuf::from("/tmp/d2"),
|
||||
is_orphan: false,
|
||||
},
|
||||
];
|
||||
let filtered = filter_runs(&runs, Some("2026-01-01"), None, &[], false);
|
||||
assert_eq!(filtered.len(), 1);
|
||||
assert_eq!(filtered[0].run_id, "old");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_runs_pipeline() {
|
||||
let runs = vec![
|
||||
RunInfo {
|
||||
run_id: "a".into(),
|
||||
dir_name: "d1".into(),
|
||||
pipeline_name: "deploy-prod".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2026-01-01T00:00:00Z".into(),
|
||||
labels: HashMap::new(),
|
||||
path: PathBuf::from("/tmp/d1"),
|
||||
is_orphan: false,
|
||||
},
|
||||
RunInfo {
|
||||
run_id: "b".into(),
|
||||
dir_name: "d2".into(),
|
||||
pipeline_name: "test-suite".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2026-01-01T00:00:00Z".into(),
|
||||
labels: HashMap::new(),
|
||||
path: PathBuf::from("/tmp/d2"),
|
||||
is_orphan: false,
|
||||
},
|
||||
];
|
||||
let filtered = filter_runs(&runs, None, Some("deploy"), &[], false);
|
||||
assert_eq!(filtered.len(), 1);
|
||||
assert_eq!(filtered[0].run_id, "a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_runs_labels() {
|
||||
let runs = vec![
|
||||
RunInfo {
|
||||
run_id: "a".into(),
|
||||
dir_name: "d1".into(),
|
||||
pipeline_name: "p".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2026-01-01T00:00:00Z".into(),
|
||||
labels: HashMap::from([("env".into(), "prod".into())]),
|
||||
path: PathBuf::from("/tmp/d1"),
|
||||
is_orphan: false,
|
||||
},
|
||||
RunInfo {
|
||||
run_id: "b".into(),
|
||||
dir_name: "d2".into(),
|
||||
pipeline_name: "p".into(),
|
||||
status: "success".into(),
|
||||
start_time: "2026-01-01T00:00:00Z".into(),
|
||||
labels: HashMap::from([("env".into(), "staging".into())]),
|
||||
path: PathBuf::from("/tmp/d2"),
|
||||
is_orphan: false,
|
||||
},
|
||||
];
|
||||
let filtered = filter_runs(
|
||||
&runs,
|
||||
None,
|
||||
None,
|
||||
&[("env".to_string(), "prod".to_string())],
|
||||
false,
|
||||
);
|
||||
assert_eq!(filtered.len(), 1);
|
||||
assert_eq!(filtered[0].run_id, "a");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_runs_orphans_excluded_by_default() {
|
||||
let runs = vec![RunInfo {
|
||||
run_id: "orphan".into(),
|
||||
dir_name: "d1".into(),
|
||||
pipeline_name: "[no manifest]".into(),
|
||||
status: "unknown".into(),
|
||||
start_time: "".into(),
|
||||
labels: HashMap::new(),
|
||||
path: PathBuf::from("/tmp/d1"),
|
||||
is_orphan: true,
|
||||
}];
|
||||
let filtered = filter_runs(&runs, None, None, &[], false);
|
||||
assert!(filtered.is_empty());
|
||||
|
||||
let filtered = filter_runs(&runs, None, None, &[], true);
|
||||
assert_eq!(filtered.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prune_dry_run_preserves_dirs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path();
|
||||
|
||||
let dir = make_run_dir(
|
||||
base,
|
||||
"arc-run-20250101-120000",
|
||||
Some(serde_json::json!({
|
||||
"run_id": "to-prune",
|
||||
"pipeline_name": "old-pipeline",
|
||||
"start_time": "2025-01-01T12:00:00Z"
|
||||
})),
|
||||
Some(serde_json::json!({ "status": "success" })),
|
||||
false,
|
||||
);
|
||||
|
||||
let args = RunsPruneArgs {
|
||||
before: Some("2026-01-01".into()),
|
||||
pipeline: None,
|
||||
label: Vec::new(),
|
||||
orphans: false,
|
||||
yes: false,
|
||||
};
|
||||
|
||||
prune_from(&args, base).unwrap();
|
||||
assert!(dir.exists(), "dry-run should preserve directory");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prune_with_yes_deletes_dirs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path();
|
||||
|
||||
let dir = make_run_dir(
|
||||
base,
|
||||
"arc-run-20250101-120000",
|
||||
Some(serde_json::json!({
|
||||
"run_id": "to-prune",
|
||||
"pipeline_name": "old-pipeline",
|
||||
"start_time": "2025-01-01T12:00:00Z"
|
||||
})),
|
||||
Some(serde_json::json!({ "status": "success" })),
|
||||
false,
|
||||
);
|
||||
|
||||
// Also add a run that should NOT be pruned (too new)
|
||||
let keep_dir = make_run_dir(
|
||||
base,
|
||||
"arc-run-20260301-120000",
|
||||
Some(serde_json::json!({
|
||||
"run_id": "keep-this",
|
||||
"pipeline_name": "new-pipeline",
|
||||
"start_time": "2026-03-01T12:00:00Z"
|
||||
})),
|
||||
Some(serde_json::json!({ "status": "success" })),
|
||||
false,
|
||||
);
|
||||
|
||||
let args = RunsPruneArgs {
|
||||
before: Some("2026-01-01".into()),
|
||||
pipeline: None,
|
||||
label: Vec::new(),
|
||||
orphans: false,
|
||||
yes: true,
|
||||
};
|
||||
|
||||
prune_from(&args, base).unwrap();
|
||||
assert!(!dir.exists(), "--yes should delete matching directory");
|
||||
assert!(keep_dir.exists(), "non-matching directory should be preserved");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn prune_orphans_with_yes() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path();
|
||||
|
||||
let orphan_dir = make_run_dir(base, "orphan-dir", None, None, false);
|
||||
|
||||
let args = RunsPruneArgs {
|
||||
before: None,
|
||||
pipeline: None,
|
||||
label: Vec::new(),
|
||||
orphans: true,
|
||||
yes: true,
|
||||
};
|
||||
|
||||
prune_from(&args, base).unwrap();
|
||||
assert!(!orphan_dir.exists(), "orphan directory should be deleted");
|
||||
}
|
||||
}
|
||||
|
|
@ -304,6 +304,9 @@ fn write_manifest(logs_root: &Path, graph: &Graph, config: &RunConfig) -> serde_
|
|||
if let Some(ref base) = config.base_sha {
|
||||
manifest["base_sha"] = serde_json::Value::String(base.clone());
|
||||
}
|
||||
if !config.labels.is_empty() {
|
||||
manifest["labels"] = serde_json::to_value(&config.labels).unwrap_or_default();
|
||||
}
|
||||
if let Ok(json) = serde_json::to_string_pretty(&manifest) {
|
||||
let _ = std::fs::create_dir_all(logs_root);
|
||||
let _ = std::fs::write(logs_root.join("manifest.json"), json);
|
||||
|
|
@ -736,6 +739,8 @@ pub struct RunConfig {
|
|||
pub run_branch: Option<String>,
|
||||
/// Metadata branch name for git-native checkpoint storage (e.g. `refs/arc/{run_id}`).
|
||||
pub meta_branch: Option<String>,
|
||||
/// User-defined key-value labels for this run.
|
||||
pub labels: HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// The pipeline execution engine.
|
||||
|
|
@ -2306,6 +2311,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2326,6 +2332,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
let checkpoint_path = dir.path().join("checkpoint.json");
|
||||
|
|
@ -2354,6 +2361,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2378,6 +2386,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -2398,6 +2407,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2431,6 +2441,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2488,6 +2499,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2565,6 +2577,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2579,6 +2592,56 @@ mod tests {
|
|||
assert!(manifest["edge_count"].is_number());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn manifest_includes_labels_when_present() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let g = simple_graph();
|
||||
let engine =
|
||||
PipelineEngine::new(make_registry(), Arc::new(EventEmitter::new()), local_env());
|
||||
let config = RunConfig {
|
||||
logs_root: dir.path().to_path_buf(),
|
||||
cancel_token: None,
|
||||
dry_run: false,
|
||||
run_id: "labels-run".into(),
|
||||
git_checkpoint: None,
|
||||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::from([("env".into(), "test".into())]),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
let manifest: serde_json::Value =
|
||||
serde_json::from_str(&std::fs::read_to_string(dir.path().join("manifest.json")).unwrap())
|
||||
.unwrap();
|
||||
assert_eq!(manifest["labels"]["env"], "test");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn manifest_omits_labels_when_empty() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let g = simple_graph();
|
||||
let engine =
|
||||
PipelineEngine::new(make_registry(), Arc::new(EventEmitter::new()), local_env());
|
||||
let config = RunConfig {
|
||||
logs_root: dir.path().to_path_buf(),
|
||||
cancel_token: None,
|
||||
dry_run: false,
|
||||
run_id: "no-labels-run".into(),
|
||||
git_checkpoint: None,
|
||||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
let manifest: serde_json::Value =
|
||||
serde_json::from_str(&std::fs::read_to_string(dir.path().join("manifest.json")).unwrap())
|
||||
.unwrap();
|
||||
assert!(manifest.get("labels").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn engine_writes_node_status_json() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
|
|
@ -2594,6 +2657,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2620,6 +2684,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2774,6 +2839,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2812,6 +2878,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2868,6 +2935,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -2926,6 +2994,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
|
||||
|
|
@ -2988,6 +3057,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_ok());
|
||||
|
|
@ -3039,6 +3109,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -3091,6 +3162,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
@ -3150,6 +3222,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
// Give spawned inform tasks time to complete
|
||||
|
|
@ -3184,6 +3257,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
engine.run(&g, &config).await.unwrap();
|
||||
// Give spawned inform tasks time to complete
|
||||
|
|
@ -3219,6 +3293,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -3242,6 +3317,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3264,6 +3340,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -3299,6 +3376,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
|
||||
// Set cancel after a short delay (while the slow handler is running)
|
||||
|
|
@ -3371,6 +3449,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3396,6 +3475,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3423,6 +3503,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3511,6 +3592,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
|
||||
// The engine returns Err because the Fail outcome has no outgoing fail edge,
|
||||
|
|
@ -3735,6 +3817,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3765,6 +3848,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3802,6 +3886,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3879,6 +3964,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -3969,6 +4055,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let result = engine.run(&g, &config).await;
|
||||
assert!(result.is_err());
|
||||
|
|
@ -4034,6 +4121,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -4088,6 +4176,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&g, &config).await.unwrap();
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -4141,6 +4230,7 @@ mod tests {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: HashMap::new(),
|
||||
};
|
||||
let _outcome = engine.run(&g, &config).await.unwrap();
|
||||
|
||||
|
|
|
|||
|
|
@ -284,6 +284,7 @@ async fn daytona_pipeline_artifact_offload_and_sync() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -474,6 +475,7 @@ async fn daytona_git_checkpoint_remote_emits_events() {
|
|||
base_sha: Some(base_sha),
|
||||
run_branch: Some(branch_name),
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -653,6 +655,7 @@ async fn daytona_parallel_git_branching_e2e() {
|
|||
base_sha: Some(base_sha),
|
||||
run_branch: Some(branch_name),
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -977,6 +980,7 @@ async fn daytona_git_checkpoint_with_shadow_branch() {
|
|||
base_sha: Some(base_sha),
|
||||
run_branch: Some(branch_name),
|
||||
meta_branch: Some(meta_branch),
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
|
|||
|
|
@ -198,6 +198,7 @@ async fn end_to_end_linear_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -334,6 +335,7 @@ async fn end_to_end_branching_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -453,6 +455,7 @@ async fn end_to_end_human_gate_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -558,6 +561,7 @@ async fn goal_gate_routes_to_retry_target_on_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -676,6 +680,7 @@ async fn goal_gate_routes_to_retry_target_when_present() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -981,6 +986,7 @@ async fn retry_on_failure_then_succeed() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -1053,6 +1059,7 @@ async fn pipeline_with_many_nodes() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -1379,6 +1386,7 @@ async fn smoke_test_with_mock_codergen_backend() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -1477,6 +1485,7 @@ async fn end_to_end_parallel_fan_out_fan_in() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -1586,6 +1595,7 @@ async fn resume_from_checkpoint_completes_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -1681,6 +1691,7 @@ async fn resume_from_checkpoint_preserves_goal_gate_outcomes() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
// This should succeed because goal gate for gated_work is satisfied
|
||||
|
|
@ -1721,6 +1732,7 @@ async fn graph_goal_in_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -1753,6 +1765,7 @@ async fn event_streaming_lifecycle() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -1829,6 +1842,7 @@ async fn context_flow_between_stages() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -1878,6 +1892,7 @@ async fn tool_handler_e2e() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -1946,6 +1961,7 @@ async fn auto_approve_interviewer_e2e() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -1979,6 +1995,7 @@ async fn codergen_without_backend_simulated() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2080,6 +2097,7 @@ async fn branching_loop_back_on_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2161,6 +2179,7 @@ async fn human_gate_loops_back() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2215,6 +2234,7 @@ async fn scenario_ship_a_feature() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2297,6 +2317,7 @@ async fn scenario_parallel_expert_review() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2373,6 +2394,7 @@ async fn scenario_node_retries_on_retry_status() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2431,6 +2453,7 @@ async fn scenario_loop_restart_resets_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2495,6 +2518,7 @@ async fn scenario_bug_triage_router() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2549,6 +2573,7 @@ async fn scenario_crash_recovery() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine
|
||||
.run_from_checkpoint(&graph, &config, &checkpoint)
|
||||
|
|
@ -2633,6 +2658,7 @@ async fn manager_loop_stop_condition_satisfied_e2e() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2685,6 +2711,7 @@ async fn manager_loop_max_cycles_exceeded_e2e() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2818,6 +2845,7 @@ async fn conditional_branching_success_fail_paths() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -2867,6 +2895,7 @@ async fn edge_selection_condition_match_wins_over_weight() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2910,6 +2939,7 @@ async fn edge_selection_weight_breaks_ties() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2945,6 +2975,7 @@ async fn edge_selection_lexical_tiebreak() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -2999,6 +3030,7 @@ async fn context_updates_visible_across_nodes() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3039,6 +3071,7 @@ async fn stylesheet_applies_model_override() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -3091,6 +3124,7 @@ async fn custom_handler_registration_and_execution() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3158,6 +3192,7 @@ async fn integration_smoke_plan_implement_review_done() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
let outcome = engine.run(&graph, &config).await.expect("run");
|
||||
assert_eq!(outcome.status, StageStatus::Success);
|
||||
|
|
@ -3230,6 +3265,7 @@ async fn sub_pipeline_e2e_through_engine() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -3369,6 +3405,7 @@ async fn manager_loop_with_child_observer_e2e() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -3502,6 +3539,7 @@ async fn graph_merge_e2e_through_engine() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -3649,6 +3687,7 @@ async fn fidelity_default_is_compact() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3702,6 +3741,7 @@ async fn fidelity_graph_default_applied() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3751,6 +3791,7 @@ async fn fidelity_node_overrides_graph_default() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3806,6 +3847,7 @@ async fn fidelity_edge_overrides_node_and_graph() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3851,6 +3893,7 @@ async fn fidelity_full_produces_empty_preamble() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3906,6 +3949,7 @@ async fn fidelity_truncate_preamble_minimal() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -3974,6 +4018,7 @@ async fn fidelity_summary_low_mode() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4037,6 +4082,7 @@ async fn fidelity_summary_medium_mode() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4100,6 +4146,7 @@ async fn fidelity_summary_high_mode() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4156,6 +4203,7 @@ async fn fidelity_full_sets_thread_id_in_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4223,6 +4271,7 @@ async fn fidelity_full_nodes_share_thread_id() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4299,6 +4348,7 @@ async fn fidelity_resume_degrades_full_to_summary_high() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine
|
||||
.run_from_checkpoint(&graph, &config, &checkpoint)
|
||||
|
|
@ -4391,6 +4441,7 @@ async fn fidelity_resume_degrade_only_affects_first_hop() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine
|
||||
.run_from_checkpoint(&graph, &config, &checkpoint)
|
||||
|
|
@ -4470,6 +4521,7 @@ async fn fidelity_resume_no_degrade_when_not_full() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine
|
||||
.run_from_checkpoint(&graph, &config, &checkpoint)
|
||||
|
|
@ -4508,6 +4560,7 @@ async fn fidelity_stored_in_checkpoint_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4590,6 +4643,7 @@ async fn fidelity_precedence_multi_node_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4654,6 +4708,7 @@ async fn fidelity_compact_preamble_includes_completed_stages_and_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4725,6 +4780,7 @@ async fn fidelity_summary_low_excludes_context_values_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine_low
|
||||
.run(&graph_low, &config_low)
|
||||
|
|
@ -4788,6 +4844,7 @@ async fn fidelity_summary_low_excludes_context_values_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine_med
|
||||
.run(&graph_med, &config_med)
|
||||
|
|
@ -4855,6 +4912,7 @@ async fn fidelity_thread_id_fallback_to_previous_node_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4905,6 +4963,7 @@ async fn fidelity_thread_id_from_node_class_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -4958,6 +5017,7 @@ async fn fidelity_edge_thread_id_override_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -5012,6 +5072,7 @@ async fn fidelity_full_without_explicit_thread_id_uses_previous_node() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -5076,6 +5137,7 @@ async fn fidelity_from_parsed_dot_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -5120,6 +5182,7 @@ async fn fidelity_checkpoint_roundtrip_preserves_fidelity() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -5186,6 +5249,7 @@ async fn fidelity_node_thread_id_overrides_edge_thread_id_in_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine.run(&graph, &config).await.expect("run");
|
||||
|
||||
|
|
@ -5268,6 +5332,7 @@ async fn fidelity_resume_preserves_context_values_across_checkpoint() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
engine
|
||||
.run_from_checkpoint(&graph, &config, &checkpoint)
|
||||
|
|
@ -5458,6 +5523,7 @@ mod real_llm {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = tokio::time::timeout(
|
||||
|
|
@ -5569,6 +5635,7 @@ mod real_llm {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = tokio::time::timeout(
|
||||
|
|
@ -5707,6 +5774,7 @@ mod real_llm {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = tokio::time::timeout(
|
||||
|
|
@ -5813,6 +5881,7 @@ mod real_llm {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = tokio::time::timeout(
|
||||
|
|
@ -5908,6 +5977,7 @@ async fn human_gate_freeform_only_routes_text() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6037,6 +6107,7 @@ async fn human_gate_freeform_with_fixed_choice_match() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6151,6 +6222,7 @@ async fn human_gate_freeform_fallback_on_unmatched_text() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6278,6 +6350,7 @@ async fn human_gate_freeform_sets_allow_freeform_on_question() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6385,6 +6458,7 @@ async fn human_gate_without_freeform_sets_allow_freeform_false() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6626,6 +6700,7 @@ async fn tool_hooks_pre_success_allows_pipeline_to_proceed() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6674,6 +6749,7 @@ async fn tool_hooks_pre_failure_skips_tool_call() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
engine
|
||||
|
|
@ -6725,6 +6801,7 @@ async fn tool_hooks_post_success_does_not_affect_outcome() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6768,6 +6845,7 @@ async fn tool_hooks_post_failure_does_not_block_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6813,6 +6891,7 @@ async fn tool_hooks_graph_level_applies_to_all_nodes() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -6869,6 +6948,7 @@ async fn tool_hooks_node_level_overrides_graph_level() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let _outcome = engine
|
||||
|
|
@ -6932,6 +7012,7 @@ async fn tool_hooks_pre_receives_node_id_env_var() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
engine
|
||||
|
|
@ -7033,6 +7114,7 @@ async fn arc_e2e_with_real_llm() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -7158,6 +7240,7 @@ async fn run_fidelity_prompt_pipeline(fidelity: &str) -> String {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
engine
|
||||
|
|
@ -7354,6 +7437,7 @@ async fn large_context_values_are_offloaded_to_artifact_store() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -7553,6 +7637,7 @@ async fn artifact_pointers_rewritten_for_remote_execution_env() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -7680,6 +7765,7 @@ async fn node_dir_uses_visit_count_on_revisit() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -8498,6 +8584,7 @@ async fn full_pipeline_with_cli_backend_node() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -8624,6 +8711,7 @@ async fn stylesheet_backend_property_routes_to_cli() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine
|
||||
|
|
@ -8891,6 +8979,7 @@ async fn git_checkpoint_host_emits_events_and_diff_patch() {
|
|||
base_sha: Some(base_sha.clone()),
|
||||
run_branch: Some("arc/run/test-docker".to_string()),
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
// 5. Run pipeline
|
||||
|
|
@ -9071,6 +9160,7 @@ async fn git_checkpoint_host_writes_shadow_branch() {
|
|||
base_sha: Some(base_sha),
|
||||
run_branch: Some(format!("arc/run/{run_id}")),
|
||||
meta_branch: Some(meta_branch),
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
// 5. Run pipeline
|
||||
|
|
@ -9263,6 +9353,7 @@ async fn parallel_git_branching_host_e2e() {
|
|||
base_sha: Some(base_sha.clone()),
|
||||
run_branch: Some(run_branch.clone()),
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
// 5. Run pipeline
|
||||
|
|
@ -9782,6 +9873,7 @@ async fn e2e_circuit_breaker_deterministic_self_loop() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -9826,6 +9918,7 @@ async fn e2e_circuit_breaker_custom_limit() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -9863,6 +9956,7 @@ async fn e2e_circuit_breaker_ignores_transient_failures() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -9907,6 +10001,7 @@ async fn e2e_circuit_breaker_different_reasons_separate_counters() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -9944,6 +10039,7 @@ async fn e2e_circuit_breaker_loop_restart() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10003,6 +10099,7 @@ async fn e2e_failure_signature_persisted_in_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine.run(&graph, &config).await.unwrap();
|
||||
|
|
@ -10064,6 +10161,7 @@ async fn e2e_failure_signature_hint_overrides_reason_in_context() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let _outcome = engine.run(&graph, &config).await.unwrap();
|
||||
|
|
@ -10117,6 +10215,7 @@ async fn e2e_signature_maps_persist_in_checkpoint() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine.run(&graph, &config).await.unwrap();
|
||||
|
|
@ -10240,6 +10339,7 @@ async fn e2e_circuit_breaker_emits_events_before_abort() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10304,6 +10404,7 @@ async fn e2e_circuit_breaker_does_not_fire_below_limit() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine.run(&graph, &config).await.unwrap();
|
||||
|
|
@ -10397,6 +10498,7 @@ async fn e2e_circuit_breaker_multi_stage_impl_verify_cycle() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10490,6 +10592,7 @@ async fn e2e_loop_restart_blocked_for_deterministic_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10524,6 +10627,7 @@ async fn e2e_loop_restart_blocked_for_structural_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10558,6 +10662,7 @@ async fn e2e_loop_restart_blocked_for_budget_exhausted_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10592,6 +10697,7 @@ async fn e2e_loop_restart_blocked_for_canceled_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10626,6 +10732,7 @@ async fn e2e_loop_restart_blocked_for_compilation_loop_failure() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10661,6 +10768,7 @@ async fn e2e_loop_restart_allowed_for_transient_infra() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10765,6 +10873,7 @@ async fn e2e_stall_watchdog_triggers_from_dot_parsed_pipeline() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let result = engine.run(&graph, &config).await;
|
||||
|
|
@ -10818,6 +10927,7 @@ async fn e2e_stall_watchdog_kept_alive_by_handler_events() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine.run(&graph, &config).await.expect("pipeline should succeed");
|
||||
|
|
@ -10854,6 +10964,7 @@ async fn e2e_stall_watchdog_disabled_with_zero_timeout() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let outcome = engine.run(&graph, &config).await.expect("pipeline should succeed");
|
||||
|
|
@ -10913,6 +11024,7 @@ async fn e2e_stall_watchdog_with_explicit_timeout_override() {
|
|||
base_sha: None,
|
||||
run_branch: None,
|
||||
meta_branch: None,
|
||||
labels: std::collections::HashMap::new(),
|
||||
};
|
||||
|
||||
let start = std::time::Instant::now();
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue