Stabilize merged workspace validation

This commit is contained in:
Bryan Helmkamp 2026-03-30 18:57:06 -04:00
parent 0313a1f4ee
commit d064b6c510
No known key found for this signature in database
9 changed files with 122 additions and 93 deletions

View file

@ -42,6 +42,10 @@ slow-timeout = { period = "5s", terminate-after = 3 }
filter = "package(fabro-agent)"
slow-timeout = { period = "5s", terminate-after = 3 }
[[profile.default.overrides]]
filter = "package(fabro-cli)"
slow-timeout = { period = "5s", terminate-after = 3 }
[[profile.default.overrides]]
filter = "package(fabro-llm)"
slow-timeout = { period = "5s", terminate-after = 3 }
@ -50,6 +54,10 @@ slow-timeout = { period = "5s", terminate-after = 3 }
filter = "package(fabro-server)"
slow-timeout = { period = "5s", terminate-after = 3 }
[[profile.default.overrides]]
filter = "package(fabro-tracker)"
slow-timeout = { period = "5s", terminate-after = 3 }
[profile.e2e]
# E2E (ignored) tests: flag SLOW after 10s, hard-kill after 30s
slow-timeout = { period = "10s", terminate-after = 3 }

View file

@ -496,10 +496,8 @@ pub(crate) fn format_event_pretty(line: &str, styles: &Styles) -> Option<String>
duration,
);
if token_total > 0 {
line.push_str(&format!(
" {}",
styles.dim.apply_to(format_tokens(token_total))
));
line.push_str(" ");
line.push_str(&styles.dim.apply_to(format_tokens(token_total)).to_string());
}
Some(line)
}

View file

