pub mod agent;
pub mod budget;
pub mod ctx;
pub mod event;
pub mod journal;
pub mod parallel;
pub mod pipeline;
pub mod progress;
pub mod worktree;
#[cfg(test)]
mod audit_gap_tests;
pub use agent::agent;
pub use ctx::{ProviderFactory, WorkflowCtx};
pub use event::{log, phase};
pub use parallel::{parallel, thunk};
pub use pipeline::pipeline;
pub async fn workflow<R, F, Fut>(
ctx: &WorkflowCtx,
_name: impl Into<String>,
body: F,
) -> Result<R, crate::error::Error>
where
F: FnOnce(WorkflowCtx) -> Fut,
Fut: std::future::Future<Output = Result<R, crate::error::Error>>,
{
let child = ctx.nested()?;
body(child).await
}
#[cfg(test)]
mod smoke_tests {
use std::sync::Arc;
use serde_json::Value;
use crate::agent::test_helpers::MockProvider;
use crate::llm::BoxedProvider;
#[tokio::test]
async fn p1_surface_composes_through_public_paths() {
let responses: Vec<_> = (0..6)
.map(|i| MockProvider::text_response(&format!("r{i}"), 5, 3))
.collect();
let provider = Arc::new(BoxedProvider::new(MockProvider::new(responses)));
let ctx = crate::WorkflowCtx::builder(provider)
.max_concurrency(4)
.build()
.expect("build ctx");
let _phase = crate::flow::phase(&ctx, "smoke");
let single = crate::flow::agent(&ctx, "solo")
.label("solo")
.run()
.await
.expect("single ok");
assert!(single.is_some());
let thunks: Vec<_> = (0..2)
.map(|i| {
let ctx = ctx.clone();
move || async move { crate::flow::agent(&ctx, format!("p{i}")).run().await }
})
.collect();
let fanned = crate::flow::parallel(&ctx, thunks).await;
assert_eq!(fanned.iter().filter(|o| o.is_some()).count(), 2);
let piped = crate::flow::pipeline(&ctx, vec![10usize, 20usize])
.stage(move |_prev, item, _idx| {
let ctx = ctx.clone();
async move {
let text = crate::flow::agent(&ctx, format!("item {item}"))
.run()
.await?
.unwrap_or_default();
Ok(Value::String(text))
}
})
.run()
.await;
assert_eq!(piped.iter().filter(|o| o.is_some()).count(), 2);
}
}