refactor(workflows): move conclusion into records module and preamble into handler/llm

Relocate conclusion.rs to records/conclusion.rs behind a new records
module, and move preamble.rs into handler/llm/preamble.rs where it is
actually used. Update all imports across fabro-cli and fabro-workflows.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Bryan Helmkamp 2026-03-25 11:05:17 -04:00
parent 0c5bbf5bc1
commit 08f4a6d978
No known key found for this signature in database
27 changed files with 42 additions and 38 deletions

View file

@ -345,7 +345,7 @@ fn write_interview_response_atomically(
fn determine_exit_code(conclusion_path: &Path, status_record: Option<RunStatusRecord>) -> ExitCode {
if conclusion_path.exists() {
if let Ok(conclusion) = fabro_workflows::conclusion::Conclusion::load(conclusion_path) {
if let Ok(conclusion) = fabro_workflows::records::Conclusion::load(conclusion_path) {
let success = matches!(
conclusion.status,
fabro_workflows::outcome::StageStatus::Success
@ -395,7 +395,7 @@ mod tests {
use chrono::Utc;
use fabro_interview::{Answer, AnswerValue};
use fabro_util::terminal::Styles;
use fabro_workflows::conclusion::Conclusion;
use fabro_workflows::records::Conclusion;
use fabro_workflows::outcome::StageStatus;
use fabro_workflows::run_status::{write_run_status, StatusReason};

View file

@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use chrono::{SecondsFormat, Utc};
use fabro_workflows::conclusion::Conclusion;
use fabro_workflows::records::Conclusion;
use fabro_workflows::event::{RunNoticeLevel, WorkflowRunEvent};
use fabro_workflows::outcome::StageStatus;
use fabro_workflows::run_status::{self, RunStatus, StatusReason};
@ -331,7 +331,7 @@ mod tests {
assert_eq!(record.status, RunStatus::Failed);
assert_eq!(record.reason, Some(StatusReason::BootstrapFailed));
let conclusion =
fabro_workflows::conclusion::Conclusion::load(&dir.path().join("conclusion.json"))
fabro_workflows::records::Conclusion::load(&dir.path().join("conclusion.json"))
.unwrap();
assert_eq!(conclusion.status, StageStatus::Fail);
assert_eq!(

View file

@ -43,7 +43,7 @@ fn inspect_run_dir(
.ok()
.and_then(|v| serde_json::to_value(v).ok());
let conclusion =
fabro_workflows::conclusion::Conclusion::load(&run_dir.join("conclusion.json"))
fabro_workflows::records::Conclusion::load(&run_dir.join("conclusion.json"))
.ok()
.and_then(|v| serde_json::to_value(v).ok());
let checkpoint =

View file

@ -332,7 +332,7 @@ async fn create_from(
.context("Failed to load start.json")?;
let conclusion =
fabro_workflows::conclusion::Conclusion::load(&run_dir.join("conclusion.json"))
fabro_workflows::records::Conclusion::load(&run_dir.join("conclusion.json"))
.context("Failed to load conclusion.json — is the run finished?")?;
match conclusion.status {

View file

@ -1852,7 +1852,7 @@ pub(crate) fn emit_run_notice(
/// the same result block that `run_command` prints in-process.
pub fn print_run_summary(run_dir: &Path, run_id: &str, styles: &Styles) {
let conclusion_path = run_dir.join("conclusion.json");
let Ok(conclusion) = fabro_workflows::conclusion::Conclusion::load(&conclusion_path) else {
let Ok(conclusion) = fabro_workflows::records::Conclusion::load(&conclusion_path) else {
return;
};
@ -1879,7 +1879,7 @@ pub fn print_run_summary(run_dir: &Path, run_id: &str, styles: &Styles) {
}
pub(crate) fn print_run_conclusion(
conclusion: &fabro_workflows::conclusion::Conclusion,
conclusion: &fabro_workflows::records::Conclusion,
run_id: &str,
run_dir: &Path,
pushed_branch: Option<&str>,

View file

@ -64,7 +64,7 @@ pub fn run(args: WaitArgs, styles: &Styles) -> Result<()> {
};
let conclusion_path = run_info.path.join("conclusion.json");
let conclusion = fabro_workflows::conclusion::Conclusion::load(&conclusion_path).ok();
let conclusion = fabro_workflows::records::Conclusion::load(&conclusion_path).ok();
if args.json {
let json_value = build_json_output(final_status, &run_info.run_id, conclusion.as_ref());
@ -85,7 +85,7 @@ pub fn run(args: WaitArgs, styles: &Styles) -> Result<()> {
fn build_json_output(
status: RunStatus,
run_id: &str,
conclusion: Option<&fabro_workflows::conclusion::Conclusion>,
conclusion: Option<&fabro_workflows::records::Conclusion>,
) -> serde_json::Value {
let mut value = serde_json::json!({
"run_id": run_id,
@ -103,7 +103,7 @@ fn build_json_output(
fn print_human_output(
status: RunStatus,
run_id: &str,
conclusion: Option<&fabro_workflows::conclusion::Conclusion>,
conclusion: Option<&fabro_workflows::records::Conclusion>,
styles: &Styles,
) {
let (style, label) = match status {
@ -137,7 +137,7 @@ fn print_human_output(
#[cfg(test)]
mod tests {
use super::*;
use fabro_workflows::conclusion::Conclusion;
use fabro_workflows::records::Conclusion;
use fabro_workflows::outcome::StageStatus;
fn no_color_styles() -> Styles {

View file

@ -12,11 +12,11 @@ use fabro_llm::client::Client;
use fabro_model::FallbackTarget;
use fabro_model::Provider;
use super::super::agent::{CodergenBackend, CodergenResult};
use crate::context::{Context, WorkflowContext};
use crate::cost::compute_stage_cost;
use crate::error::FabroError;
use crate::event::WorkflowRunEvent;
use super::super::agent::{CodergenBackend, CodergenResult};
use crate::outcome::StageUsage;
use fabro_graphviz::graph::Node;

View file

@ -7,11 +7,11 @@ use fabro_agent::sandbox::ExecResult;
use fabro_agent::Sandbox;
use fabro_model::Provider;
use super::super::agent::{CodergenBackend, CodergenResult};
use crate::context::Context;
use crate::cost::compute_stage_cost;
use crate::error::FabroError;
use crate::event::{EventEmitter, WorkflowRunEvent};
use super::super::agent::{CodergenBackend, CodergenResult};
use crate::outcome::StageUsage;
use fabro_graphviz::graph::Node;

View file

@ -1,5 +1,6 @@
pub mod api;
pub mod cli;
pub mod preamble;
pub use api::AgentApiBackend;
pub use cli::{parse_cli_response, AgentCliBackend, BackendRouter};

View file

@ -1,10 +1,10 @@
pub mod agent;
pub mod command;
pub mod llm;
pub mod conditional;
pub mod exit;
pub mod fan_in;
pub mod human;
pub mod llm;
pub mod manager_loop;
pub mod parallel;
pub mod prompt;

View file

@ -92,7 +92,6 @@ pub mod artifact;
pub mod asset_snapshot;
pub mod assets;
pub mod checkpoint;
pub mod conclusion;
pub mod condition;
pub mod context;
pub mod cost;
@ -109,6 +108,7 @@ pub mod operations;
pub mod outcome;
pub mod pipeline;
pub mod pull_request;
pub mod records;
pub mod run_dir;
pub mod run_lookup;
pub mod run_record;

View file

@ -8,10 +8,10 @@ use fabro_core::lifecycle::{AttemptContext, AttemptResultContext, RunLifecycle};
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::artifact::{offload_large_values, sync_artifacts_to_env, ArtifactStore};
use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent};
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::outcome::StageUsage;
use fabro_core::lifecycle::NodeDecision;

View file

@ -8,9 +8,9 @@ use fabro_core::lifecycle::{EdgeContext, EdgeDecision, RunLifecycle};
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::error::{FailureCategory, FailureSignature};
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::error::{FailureCategory, FailureSignature};
use crate::graph_ops::classify_outcome;
use crate::outcome::{OutcomeExt, StageStatus, StageUsage};

View file

@ -8,11 +8,11 @@ use fabro_core::lifecycle::RunLifecycle;
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use super::circuit_breaker::CircuitBreakerLifecycle;
use crate::checkpoint::Checkpoint;
use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent};
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::outcome::StageUsage;
use crate::run_dir::{write_node_status, write_start_record};
use crate::run_settings::RunSettings;

View file

@ -8,11 +8,11 @@ use fabro_core::lifecycle::{AttemptContext, AttemptResultContext, EdgeContext, R
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use super::git::GitCheckpointResult;
use crate::artifact::ArtifactStore;
use crate::event::{EventEmitter, WorkflowRunEvent};
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::graph_ops::node_script;
use crate::outcome::{FailureCategory, FailureDetail, Outcome, StageStatus, StageUsage};

View file

@ -6,12 +6,12 @@ use fabro_core::graph::NodeSpec;
use fabro_core::lifecycle::{EdgeContext, NodeDecision, RunLifecycle};
use fabro_core::state::RunState;
use crate::context::keys;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::context::keys;
use crate::graph_ops::{resolve_fidelity, resolve_thread_id};
use crate::handler::llm::preamble::build_preamble;
use crate::outcome::StageUsage;
use crate::preamble::build_preamble;
type WfRunState = RunState<Option<StageUsage>>;
type WfNodeDecision = NodeDecision<Option<StageUsage>>;

View file

@ -9,10 +9,10 @@ use fabro_core::lifecycle::RunLifecycle;
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::artifact::ArtifactStore;
use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent};
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::outcome::{Outcome, StageStatus, StageUsage};
use crate::run_dir::node_dir;
use crate::run_settings::RunSettings;

View file

@ -23,11 +23,11 @@ use fabro_core::lifecycle::{
use fabro_core::outcome::NodeResult;
use fabro_core::state::RunState;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::artifact::ArtifactStore;
use crate::context;
use crate::event::EventEmitter;
use crate::graph::WorkflowGraph;
use crate::graph::WorkflowNode;
use crate::outcome::{Outcome, StageUsage};
use crate::run_settings::RunSettings;
use fabro_hooks::HookRunner;

View file

@ -147,8 +147,8 @@ mod tests {
use fabro_graphviz::graph::types::{Edge, Graph, Node};
use fabro_graphviz::graph::AttrValue;
use crate::graph::WorkflowGraph;
use super::*;
use crate::graph::WorkflowGraph;
/// Minimal spike handler that always succeeds — proves the trait plumbing.
pub struct SpikeHandler;

View file

@ -6,11 +6,11 @@ use fabro_core::state::RunState;
use tokio_util::sync::CancellationToken;
use crate::context::{self, Context};
use crate::error::FabroError;
use crate::graph::WorkflowGraph;
use crate::handler::EngineServices;
use crate::lifecycle::WorkflowLifecycle;
use crate::node_handler::WorkflowNodeHandler;
use crate::error::FabroError;
use crate::handler::EngineServices;
use crate::outcome::{Outcome, StageStatus};
use crate::sandbox_git::GitState;

View file

@ -2,7 +2,7 @@ use std::path::Path;
use std::sync::Arc;
use crate::checkpoint::Checkpoint;
use crate::conclusion::Conclusion;
use crate::records::Conclusion;
use crate::error::FabroError;
use crate::event::{EventEmitter, RunNoticeLevel, WorkflowRunEvent};
use crate::outcome::{Outcome, OutcomeExt, StageStatus};
@ -106,7 +106,7 @@ pub fn build_conclusion(
total_reasoning_tokens += usage.reasoning_tokens.unwrap_or(0);
}
stages.push(crate::conclusion::StageSummary {
stages.push(crate::records::StageSummary {
stage_id: node_id.clone(),
stage_label: node_id.clone(),
duration_ms: stage_durations.get(node_id).copied().unwrap_or(0),

View file

@ -8,7 +8,7 @@ use fabro_hooks::HookRunner;
use fabro_validate::Diagnostic;
use crate::checkpoint::Checkpoint;
use crate::conclusion::Conclusion;
use crate::records::Conclusion;
use crate::context::Context;
use crate::error::FabroError;
use crate::event::EventEmitter;

View file

@ -6,7 +6,7 @@ use tracing::{debug, info};
use fabro_github::{self as github_app, ssh_url_to_https, GitHubAppCredentials};
use crate::conclusion::Conclusion;
use crate::records::Conclusion;
use crate::run_record::RunRecord;
use fabro_retro::retro::Retro;
@ -452,7 +452,7 @@ pub async fn maybe_open_pull_request(
#[cfg(test)]
mod tests {
use super::*;
use crate::conclusion::StageSummary;
use crate::records::StageSummary;
use chrono::Utc;
use fabro_retro::retro::{
AggregateStats, FrictionKind, FrictionPoint, OpenItem, OpenItemKind, StageRetro,

View file

@ -0,0 +1,3 @@
mod conclusion;
pub use conclusion::{Conclusion, StageSummary};

View file

@ -165,7 +165,7 @@ fn read_status(run_dir: &Path) -> StatusInfo {
if let Ok(record) = RunStatusRecord::load(&run_dir.join("status.json")) {
if record.status.is_terminal() {
if let Ok(conclusion) =
crate::conclusion::Conclusion::load(&run_dir.join("conclusion.json"))
crate::records::Conclusion::load(&run_dir.join("conclusion.json"))
{
return StatusInfo {
status: record.status,