@ -46,6 +46,10 @@ fn logs_completed_run_outputs_raw_ndjson() {
r#""duration_ms":\s*\d+"#.to_string(),
r#""duration_ms": [DURATION_MS]"#.to_string(),
));
filters.push((
r#""id":"[0-9a-f-]+""#.to_string(),
r#""id":"[EVENT_ID]""#.to_string(),
));
let mut cmd = context.command();
cmd.args(["logs", &run.run_id]);
@ -53,27 +57,27 @@ fn logs_completed_run_outputs_raw_ndjson() {
success: true
exit_code: 0
----- stdout -----
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.Initializing","sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.Ready","url":null,"duration_ms": [DURATION_MS],"name":null,"cpu":null,"memory":null,"sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"SandboxInitialized","working_directory":"[TEMP_DIR]"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"WorkflowRunStarted","goal":"Run tests and report results","workflow_name":"Simple"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"start","max_attempts":1,"node_label":"Start","handler_type":"start","attempt":1,"stage_index":0}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"start","max_attempts":1,"node_label":"Start","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] start","files_touched":[],"attempt":1,"stage_index":0}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"start","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"run_tests"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"start","status":"success","node_label":"start"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"run_tests","max_attempts":1,"node_label":"Run Tests","handler_type":"agent","attempt":1,"stage_index":1}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"run_tests","max_attempts":1,"node_label":"Run Tests","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] run_tests","files_touched":[],"attempt":1,"stage_index":1}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"run_tests","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"report"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"run_tests","status":"success","node_label":"run_tests"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"report","max_attempts":1,"node_label":"Report","handler_type":"agent","attempt":1,"stage_index":2}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"report","max_attempts":1,"node_label":"Report","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] report","files_touched":[],"attempt":1,"stage_index":2}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"report","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"exit"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"report","status":"success","node_label":"report"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"exit","max_attempts":1,"node_label":"Exit","handler_type":"exit","attempt":1,"stage_index":3}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"exit","max_attempts":1,"node_label":"Exit","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":null,"files_touched":[],"attempt":1,"stage_index":3}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"WorkflowRunCompleted","duration_ms": [DURATION_MS],"artifact_count":0,"status":"success"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupStarted","sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupCompleted","duration_ms": [DURATION_MS],"sandbox_provider":"local"}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.initializing","properties":{"provider":"local"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.ready","properties":{"provider":"local","duration_ms": [DURATION_MS],"name":null,"cpu":null,"memory":null,"url":null}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.initialized","properties":{"working_directory":"[TEMP_DIR]"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"run.started","properties":{"name":"Simple","goal":"Run tests and report results"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"start","node_label":"Start","properties":{"max_attempts":1,"attempt":1,"index":0,"handler_type":"start"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"start","node_label":"Start","properties":{"max_attempts":1,"attempt":1,"index":0,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] start","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"start","to_node":"run_tests","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"start","node_label":"start","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"run_tests","node_label":"Run Tests","properties":{"max_attempts":1,"attempt":1,"index":1,"handler_type":"agent"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"run_tests","node_label":"Run Tests","properties":{"max_attempts":1,"attempt":1,"index":1,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] run_tests","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"run_tests","to_node":"report","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"run_tests","node_label":"run_tests","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"report","node_label":"Report","properties":{"max_attempts":1,"attempt":1,"index":2,"handler_type":"agent"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"report","node_label":"Report","properties":{"max_attempts":1,"attempt":1,"index":2,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] report","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"report","to_node":"exit","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"report","node_label":"report","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"exit","node_label":"Exit","properties":{"max_attempts":1,"attempt":1,"index":3,"handler_type":"exit"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"exit","node_label":"Exit","properties":{"max_attempts":1,"attempt":1,"index":3,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":null,"files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"run.completed","properties":{"duration_ms": [DURATION_MS],"artifact_count":0,"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.started","properties":{"provider":"local"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.completed","properties":{"provider":"local","duration_ms": [DURATION_MS]}}
----- stderr -----
"###);
}
@ -91,6 +95,10 @@ fn logs_tail_limits_output() {
r#""duration_ms":\s*\d+"#.to_string(),
r#""duration_ms": [DURATION_MS]"#.to_string(),
));
filters.push((
r#""id":"[0-9a-f-]+""#.to_string(),
r#""id":"[EVENT_ID]""#.to_string(),
));
let mut cmd = context.command();
cmd.args(["logs", "--tail", "2", &run.run_id]);
@ -98,8 +106,8 @@ fn logs_tail_limits_output() {
success: true
exit_code: 0
----- stdout -----
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupStarted","sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupCompleted","duration_ms": [DURATION_MS],"sandbox_provider":"local"}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.started","properties":{"provider":"local"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.completed","properties":{"provider":"local","duration_ms": [DURATION_MS]}}
----- stderr -----
"###);
}
@ -126,16 +134,16 @@ fn logs_pretty_formats_small_run() {
Run tests and report results
[CLOCK] ▶ Start
[CLOCK] ✓ Start [DURATION] (0 turns, 0 tools, 0 toks)
[CLOCK] ✓ Start [DURATION]
[CLOCK] → run_tests unconditional
[CLOCK] ▶ Run Tests
[CLOCK] ✓ Run Tests [DURATION] (0 turns, 0 tools, 0 toks)
[CLOCK] ✓ Run Tests [DURATION]
[CLOCK] → report unconditional
[CLOCK] ▶ Report
[CLOCK] ✓ Report [DURATION] (0 turns, 0 tools, 0 toks)
[CLOCK] ✓ Report [DURATION]
[CLOCK] → exit unconditional
[CLOCK] ▶ Exit
[CLOCK] ✓ Exit [DURATION] (0 turns, 0 tools, 0 toks)
[CLOCK] ✓ Exit [DURATION]
[CLOCK] ✓ SUCCESS [DURATION]
----- stderr -----
"#);
@ -154,6 +162,10 @@ fn logs_follow_detached_run_streams_until_completion() {
r#""duration_ms":\s*\d+"#.to_string(),
r#""duration_ms": [DURATION_MS]"#.to_string(),
));
filters.push((
r#""id":"[0-9a-f-]+""#.to_string(),
r#""id":"[EVENT_ID]""#.to_string(),
));
let mut cmd = context.command();
cmd.args(["logs", "--follow", &run.run_id]);
@ -161,27 +173,27 @@ fn logs_follow_detached_run_streams_until_completion() {
success: true
exit_code: 0
----- stdout -----
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.Initializing","sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.Ready","url":null,"duration_ms": [DURATION_MS],"name":null,"cpu":null,"memory":null,"sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"SandboxInitialized","working_directory":"[TEMP_DIR]"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"WorkflowRunStarted","goal":"Run tests and report results","workflow_name":"Simple"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"start","max_attempts":1,"node_label":"Start","handler_type":"start","attempt":1,"stage_index":0}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"start","max_attempts":1,"node_label":"Start","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] start","files_touched":[],"attempt":1,"stage_index":0}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"start","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"run_tests"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"start","status":"success","node_label":"start"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"run_tests","max_attempts":1,"node_label":"Run Tests","handler_type":"agent","attempt":1,"stage_index":1}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"run_tests","max_attempts":1,"node_label":"Run Tests","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] run_tests","files_touched":[],"attempt":1,"stage_index":1}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"run_tests","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"report"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"run_tests","status":"success","node_label":"run_tests"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"report","max_attempts":1,"node_label":"Report","handler_type":"agent","attempt":1,"stage_index":2}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"report","max_attempts":1,"node_label":"Report","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] report","files_touched":[],"attempt":1,"stage_index":2}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"EdgeSelected","is_jump":false,"from_node_id":"report","label":null,"condition":null,"reason":"unconditional","stage_status":"success","to_node_id":"exit"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"CheckpointCompleted","node_id":"report","status":"success","node_label":"report"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageStarted","node_id":"exit","max_attempts":1,"node_label":"Exit","handler_type":"exit","attempt":1,"stage_index":3}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"StageCompleted","node_id":"exit","max_attempts":1,"node_label":"Exit","duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":null,"files_touched":[],"attempt":1,"stage_index":3}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"WorkflowRunCompleted","duration_ms": [DURATION_MS],"artifact_count":0,"status":"success"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupStarted","sandbox_provider":"local"}
{"ts":"[TIMESTAMP]","run_id":"[ULID]","event":"Sandbox.CleanupCompleted","duration_ms": [DURATION_MS],"sandbox_provider":"local"}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.initializing","properties":{"provider":"local"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.ready","properties":{"provider":"local","duration_ms": [DURATION_MS],"name":null,"cpu":null,"memory":null,"url":null}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.initialized","properties":{"working_directory":"[TEMP_DIR]"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"run.started","properties":{"name":"Simple","goal":"Run tests and report results"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"start","node_label":"Start","properties":{"max_attempts":1,"attempt":1,"index":0,"handler_type":"start"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"start","node_label":"Start","properties":{"max_attempts":1,"attempt":1,"index":0,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] start","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"start","to_node":"run_tests","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"start","node_label":"start","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"run_tests","node_label":"Run Tests","properties":{"max_attempts":1,"attempt":1,"index":1,"handler_type":"agent"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"run_tests","node_label":"Run Tests","properties":{"max_attempts":1,"attempt":1,"index":1,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] run_tests","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"run_tests","to_node":"report","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"run_tests","node_label":"run_tests","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"report","node_label":"Report","properties":{"max_attempts":1,"attempt":1,"index":2,"handler_type":"agent"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"report","node_label":"Report","properties":{"max_attempts":1,"attempt":1,"index":2,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":"[Simulated] report","files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"edge.selected","properties":{"from_node":"report","to_node":"exit","label":null,"condition":null,"reason":"unconditional","stage_status":"success","is_jump":false}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"checkpoint.completed","node_id":"report","node_label":"report","properties":{"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.started","node_id":"exit","node_label":"Exit","properties":{"max_attempts":1,"attempt":1,"index":3,"handler_type":"exit"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"stage.completed","node_id":"exit","node_label":"Exit","properties":{"max_attempts":1,"attempt":1,"index":3,"duration_ms": [DURATION_MS],"status":"success","preferred_label":null,"suggested_next_ids":[],"usage":null,"notes":null,"files_touched":[]}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"run.completed","properties":{"duration_ms": [DURATION_MS],"artifact_count":0,"status":"success"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.started","properties":{"provider":"local"}}
{"id":"[EVENT_ID]","ts":"[TIMESTAMP]","run_id":"[ULID]","event":"sandbox.cleanup.completed","properties":{"provider":"local","duration_ms": [DURATION_MS]}}
----- stderr -----
"###);
}

