diff --git a/lib/crates/fabro-cli/tests/it/cmd/start.rs b/lib/crates/fabro-cli/tests/it/cmd/start.rs index df7038632..ba6016635 100644 --- a/lib/crates/fabro-cli/tests/it/cmd/start.rs +++ b/lib/crates/fabro-cli/tests/it/cmd/start.rs @@ -1,6 +1,6 @@ use fabro_test::{fabro_snapshot, test_context}; -use crate::support::{example_fixture, fabro_json_snapshot, read_json}; +use crate::support::{example_fixture, fabro_json_snapshot}; use super::support::{output_stdout, resolve_run, wait_for_status, write_gated_workflow}; @@ -58,21 +58,21 @@ fn start_by_run_id_starts_created_run() { .assert() .success(); - let run_dir = context.find_run_dir(run_id); - let status = read_json(run_dir.join("status.json")); - let conclusion = read_json(run_dir.join("conclusion.json")); + let output = context + .command() + .args(["wait", "--json", run_id]) + .output() + .expect("wait should execute"); + assert!(output.status.success(), "wait should succeed"); + let value: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON"); fabro_json_snapshot!( context, serde_json::json!({ - "status": status["status"], - "reason": status["reason"], - "conclusion_status": conclusion["status"], + "status": value["status"], }), @r#" { - "status": "succeeded", - "reason": "completed", - "conclusion_status": "success" + "status": "succeeded" } "# ); @@ -98,25 +98,24 @@ fn start_by_run_id_starts_created_run_without_run_json_or_status_json() { let run_dir = context.find_run_dir(run_id); std::fs::remove_file(run_dir.join("run.json")).unwrap(); - std::fs::remove_file(run_dir.join("status.json")).unwrap(); context.command().args(["start", run_id]).assert().success(); - context + let output = context .command() - .args(["wait", run_id]) + .args(["wait", "--json", run_id]) .timeout(std::time::Duration::from_secs(10)) - .assert() - .success(); - - let conclusion = read_json(run_dir.join("conclusion.json")); + .output() + .expect("wait should execute"); + assert!(output.status.success(), "wait should succeed"); + let value: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON"); fabro_json_snapshot!( context, serde_json::json!({ - "conclusion_status": conclusion["status"], + "status": value["status"], }), @r#" { - "conclusion_status": "success" + "status": "succeeded" } "# ); @@ -190,13 +189,20 @@ digraph Smoke { .assert() .success(); - let new_run_dir = context.find_run_dir(new_run_id); - let status = read_json(new_run_dir.join("status.json")); - fabro_json_snapshot!(context, &status, @r#" + let output = context + .command() + .args(["wait", "--json", new_run_id]) + .output() + .expect("wait should execute"); + assert!(output.status.success(), "wait should succeed"); + let status: serde_json::Value = serde_json::from_slice(&output.stdout).expect("wait JSON"); + fabro_json_snapshot!(context, &serde_json::json!({ + "run_id": status["run_id"], + "status": status["status"], + }), @r#" { - "status": "succeeded", - "reason": "completed", - "updated_at": "[TIMESTAMP]" + "run_id": "[ULID]", + "status": "succeeded" } "#); } diff --git a/lib/crates/fabro-workflow/src/operations/create.rs b/lib/crates/fabro-workflow/src/operations/create.rs index be451c72d..345b67b94 100644 --- a/lib/crates/fabro-workflow/src/operations/create.rs +++ b/lib/crates/fabro-workflow/src/operations/create.rs @@ -15,7 +15,7 @@ use crate::pipeline::types::PersistOptions; use crate::pipeline::{self, Persisted, TransformOptions, Validated}; use crate::records::RunRecord; use crate::run_lookup::default_runs_base; -use crate::run_status::{RunStatus, RunStatusRecord, write_run_status}; +use crate::run_status::{RunStatus, RunStatusRecord}; use crate::transforms::{Transform, expand_vars}; use fabro_sandbox::daytona::detect_repo_info; @@ -120,7 +120,6 @@ pub async fn create(store: &dyn Store, request: CreateRunInput) -> Result InMemoryStore { InMemoryStore::default() } @@ -752,10 +749,9 @@ mod tests { created.persisted.run_record().workflow_slug.as_deref(), Some("slug") ); + let run_store = store.open_run(&fixtures::RUN_1).await.unwrap().unwrap(); assert_eq!( - crate::run_status::RunStatusRecord::load(&created.run_dir.join("status.json")) - .unwrap() - .status, + run_store.get_status().await.unwrap().unwrap().status, crate::run_status::RunStatus::Submitted ); assert!(!created.run_dir.join("id.txt").exists()); diff --git a/lib/crates/fabro-workflow/src/operations/resume.rs b/lib/crates/fabro-workflow/src/operations/resume.rs index 8c13ae0ce..268f04a69 100644 --- a/lib/crates/fabro-workflow/src/operations/resume.rs +++ b/lib/crates/fabro-workflow/src/operations/resume.rs @@ -46,7 +46,6 @@ pub async fn resume(run_dir: &Path, services: StartServices) -> Result, cancel_token: Option>, active: bool, @@ -608,17 +607,11 @@ struct DetachedRunBootstrapGuard { impl DetachedRunBootstrapGuard { fn arm( - run_dir: &Path, + _run_dir: &Path, run_store: Arc, cancel_token: Option>, ) -> Self { - run_status::write_run_status( - run_dir, - RunStatus::Starting, - Some(StatusReason::SandboxInitializing), - ); Self { - run_dir: run_dir.to_path_buf(), run_store, cancel_token, active: true, @@ -642,7 +635,6 @@ impl Drop for DetachedRunBootstrapGuard { } else { StatusReason::SandboxInitFailed }; - run_status::write_run_status(&self.run_dir, RunStatus::Failed, Some(reason)); let run_store = Arc::clone(&self.run_store); if let Ok(handle) = Handle::try_current() { handle.spawn(async move { @@ -715,10 +707,6 @@ impl Drop for DetachedRunCompletionGuard { "postrun_aborted" }; - run_status::write_run_status(&self.run_dir, RunStatus::Failed, Some(reason)); - if !self.run_dir.join("conclusion.json").exists() { - let _ = write_failure_conclusion(&self.run_dir, message, Some(reason)); - } let serialized_notice = load_run_id(&self.run_dir).and_then(|run_id| { let envelope = canonicalize_event( &run_id, @@ -832,8 +820,7 @@ async fn persist_detached_failure( ) .map_err(|err| FabroError::Io(err.to_string()))?; - let conclusion = write_failure_conclusion(run_dir, &message, Some(reason))?; - run_status::write_run_status(run_dir, RunStatus::Failed, Some(reason)); + let conclusion = build_failure_conclusion(&message); if let Err(err) = run_store.put_conclusion(&conclusion).await { tracing::warn!(error = %err, "Failed to save detached failure conclusion to store"); } @@ -872,20 +859,6 @@ async fn persist_detached_failure( Ok(()) } -fn write_failure_conclusion( - run_dir: &Path, - message: &str, - _reason: Option, -) -> Result { - if run_dir.join("conclusion.json").exists() { - return Conclusion::load(&run_dir.join("conclusion.json")); - } - - let conclusion = build_failure_conclusion(message); - conclusion.save(&run_dir.join("conclusion.json"))?; - Ok(conclusion) -} - fn build_failure_conclusion(message: &str) -> Conclusion { Conclusion { timestamp: Utc::now(), @@ -922,7 +895,7 @@ mod tests { use crate::handler::exit::ExitHandler; use crate::handler::start::StartHandler; use crate::operations::resume; - use crate::records::CheckpointExt; + use crate::records::{CheckpointExt, ConclusionExt}; const MINIMAL_DOT: &str = r#"digraph Test { graph [goal="Build feature"] @@ -1044,7 +1017,8 @@ mod tests { .unwrap(); assert_eq!(started.finalized.conclusion.status, StageStatus::Success); - assert!(run_dir.join("conclusion.json").exists()); + let run_store = store.open_run(&fixtures::RUN_1).await.unwrap().unwrap(); + assert!(run_store.get_conclusion().await.unwrap().is_some()); } #[tokio::test] diff --git a/lib/crates/fabro-workflow/src/pipeline/finalize.rs b/lib/crates/fabro-workflow/src/pipeline/finalize.rs index ee96fd642..9e08e5d14 100644 --- a/lib/crates/fabro-workflow/src/pipeline/finalize.rs +++ b/lib/crates/fabro-workflow/src/pipeline/finalize.rs @@ -5,9 +5,9 @@ use crate::error::FabroError; use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent}; use crate::git::{MetadataStore, scan_node_files_from_store}; use crate::outcome::{Outcome, OutcomeExt, StageStatus}; -use crate::records::{Checkpoint, CheckpointExt, Conclusion, ConclusionExt, StageSummary}; +use crate::records::{Checkpoint, CheckpointExt, Conclusion, StageSummary}; use crate::run_options::RunOptions; -use crate::run_status::{RunStatus, StatusReason, write_run_status}; +use crate::run_status::{RunStatus, StatusReason}; use crate::sandbox_git::git_push_host; use fabro_hooks::{HookContext, HookEvent, HookRunner}; use fabro_retro::retro::extract_stage_durations; @@ -249,13 +249,12 @@ fn build_conclusion_from_parts( } pub fn persist_terminal_outcome( - run_dir: &Path, + _run_dir: &Path, conclusion: &Conclusion, run_status: RunStatus, status_reason: Option, ) { - let _ = conclusion.save(&run_dir.join("conclusion.json")); - write_run_status(run_dir, run_status, status_reason); + let _ = (conclusion, run_status, status_reason); } /// Write a finalize commit to the shadow branch with retro.json and final node files. @@ -512,7 +511,7 @@ mod tests { .await .unwrap(); - assert!(run_dir.join("conclusion.json").exists()); + assert!(run_store.get_conclusion().await.unwrap().is_some()); assert_eq!(concluded.conclusion.status, StageStatus::Success); } }