View file

@ -74,14 +74,14 @@ fn resume_rewound_run_succeeds() {
let mut resume_cmd = context.command();
resume_cmd.current_dir(&setup.repo_dir);
resume_cmd.env("OPENAI_API_KEY", "test");
resume_cmd.args(["resume", "-d", &setup.run.run_id]);
fabro_snapshot!(context.filters(), resume_cmd, @"
success: true
exit_code: 0
----- stdout -----
[ULID]
----- stderr -----
");
resume_cmd.args(["resume", &setup.run.run_id]);
let resume_output = resume_cmd.output().expect("resume should execute");
assert!(
resume_output.status.success(),
"resume should succeed\nstdout:\n{}\nstderr:\n{}",
String::from_utf8_lossy(&resume_output.stdout),
output_stderr(&resume_output)
);
let mut wait_filters = context.filters();
wait_filters.push((

View file

@ -200,7 +200,7 @@ pub(crate) fn setup_asset_sandbox_run(context: &TestContext) -> AssetSandboxSetu
exit [shape=Msquare]
create_assets [shape=parallelogram, script="mkdir -p assets/shared assets/node_a sandbox_dir/download_me/nested && printf one > assets/shared/report.txt && printf alpha > assets/node_a/summary.txt && printf keep > sandbox_dir/download_me/root.txt && printf nested > sandbox_dir/download_me/nested/child.txt && sleep 1", max_retries=0]
retry_assets [shape=parallelogram, script="mkdir -p assets/retry && if [ ! -f .retry-sentinel ]; then printf first > assets/retry/report.txt && touch .retry-sentinel && sleep 1; else printf second > assets/retry/report.txt; fi", retry_policy="linear", timeout="50ms"]
create_colliding [shape=parallelogram, script="mkdir -p assets/other && printf beta > assets/other/summary.txt", max_retries=0]
create_colliding [shape=parallelogram, script="mkdir -p assets/other assets/retry && printf beta > assets/other/summary.txt && printf second > assets/retry/report.txt", max_retries=0]
start -> create_assets -> retry_assets -> create_colliding -> exit
}
"#,

View file

@ -180,6 +180,27 @@ impl TestContext {
cmd
}
/// Build a `sandbox cp` subcommand.
pub fn cp(&self) -> Command {
let mut cmd = self.sandbox();
cmd.arg("cp");
cmd
}
/// Build a `sandbox ssh` subcommand.
pub fn ssh(&self) -> Command {
let mut cmd = self.sandbox();
cmd.arg("ssh");
cmd
}
/// Build a `sandbox preview` subcommand.
pub fn preview(&self) -> Command {
let mut cmd = self.sandbox();
cmd.arg("preview");
cmd
}
/// Build an `init` subcommand.
pub fn init_cmd(&self) -> Command {
let mut cmd = self.command();

View file

@ -962,8 +962,8 @@ fn flatten_failure_detail(fields: &mut Map<String, Value>) {
}
}
fn default_node_label(node_id: &Option<String>, node_label: Option<String>) -> Option<String> {
node_label.or_else(|| node_id.clone())
fn default_node_label(node_id: Option<&String>, node_label: Option<String>) -> Option<String> {
node_label.or_else(|| node_id.cloned())
}
fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
@ -993,7 +993,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
WorkflowRunEvent::StageCompleted { .. } | WorkflowRunEvent::StageFailed { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "node_id");
let node_label = default_node_label(&node_id, remove_string(&mut fields, "name"));
let node_label = default_node_label(node_id.as_ref(), remove_string(&mut fields, "name"));
flatten_failure_detail(&mut fields);
EnvelopeFields {
session_id: None,
@ -1003,10 +1003,16 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
properties: Value::Object(fields),
}
}
WorkflowRunEvent::StageStarted { .. } | WorkflowRunEvent::StageRetrying { .. } => {
WorkflowRunEvent::StageStarted { .. }
| WorkflowRunEvent::StageRetrying { .. }
| WorkflowRunEvent::CheckpointCompleted { .. }
| WorkflowRunEvent::CheckpointFailed { .. }
| WorkflowRunEvent::SubgraphStarted { .. }
| WorkflowRunEvent::SubgraphCompleted { .. }
| WorkflowRunEvent::AssetCaptured { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "node_id");
let node_label = default_node_label(&node_id, remove_string(&mut fields, "name"));
let node_label = default_node_label(node_id.as_ref(), remove_string(&mut fields, "name"));
EnvelopeFields {
session_id: None,
parent_session_id: None,
@ -1022,7 +1028,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
} => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "stage");
let node_label = default_node_label(&node_id, None);
let node_label = default_node_label(node_id.as_ref(), None);
fields.remove("session_id");
fields.remove("parent_session_id");
let properties = fields.remove("event").map_or_else(
@ -1054,7 +1060,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
WorkflowRunEvent::GitCommit { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "node_id");
let node_label = default_node_label(&node_id, None);
let node_label = default_node_label(node_id.as_ref(), None);
EnvelopeFields {
session_id: None,
parent_session_id: None,
@ -1067,7 +1073,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
| WorkflowRunEvent::ParallelBranchCompleted { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "branch");
let node_label = default_node_label(&node_id, None);
let node_label = default_node_label(node_id.as_ref(), None);
EnvelopeFields {
session_id: None,
parent_session_id: None,
@ -1082,23 +1088,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
| WorkflowRunEvent::Failover { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "stage");
let node_label = default_node_label(&node_id, None);
EnvelopeFields {
session_id: None,
parent_session_id: None,
node_id,
node_label,
properties: Value::Object(fields),
}
}
WorkflowRunEvent::CheckpointCompleted { .. }
| WorkflowRunEvent::CheckpointFailed { .. }
| WorkflowRunEvent::SubgraphStarted { .. }
| WorkflowRunEvent::SubgraphCompleted { .. }
| WorkflowRunEvent::AssetCaptured { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "node_id");
let node_label = default_node_label(&node_id, remove_string(&mut fields, "name"));
let node_label = default_node_label(node_id.as_ref(), None);
EnvelopeFields {
session_id: None,
parent_session_id: None,
@ -1110,7 +1100,7 @@ fn extract_envelope_fields(event: &WorkflowRunEvent) -> EnvelopeFields {
WorkflowRunEvent::StallWatchdogTimeout { .. } => {
let mut fields = tagged_variant_fields(event);
let node_id = remove_string(&mut fields, "node");
let node_label = default_node_label(&node_id, None);
let node_label = default_node_label(node_id.as_ref(), None);
EnvelopeFields {
session_id: None,
parent_session_id: None,
@ -1362,7 +1352,7 @@ impl EventEmitter {
.expect("listeners lock poisoned")
.clone();
for listener in &snapshot {
listener(&envelope);
listener(envelope);
}
}

View file

@ -761,7 +761,7 @@ impl Drop for DetachedRunCompletionGuard {
);
}
if let Some((run_id, line)) = serialized_notice.or(run_id
.map(|run_id| {
.and_then(|run_id| {
let envelope = canonicalize_event(
&run_id,
&WorkflowRunEvent::RunNotice {
@ -774,7 +774,7 @@ impl Drop for DetachedRunCompletionGuard {
.ok()
.map(|line| (run_id, line))
})
.flatten())
)
{
match event_payload_from_redacted_json(&line, &run_id) {
Ok(payload) => {

View file

@ -21,9 +21,9 @@ struct InitializedOptions {
checkpoint: Option<Checkpoint>,
}
fn bound_emitter(run_id: fabro_types::RunId, observer: Arc<EventEmitter>) -> Arc<EventEmitter> {
fn bound_emitter(run_id: fabro_types::RunId, observer: &Arc<EventEmitter>) -> Arc<EventEmitter> {
let emitter = Arc::new(EventEmitter::new(run_id));
let observer_clone = Arc::clone(&observer);
let observer_clone = Arc::clone(observer);
emitter.on_event(move |event| observer_clone.dispatch_envelope(event));
emitter
}
@ -49,7 +49,7 @@ async fn initialized(
inner_store,
run_options.run_dir.clone(),
));
let emitter = bound_emitter(run_options.run_id, emitter);
let emitter = bound_emitter(run_options.run_id, &emitter);
Initialized {
graph: graph.clone(),
source: String::new(),