use std::collections::{BTreeMap, BTreeSet};
use std::num::NonZeroU64;
use std::sync::Mutex;
use serde_json::Value;
use crate::edits::{self, Frontier, Operation};
use crate::event::{Envelope, Source};
use crate::graph::{Graph, Landing, NodeStatus};
use crate::journal;
use crate::plan::Plan;
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum StopState {
#[default]
NotStopped,
WorkersSignalled,
WorkersUndetermined,
}
#[derive(Debug, Clone, Default)]
pub struct RunState {
pub graph: Graph,
pub plan: Option<Plan>,
pub recorded: BTreeMap<String, Recorded>,
pub outcomes: BTreeMap<String, String>,
pub parks: BTreeMap<String, edits::Park>,
pub branches: BTreeMap<String, String>,
pub sessions: BTreeMap<String, crate::vcs::DispatchSession>,
pub abandoned: BTreeMap<String, crate::vcs::DispatchSession>,
pub change_urls: BTreeMap<String, String>,
pub causes: BTreeMap<String, String>,
pub heads: BTreeMap<String, String>,
pub landing_commits: BTreeMap<String, String>,
pub landings: BTreeMap<String, Landing>,
pub completed_steps: BTreeMap<String, Vec<String>>,
pub dispatched_at: BTreeMap<String, u64>,
pub settled_at: BTreeMap<String, u64>,
pub attestations: BTreeSet<String>,
pub decisions_pending: BTreeMap<String, PendingDecision>,
pub holds: BTreeMap<String, Vec<Value>>,
pub completion_requests: Vec<String>,
pub surfaces_queued: u64,
pub surfaces_read: u64,
pub last_surface_at: Option<u64>,
pub last_write_at: Option<u64>,
pub stop: StopState,
pub strict: bool,
pub cross_dag_watches: BTreeMap<String, u64>,
pub cross_dag_baselines: BTreeMap<String, u64>,
pub cross_dag_reported: BTreeSet<(String, String)>,
pub cross_dag: BTreeMap<String, NodeStatus>,
pub pending_context: BTreeMap<String, String>,
pub refusals: BTreeMap<String, Vec<Refusal>>,
pub served: BTreeMap<String, Vec<Served>>,
pub superseded: BTreeMap<String, String>,
pub activity: BTreeMap<String, NodeActivity>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Recorded {
At(NodeStatus),
Cancelling {
since: u64,
},
}
impl Recorded {
pub fn status(self) -> NodeStatus {
match self {
Self::At(status) => status,
Self::Cancelling { .. } => NodeStatus::Parked,
}
}
pub fn cancelling_since(self) -> Option<u64> {
match self {
Self::At(_) => None,
Self::Cancelling { since } => Some(since),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct PendingDecision {
pub kind: String,
pub unblocks: Vec<String>,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct NodeActivity {
pub doing: Option<String>,
pub progress: Option<Progress>,
pub last_heartbeat_at: Option<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Progress {
events: NonZeroU64,
last_at: u64,
}
impl Progress {
pub(crate) fn first(at: Option<u64>) -> Option<Self> {
Some(Self {
events: NonZeroU64::MIN,
last_at: at?,
})
}
pub(crate) fn and(self, at: Option<u64>) -> Self {
Self {
events: self.events.saturating_add(1),
last_at: at.unwrap_or(self.last_at),
}
}
pub fn events(self) -> u64 {
self.events.get()
}
pub fn last_at(self) -> u64 {
self.last_at
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Refusal {
pub advanced: oneagentgraph::event::FallbackAdvanced,
pub member: MemberLabel,
pub records: std::num::NonZeroU64,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Served {
pub session: oneagentgraph::event::OneharnessSession,
pub member: MemberLabel,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MemberLabel {
Named(String),
Unstamped,
Unreadable,
}
fn is_fallback_advanced(kind: &crate::event::EventKind) -> bool {
serde_json::from_value::<oneagentgraph::event::EventKind>(Value::String(kind.0.clone()))
.is_ok_and(|known| known == oneagentgraph::event::EventKind::FallbackAdvanced)
}
fn is_oneharness_session(kind: &crate::event::EventKind) -> bool {
serde_json::from_value::<oneagentgraph::event::EventKind>(Value::String(kind.0.clone()))
.is_ok_and(|known| known == oneagentgraph::event::EventKind::OneharnessSession)
}
const TURN_ACTIVITY: &str = "turn-activity";
pub(crate) fn evidences_progress(event: &Envelope) -> bool {
event.kind.0 != oneagentgraph::event::EventKind::MemberHeartbeat.as_str()
}
impl RunState {
pub fn stop_recorded(&self) -> bool {
self.stop != StopState::NotStopped
}
pub fn frontier(&self) -> Frontier {
Frontier {
recorded: self.statuses_recorded(),
attestations: self.attestations.clone(),
parks: self.parks.clone(),
in_flight: BTreeMap::new(),
node_validator: None,
}
}
fn statuses_recorded(&self) -> BTreeMap<String, NodeStatus> {
self.recorded
.iter()
.map(|(id, recorded)| (id.clone(), recorded.status()))
.collect()
}
pub fn sessions_in_flight(&self) -> BTreeMap<String, crate::vcs::DispatchSession> {
self.recorded
.iter()
.filter(|(_, recorded)| recorded.status() == NodeStatus::Running)
.filter_map(|(id, _)| Some((id.clone(), self.sessions.get(id)?.clone())))
.collect()
}
pub fn awaiting_human_action(&self) -> bool {
self.statuses()
.values()
.any(|status| *status == NodeStatus::Waiting)
}
pub fn statuses(&self) -> BTreeMap<String, NodeStatus> {
let recorded = self.statuses_recorded();
if let Some(derived) = derived_already(&self.graph, &recorded, &self.cross_dag) {
return derived;
}
let derived = self.statuses_with(&|dependency| self.cross_dag.get(dependency).copied());
remember_derived(&self.graph, &recorded, &self.cross_dag, &derived);
derived
}
pub fn statuses_with(
&self,
upstream: &dyn Fn(&str) -> Option<NodeStatus>,
) -> BTreeMap<String, NodeStatus> {
crate::loopstats::statuses_derived();
crate::graph::derive(&self.graph, &self.statuses_recorded(), upstream)
}
}
static DERIVED: Mutex<Vec<Derivation>> = Mutex::new(Vec::new());
const DERIVATIONS_HELD: usize = 4;
type Statuses = BTreeMap<String, NodeStatus>;
struct Derivation {
graph: Graph,
recorded: Statuses,
cross_dag: Statuses,
derived: Statuses,
}
fn derivations() -> std::sync::MutexGuard<'static, Vec<Derivation>> {
DERIVED.lock().unwrap_or_else(|held| held.into_inner())
}
fn derived_already(graph: &Graph, recorded: &Statuses, cross_dag: &Statuses) -> Option<Statuses> {
derivations()
.iter()
.find(|held| {
held.graph == *graph && held.recorded == *recorded && held.cross_dag == *cross_dag
})
.map(|held| held.derived.clone())
}
fn remember_derived(graph: &Graph, recorded: &Statuses, cross_dag: &Statuses, derived: &Statuses) {
let mut held = derivations();
if held.len() >= DERIVATIONS_HELD {
held.remove(0);
}
held.push(Derivation {
graph: graph.clone(),
recorded: recorded.clone(),
cross_dag: cross_dag.clone(),
derived: derived.clone(),
});
}
pub fn fold(events: &[Envelope]) -> RunState {
let mut state = RunState {
strict: true,
..RunState::default()
};
for event in events {
fold_one(&mut state, event);
}
state
}
pub(crate) fn fold_one(state: &mut RunState, event: &Envelope) {
state.last_write_at = Some(
millis_of(&event.ts)
.unwrap_or(0)
.max(state.last_write_at.unwrap_or(0)),
);
if event.source != Source::Pipeline {
fold_activity(state, event);
fold_refusal(state, event);
fold_invocation(state, event);
fold_session(state, event);
fold_landing_commit(state, event);
return;
}
let payload = &event.payload;
match journal::PipelineKind::from_wire(&event.kind) {
Some(journal::PipelineKind::RunStarted) => {
if let Some(plan) = plan_of(payload) {
state.graph = Graph::from_plan(&plan);
state.plan = Some(plan);
}
}
Some(journal::PipelineKind::ConcurrentAcknowledged) => {}
Some(journal::PipelineKind::NodeReady) => {}
Some(journal::PipelineKind::NodeDispatched) => {
if let Some(node) = &event.labels.node {
state
.recorded
.insert(node.clone(), Recorded::At(NodeStatus::Running));
if let Some(ts) = millis_of(&event.ts) {
state.dispatched_at.insert(node.clone(), ts);
}
state.sessions.remove(node);
state.pending_context.remove(node);
if let Some(dispatched) = state.graph.get_mut(node) {
dispatched.context = None;
}
}
}
Some(journal::PipelineKind::NodeSettled) => {
let Some(node) = &event.labels.node else {
return;
};
let status = payload
.get("status")
.and_then(Value::as_str)
.and_then(NodeStatus::parse);
if let Some(status) = status {
state.recorded.insert(node.clone(), Recorded::At(status));
}
if let Some(outcome) = payload.get("outcome").and_then(Value::as_str) {
state.outcomes.insert(node.clone(), outcome.to_string());
}
if let Some(branch) = payload.get("branch").and_then(Value::as_str) {
state.branches.insert(node.clone(), branch.to_string());
}
if let Some(steps) = payload.get("completed_steps").and_then(Value::as_array) {
state.completed_steps.insert(
node.clone(),
steps
.iter()
.filter_map(Value::as_str)
.map(str::to_string)
.collect(),
);
}
if let Some(url) = payload.get("change_url").and_then(Value::as_str) {
state.change_urls.insert(node.clone(), url.to_string());
}
if let Some(cause) = payload
.get(journal::SETTLED_CAUSE)
.and_then(Value::as_str)
.filter(|cause| crate::engine::is_a_classification(cause))
{
state.causes.insert(node.clone(), cause.to_string());
}
if let Some(head) = payload
.get(journal::SETTLED_HEAD)
.and_then(Value::as_str)
.and_then(crate::vcs::usable)
{
state.heads.insert(node.clone(), head);
} if let Some(landing) = payload
.get(journal::SETTLED_LANDING)
.and_then(Value::as_str)
.and_then(Landing::parse)
{
state.landings.insert(node.clone(), landing);
} if let Some(ts) = millis_of(&event.ts) {
state.settled_at.insert(node.clone(), ts);
}
if let Some(status) = status {
pin_preserved_branch(state, node, status);
}
}
Some(journal::PipelineKind::EditCommitted) => {
let operations = payload
.get("operations")
.and_then(|value| serde_json::from_value::<Vec<Operation>>(value.clone()).ok());
let Some(operations) = operations else {
state.strict = false;
return;
};
for operation in &operations {
edits::apply(&mut state.graph, operation);
match operation {
Operation::HumanAttested { node } => {
state.attestations.insert(node.clone());
state
.recorded
.insert(node.clone(), Recorded::At(NodeStatus::Done));
}
Operation::CompletionRequested { .. } => {}
Operation::RetryRequested {
node, replacement, ..
} => {
state
.recorded
.insert(node.clone(), Recorded::At(NodeStatus::Cancelled));
state.superseded.insert(node.clone(), replacement.clone());
}
Operation::NodeParked { node, by, reason } => {
state
.parks
.insert(node.clone(), edits::Park::of(*by, reason.as_deref()));
let was = state.recorded.get(node).map(|recorded| recorded.status());
let parked = match (was, millis_of(&event.ts)) {
(Some(NodeStatus::Running), Some(since)) => {
Recorded::Cancelling { since }
}
_ => Recorded::At(NodeStatus::Parked),
};
state.recorded.insert(node.clone(), parked);
}
Operation::NodeRequeued { node, .. } => {
state.recorded.remove(node);
state.parks.remove(node);
}
Operation::SettledFromEvidence {
node,
outcome,
evidence: _,
} => {
state
.recorded
.insert(node.clone(), Recorded::At(edits::settled_status(*outcome)));
state
.outcomes
.insert(node.clone(), journal::SETTLED_FROM_EVIDENCE.to_string());
}
Operation::ContextAdded {
node,
note,
delivery: edits::Delivery::Deferred,
} => {
state.pending_context.insert(node.clone(), note.clone());
}
Operation::ContextAdded { .. } => {}
Operation::NoteDelivered {
node,
text,
reached: crate::note::Reached::Carried,
..
} => {
state
.pending_context
.insert(node.clone(), text.as_str().to_string());
}
_ => {}
}
}
}
Some(journal::PipelineKind::ReleaseAdopted) => {
let Some(node) = &event.labels.node else {
return;
};
if payload.get("delivery").and_then(Value::as_str) != Some("next") {
return;
}
let Some(versions) = payload.get("versions") else {
return;
};
let released = crate::release::Released::of_payload(versions);
if released.is_empty() {
return;
}
let note = crate::release::arrival_note(&released);
state.pending_context.insert(node.clone(), note.clone());
if let Some(waiting) = state.graph.get_mut(node) {
waiting.context = Some(note);
}
if state.recorded.get(node).copied().map(Recorded::status)
== Some(NodeStatus::CompleteDraft)
{
state.recorded.remove(node);
}
}
Some(journal::PipelineKind::ReleaseWait | journal::PipelineKind::ReleaseArrived) => {}
Some(journal::PipelineKind::HumanAttested) => {
if let Some(reference) = payload.get("ref").and_then(Value::as_str) {
state.attestations.insert(reference.to_string());
state
.recorded
.insert(reference.to_string(), Recorded::At(NodeStatus::Done));
}
}
Some(journal::PipelineKind::CompletionRequested) => {
if let Some(reason) = payload.get("reason").and_then(Value::as_str) {
state.completion_requests.push(reason.to_string());
}
}
Some(journal::PipelineKind::DriverAdopted) => {
abandon_the_dispatch_in_flight(state);
state
.recorded
.retain(|_, recorded| recorded.status() != NodeStatus::Running);
for recorded in state.recorded.values_mut() {
if matches!(recorded, Recorded::Cancelling { .. }) {
*recorded = Recorded::At(NodeStatus::Parked);
}
}
}
Some(journal::PipelineKind::NodeHeld) => {
if let (Some(node), Some(reasons)) = (
event.labels.node.clone(),
payload.get("reasons").and_then(Value::as_array),
) {
state.holds.insert(node, reasons.clone());
}
} Some(journal::PipelineKind::NodeUnheld) => {
if let Some(node) = event.labels.node.as_ref() {
state.holds.remove(node);
}
}
Some(journal::PipelineKind::DecisionPending) => {
if let Some(reference) = payload.get("reference").and_then(Value::as_str) {
state.decisions_pending.insert(
reference.to_string(),
PendingDecision {
kind: payload
.get("kind")
.and_then(Value::as_str)
.unwrap_or_default()
.to_string(),
unblocks: payload
.get("unblocks")
.and_then(Value::as_array)
.map(|held| {
held.iter()
.filter_map(Value::as_str)
.map(str::to_string)
.collect()
})
.unwrap_or_default(),
},
);
}
}
Some(journal::PipelineKind::DecisionCleared) => {
if let Some(reference) = payload.get("reference").and_then(Value::as_str) {
state.decisions_pending.remove(reference);
}
}
Some(journal::PipelineKind::PlannerSurfaceQueued) => state.surfaces_queued += 1,
Some(journal::PipelineKind::PlannerSurfaced) => {
state.surfaces_read += 1;
state.last_surface_at = millis_of(&event.ts);
}
Some(journal::PipelineKind::RunStopped) => {
state.stop = match journal::StopTeardown::of(payload) {
journal::StopTeardown::Signalled => StopState::WorkersSignalled,
journal::StopTeardown::NothingToStop
| journal::StopTeardown::IdentityDeclined
| journal::StopTeardown::NotAttempted
| journal::StopTeardown::PartlySignalled
| journal::StopTeardown::Refused
| journal::StopTeardown::Elsewhere => StopState::WorkersUndetermined,
};
}
Some(journal::PipelineKind::CrossDagSatisfied) => {
if let (Some(dependency), Some(last)) = (
payload.get("dependency").and_then(Value::as_str),
payload.get("last_seq").and_then(Value::as_u64),
) {
state
.cross_dag_baselines
.entry(dependency.to_string())
.or_insert(last);
}
}
Some(journal::PipelineKind::UpstreamModified) => {
if let Some(dependency) = payload.get("dependency").and_then(Value::as_str) {
*state
.cross_dag_watches
.entry(dependency.to_string())
.or_insert(0) += 1;
if let Some(consumer) = event.labels.node.as_deref() {
state
.cross_dag_reported
.insert((dependency.to_string(), consumer.to_string()));
}
}
}
_ => {}
}
}
fn abandon_the_dispatch_in_flight(state: &mut RunState) {
for (id, session) in state.sessions_in_flight() {
state.sessions.remove(&id);
if let Some(node) = state.graph.get_mut(&id) {
node.branch = Some(session.branch().as_str().to_owned());
}
state.abandoned.insert(id, session);
}
}
fn fold_landing_commit(state: &mut RunState, event: &Envelope) {
let Some(node) = event.labels.node.as_deref() else {
return;
};
let Some(commit) = crate::vcs::landing_commit_of(event) else {
return;
};
state.landing_commits.insert(node.to_string(), commit);
}
fn fold_session(state: &mut RunState, event: &Envelope) {
if event.source != Source::Vcs || !crate::vcs::is_session_opened(&event.kind) {
return;
}
let Some(node) = event.labels.node.as_deref() else {
return;
};
let Some(session) = crate::vcs::DispatchSession::read_from(event) else {
return;
};
state.sessions.insert(node.to_string(), session);
}
fn preserves_its_branch(status: NodeStatus) -> bool {
matches!(
status,
NodeStatus::Failed
| NodeStatus::Cancelled
| NodeStatus::Parked
| NodeStatus::CompleteDraft
)
}
fn pin_preserved_branch(state: &mut RunState, id: &str, status: NodeStatus) {
if !preserves_its_branch(status) {
return;
}
let Some(preserved) = state.branches.get(id).cloned() else {
return;
};
let completed = state.completed_steps.get(id).cloned().unwrap_or_default();
let Some(node) = state.graph.get_mut(id) else {
return;
};
let branch = node.branch.clone().unwrap_or(preserved);
node.resume = Some(crate::plan::Resume {
checkpoint: node.resume.as_ref().and_then(|r| r.checkpoint.clone()),
branch: branch.clone(),
completed_steps: completed,
});
node.branch = Some(branch);
}
fn fold_activity(state: &mut RunState, event: &Envelope) {
let Some(node) = event.labels.node.as_deref() else {
return;
};
let activity = state.activity.entry(node.to_string()).or_default();
let at = millis_of(&event.ts);
if !evidences_progress(event) {
activity.last_heartbeat_at = at.or(activity.last_heartbeat_at);
return;
}
activity.progress = match activity.progress {
Some(progress) => Some(progress.and(at)),
None => Progress::first(at),
};
if event.kind.0 != TURN_ACTIVITY {
return;
}
let text = |key: &str| {
event
.payload
.get(key)
.and_then(Value::as_str)
.unwrap_or_default()
};
let summary = [text("name"), text("detail")]
.into_iter()
.filter(|part| !part.is_empty())
.collect::<Vec<_>>()
.join(" ");
if !summary.is_empty() {
activity.doing = Some(summary);
}
}
fn fold_refusal(state: &mut RunState, event: &Envelope) {
if event.source != Source::Agentgraph || !is_fallback_advanced(&event.kind) {
return;
}
let Some(node) = event.labels.node.as_deref() else {
return;
};
let Ok(advanced) = serde_json::from_value::<oneagentgraph::event::FallbackAdvanced>(
Value::Object(event.payload.clone()),
) else {
return;
};
let refusal = Refusal {
advanced,
member: member_label(event),
records: std::num::NonZeroU64::MIN,
};
let recorded = state.refusals.entry(node.to_string()).or_default();
if let Some(same) = recorded.iter_mut().find(|seen| {
seen.advanced.identity == refusal.advanced.identity
&& seen.advanced.role == refusal.advanced.role
&& seen.advanced.reason == refusal.advanced.reason
&& seen.advanced.turn == refusal.advanced.turn
&& seen.member == refusal.member
}) {
same.records = same.records.saturating_add(1);
return;
}
recorded.push(refusal);
}
fn fold_invocation(state: &mut RunState, event: &Envelope) {
if event.source != Source::Agentgraph || !is_oneharness_session(&event.kind) {
return;
}
let Some(node) = event.labels.node.as_deref() else {
return;
};
let Ok(session) = serde_json::from_value::<oneagentgraph::event::OneharnessSession>(
Value::Object(event.payload.clone()),
) else {
return;
};
let served = Served {
session,
member: member_label(event),
};
state
.served
.entry(node.to_string())
.or_default()
.push(served);
}
fn member_label(event: &Envelope) -> MemberLabel {
match event.labels.extra.get("member") {
None => MemberLabel::Unstamped,
Some(Value::String(member)) => MemberLabel::Named(member.clone()),
Some(_) => MemberLabel::Unreadable,
}
}
fn plan_of(payload: &serde_json::Map<String, Value>) -> Option<Plan> {
payload
.get("plan")
.and_then(|value| serde_json::from_value::<Plan>(value.clone()).ok())
}
pub fn millis_of(ts: &str) -> Option<u64> {
let bytes = ts.as_bytes();
if bytes.len() != 24 {
return None;
}
for (at, separator) in [
(4, b'-'),
(7, b'-'),
(10, b'T'),
(13, b':'),
(16, b':'),
(19, b'.'),
(23, b'Z'),
] {
if bytes[at] != separator {
return None;
}
}
let field = |from: usize, to: usize| -> Option<i64> {
let text = ts.get(from..to)?;
if !text.bytes().all(|byte| byte.is_ascii_digit()) {
return None;
}
text.parse().ok()
};
let (year, month, day) = (field(0, 4)?, field(5, 7)?, field(8, 10)?);
let (hour, minute, second) = (field(11, 13)?, field(14, 16)?, field(17, 19)?);
let ms = field(20, 23)?;
if !(1..=12).contains(&month)
|| !(1..=days_in_month(year, month)).contains(&day)
|| hour > 23
|| minute > 59
|| second > 60
{
return None;
}
let days = days_from_civil(year, month, day);
let total = days * 86_400 + hour * 3_600 + minute * 60 + second;
u64::try_from(total.checked_mul(1_000)?.checked_add(ms)?).ok()
}
fn days_in_month(year: i64, month: i64) -> i64 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 if year % 4 == 0 && (year % 100 != 0 || year % 400 == 0) => 29,
2 => 28,
_ => 0,
}
}
fn days_from_civil(year: i64, month: i64, day: i64) -> i64 {
let y = if month <= 2 { year - 1 } else { year };
let era = if y >= 0 { y } else { y - 399 } / 400;
let yoe = y - era * 400;
let mp = if month > 2 { month - 3 } else { month + 9 };
let doy = (153 * mp + 2) / 5 + day - 1;
let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
era * 146_097 + doe - 719_468
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::{Labels, ENVELOPE_VERSION};
use crate::journal::{labels, payload};
use crate::plan::{Node, PLAN_SCHEMA_VERSION};
use serde_json::json;
fn agent(id: &str, deps: &[&str]) -> Node {
Node {
id: id.into(),
persona: Some("engineer".into()),
task: Some("## What\ndo it".into()),
deps: deps.iter().map(|d| (*d).to_string()).collect(),
..Node::default()
}
}
fn plan_of_nodes(nodes: Vec<Node>) -> Plan {
Plan {
schema_version: PLAN_SCHEMA_VERSION,
goal: None,
name: Some("demo".into()),
concurrency: 4,
tasks: nodes,
}
}
fn pipeline(
kind: journal::PipelineKind,
seq: u64,
node: Option<&str>,
fields: &[(&str, Value)],
) -> Envelope {
Envelope {
v: ENVELOPE_VERSION,
ts: crate::sys::rfc3339_from_millis(1_786_000_000_000 + seq * 1_000),
stream: "s".into(),
seq,
source: Source::Pipeline,
kind: kind.into(),
phase: None,
labels: Labels {
node: node.map(str::to_string),
..labels("demo", None)
},
payload: payload(fields),
artifacts: Vec::new(),
}
}
#[test]
fn a_timestamp_round_trips_through_the_envelope_format() {
for millis in [0u64, 1_786_296_585_678, 1_709_164_800_000] {
let rendered = crate::sys::rfc3339_from_millis(millis);
assert_eq!(millis_of(&rendered), Some(millis), "{rendered}");
}
assert_eq!(millis_of("nope"), None);
assert_eq!(millis_of("2026-08-08T13:29:45.678+00:00"), None);
assert_eq!(millis_of("2026-13-08T13:29:45.678Z"), None);
assert_eq!(millis_of("2026-08-00T13:29:45.678Z"), None);
assert_eq!(millis_of("20x6-08-08T13:29:45.678Z"), None);
}
#[test]
fn a_timestamp_shaped_string_that_is_not_a_time_carries_no_timing_evidence() {
assert_eq!(millis_of("2026-08-08 13:29:45.678Z"), None);
assert_eq!(millis_of("2026/08/08T13:29:45.678Z"), None);
assert_eq!(millis_of("2026-08-08T13-29-45.678Z"), None);
assert_eq!(millis_of("2026-08-08T13:29:45,678Z"), None);
assert_eq!(millis_of("2026-08-08T+3:29:45.678Z"), None);
assert_eq!(millis_of("+026-08-08T13:29:45.678Z"), None);
assert_eq!(millis_of("2026-08-08T24:29:45.678Z"), None);
assert_eq!(millis_of("2026-08-08T13:60:45.678Z"), None);
assert_eq!(millis_of("2026-08-08T13:29:61.678Z"), None);
assert!(millis_of("2026-08-08T23:59:60.000Z").is_some());
assert_eq!(millis_of("2026-02-31T00:00:00.000Z"), None);
assert_eq!(millis_of("2026-04-31T00:00:00.000Z"), None);
assert_eq!(
millis_of("2026-02-29T00:00:00.000Z"),
None,
"2026 is not a leap year"
);
assert!(millis_of("2024-02-29T00:00:00.000Z").is_some(), "2024 is");
assert_eq!(millis_of("2100-02-29T00:00:00.000Z"), None, "2100 is not");
assert!(millis_of("2000-02-29T00:00:00.000Z").is_some(), "2000 is");
}
#[test]
fn a_dispatch_deaths_cause_and_commit_are_folded_only_where_they_are_usable() {
let plan = plan_of_nodes(vec![agent("good", &[]), agent("forged", &[])]);
let settled = |seq: u64, node: &str, cause: Value, head: Value| {
pipeline(
journal::PipelineKind::NodeSettled,
seq,
Some(node),
&[
("status", json!("failed")),
("outcome", json!(crate::engine::DISPATCH_DIED)),
(journal::SETTLED_CAUSE, cause),
(journal::SETTLED_HEAD, head),
],
)
};
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
settled(1, "good", json!("rate_limit"), json!("abc123")),
settled(
2,
"forged",
json!("the harness said a great many things about this"),
json!("abc123\n ship done"),
),
]);
assert_eq!(
state.causes.get("good").map(String::as_str),
Some("rate_limit")
);
assert_eq!(state.heads.get("good").map(String::as_str), Some("abc123"));
assert_eq!(
state.causes.get("forged"),
None,
"a classification this build cannot use was folded anyway"
);
assert_eq!(
state.heads.get("forged"),
None,
"a commit carrying a line of its own was folded onto a view"
);
assert_eq!(
state.outcomes.get("forged").map(String::as_str),
Some(crate::engine::DISPATCH_DIED)
);
}
#[test]
fn a_landing_outlives_its_dispatch_moves_only_on_a_re_settlement_and_an_unreadable_one_records_nothing(
) {
let plan = plan_of_nodes(vec![agent("open", &[]), agent("guessy", &[])]);
let settled = |seq: u64, node: &str, landing: Value| {
pipeline(
journal::PipelineKind::NodeSettled,
seq,
Some(node),
&[
("status", json!("done")),
("outcome", json!("change-open")),
(journal::SETTLED_LANDING, landing),
],
)
};
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
settled(1, "open", json!("unlanded")),
settled(2, "guessy", json!("half-landed")),
pipeline(journal::PipelineKind::NodeDispatched, 3, Some("later"), &[]),
pipeline(
journal::PipelineKind::NodeSettled,
4,
Some("later"),
&[("status", json!("done"))],
),
];
let state = fold(&events);
assert_eq!(
state.landings.get("open"),
Some(&Landing::Unlanded),
"the open change stopped being reported while the run carried on"
);
assert_eq!(
state.landings.get("guessy"),
None,
"a landing this build cannot read was folded as one it could"
);
let mut relanded = events;
relanded.push(settled(5, "open", json!("landed")));
assert_eq!(
fold(&relanded).landings.get("open"),
Some(&Landing::Landed),
"a node that settled again did not overwrite its own landing"
);
}
#[test]
fn the_fold_reconstructs_the_graph_the_run_is_executing() {
let plan = plan_of_nodes(vec![agent("build", &[]), agent("ship", &["build"])]);
let retry = Operation::NodeAdded {
node: Box::new(agent("build-2", &[])),
retry_of: Some("build".into()),
};
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::NodeReady, 1, Some("build"), &[]),
pipeline(journal::PipelineKind::NodeDispatched, 2, Some("build"), &[]),
pipeline(
journal::PipelineKind::NodeSettled,
3,
Some("build"),
&[
("status", json!("failed")),
("outcome", json!("gate-failed")),
],
),
pipeline(
journal::PipelineKind::EditCommitted,
4,
None,
&[
("command", json!({"op": "retry"})),
(
"operations",
json!([
Operation::RetryRequested {
node: "build".into(),
replacement: "build-2".into(),
reset: vec!["ship".into()],
},
retry,
]),
),
],
),
];
let state = fold(&events);
assert!(state.strict);
assert!(
state.graph.contains("build-2"),
"the replacement is not in the plan of record"
);
assert_eq!(state.recorded["build"].status(), NodeStatus::Cancelled);
assert_eq!(state.outcomes["build"], "gate-failed");
assert!(state.dispatched_at.contains_key("build"));
assert!(state.settled_at.contains_key("build"));
}
#[test]
fn a_replayed_journal_reconstructs_the_consumes_the_reconciler_compiled() {
let target = |name: &str| {
name.parse::<onevcs::releases::TargetName>()
.expect("a release target name")
};
let consuming_plan = || {
let mut ship = agent("ship", &["engine", "packager"]);
ship.consumes.insert("engine".into(), target("crate"));
ship.consumes.insert("packager".into(), target("wheel"));
plan_of_nodes(vec![
agent("engine", &[]),
agent("packager", &[]),
agent("docs", &[]),
ship,
])
};
for (what, command, recorded) in [
(
"retry",
crate::channel::Command::Retry {
id: "engine".into(),
node: agent("engine-2", &[]),
},
vec![("engine", NodeStatus::Failed)],
),
(
"drop",
crate::channel::Command::Drop {
id: "engine".into(),
dependents: crate::channel::Dependents::Detach,
},
Vec::new(),
),
(
"reparent",
crate::channel::Command::Reparent {
id: "ship".into(),
deps: vec!["packager".into(), "docs".into()],
},
Vec::new(),
),
] {
let plan = consuming_plan();
let mut live = Graph::from_plan(&plan);
let frontier = Frontier {
recorded: recorded
.iter()
.map(|(id, status)| ((*id).to_string(), *status))
.collect(),
..Frontier::default()
};
let operations = edits::compile(
&mut live,
&frontier,
crate::channel::Author::Planner,
&command,
)
.unwrap_or_else(|e| panic!("the {what} is accepted: {e}"));
let mut events = vec![pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
)];
for (seq, (id, _)) in recorded.iter().enumerate() {
let seq = seq as u64 + 1;
events.push(pipeline(
journal::PipelineKind::NodeDispatched,
seq,
Some(id),
&[],
));
events.push(pipeline(
journal::PipelineKind::NodeSettled,
seq + 1,
Some(id),
&[
("status", json!("failed")),
("outcome", json!(crate::engine::DISPATCH_DIED)),
],
));
}
events.push(pipeline(
journal::PipelineKind::EditCommitted,
9,
None,
&[("operations", json!(operations))],
));
let replayed = fold(&events).graph;
assert_eq!(
replayed
.iter()
.map(|node| (node.id.clone(), node.consumes.clone()))
.collect::<BTreeMap<_, _>>(),
live.iter()
.map(|node| (node.id.clone(), node.consumes.clone()))
.collect::<BTreeMap<_, _>>(),
"the {what} the journal replays consumes different targets than the one \
the reconciler compiled"
);
assert!(
replayed.iter().any(|node| !node.consumes.is_empty()),
"the {what} left no target for this comparison to be about"
);
}
}
#[test]
fn an_edit_whose_operations_cannot_be_folded_ends_strict_replay() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[("operations", json!([{"kind": "from-the-future"}]))],
),
];
let state = fold(&events);
assert!(!state.strict, "an unfoldable operation was folded anyway");
}
#[test]
fn a_settled_node_keeps_its_status_and_its_dependent_becomes_ready() {
let plan = plan_of_nodes(vec![agent("build", &[]), agent("ship", &["build"])]);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::NodeDispatched, 1, Some("build"), &[]),
pipeline(
journal::PipelineKind::NodeSettled,
2,
Some("build"),
&[("status", json!("done"))],
),
]);
assert_eq!(state.recorded["build"].status(), NodeStatus::Done);
assert_eq!(
state.statuses()["ship"],
NodeStatus::Ready,
"a dependent did not become ready on its dependency's settlement"
);
}
#[test]
fn a_carried_note_attaches_to_the_next_dispatch_and_is_consumed_by_it() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let note = pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([Operation::ContextAdded {
node: "build".into(),
note: "the gate needs the lockfile".into(),
delivery: edits::Delivery::Deferred,
}]),
)],
);
let started = pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
);
let attached = fold(&[started.clone(), note.clone()]);
assert_eq!(
attached.pending_context["build"],
"the gate needs the lockfile"
);
assert_eq!(
attached
.graph
.get("build")
.expect("build")
.context
.as_deref(),
Some("the gate needs the lockfile"),
"the note did not reach the node it is for"
);
let consumed = fold(&[
started,
note,
pipeline(journal::PipelineKind::NodeDispatched, 2, Some("build"), &[]),
]);
assert!(
!consumed.pending_context.contains_key("build"),
"a note outlived the dispatch that took it"
);
assert_eq!(
consumed.graph.get("build").expect("build").context,
None,
"a note outlived the dispatch that took it"
);
}
#[test]
fn a_note_the_running_turn_took_is_never_owed_to_a_later_dispatch() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([Operation::ContextAdded {
node: "build".into(),
note: "look at the lockfile".into(),
delivery: edits::Delivery::Live,
}]),
)],
),
]);
assert!(state.pending_context.is_empty());
assert_eq!(state.graph.get("build").expect("build").context, None);
}
#[test]
fn only_a_note_no_running_turn_took_is_owed_to_the_nodes_next_dispatch() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let started = pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
);
let delivered = |reached: crate::note::Reached| {
pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([Operation::NoteDelivered {
node: "build".into(),
addressee: crate::note::Addressee::Worker,
text: "the fixture moved".parse().expect("a usable note"),
criterion: None,
reached,
}]),
)],
)
};
let carried = fold(&[started.clone(), delivered(crate::note::Reached::Carried)]);
assert_eq!(carried.pending_context["build"], "the fixture moved");
assert_eq!(
carried
.graph
.get("build")
.expect("build")
.context
.as_deref(),
Some("the fixture moved"),
"a note no turn took did not reach the node it is still owed to"
);
let taken = fold(&[started, delivered(crate::note::Reached::Worker)]);
assert!(
taken.pending_context.is_empty(),
"a note a running turn read was also owed to the dispatch after it"
);
assert_eq!(taken.graph.get("build").expect("build").context, None);
}
#[test]
fn an_arrival_note_that_did_not_reach_a_running_turn_is_owed_to_the_next_dispatch() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let started = pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
);
let versions = json!([{
"identity": "github.com/nickderobertis/onevcs",
"target": "crate",
"version": "0.13.0"
}]);
let adopted = |seq: u64, delivery: &str| {
pipeline(
journal::PipelineKind::ReleaseAdopted,
seq,
Some("build"),
&[
("node", json!("build")),
("delivery", json!(delivery)),
("versions", versions.clone()),
],
)
};
let owed = fold(&[started.clone(), adopted(1, "next")]);
let note = owed
.pending_context
.get("build")
.expect("the note is owed to the next dispatch");
assert!(
note.contains("github.com/nickderobertis/onevcs — crate 0.13.0"),
"{note}"
);
assert_eq!(
owed.graph.get("build").expect("build").context.as_deref(),
Some(note.as_str()),
"the note did not reach the node it is for"
);
let unreadable = fold(&[
started.clone(),
pipeline(
journal::PipelineKind::ReleaseAdopted,
1,
Some("build"),
&[
("node", json!("build")),
("delivery", json!("next")),
("versions", json!([{"identity": "", "target": "crate"}])),
],
),
]);
assert!(unreadable.pending_context.is_empty());
assert_eq!(unreadable.graph.get("build").expect("build").context, None);
let taken = fold(&[started.clone(), adopted(1, "live")]);
assert!(taken.pending_context.is_empty());
assert_eq!(taken.graph.get("build").expect("build").context, None);
let consumed = fold(&[
started,
adopted(1, "next"),
pipeline(journal::PipelineKind::NodeDispatched, 2, Some("build"), &[]),
]);
assert!(!consumed.pending_context.contains_key("build"));
for kind in [
journal::PipelineKind::ReleaseWait,
journal::PipelineKind::ReleaseArrived,
] {
let reported = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan_of_nodes(vec![agent("build", &[])])))],
),
pipeline(kind, 1, Some("build"), &[("node", json!("build"))]),
]);
assert!(
reported.pending_context.is_empty(),
"{kind} changed the graph"
);
assert_eq!(reported.graph.get("build").expect("build").context, None);
}
}
#[test]
fn an_adoption_ends_the_dispatches_the_driver_before_it_left_running() {
let plan = plan_of_nodes(vec![agent("build", &[]), agent("ship", &["build"])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::NodeDispatched, 1, Some("build"), &[]),
];
let held = fold(&events);
assert_eq!(held.recorded["build"].status(), NodeStatus::Running);
assert_eq!(held.statuses()["build"], NodeStatus::Running);
let mut adopted = events;
adopted.push(pipeline(
journal::PipelineKind::DriverAdopted,
2,
None,
&[("adoption", json!(1))],
));
let state = fold(&adopted);
assert!(!state.recorded.contains_key("build"));
assert_eq!(
state.statuses()["build"],
NodeStatus::Ready,
"a node the dead driver left running was not offered to the fresh one"
);
}
fn opened(seq: u64, node: &str, token: &str, branch: &str) -> Envelope {
let session = onevcs::Session {
token: onevcs::SessionToken(token.into()),
worktree: std::path::PathBuf::from("/tmp/worktree"),
branch: branch.into(),
base: "main".into(),
};
Envelope {
seq,
..crate::vcs::session_opened_event(
&session,
&Labels {
node: Some(node.to_string()),
..labels("demo", None)
},
)
}
}
#[test]
fn an_adoption_names_the_dispatch_it_cleared_and_pins_its_node_to_that_branch() {
let plan = plan_of_nodes(vec![agent("service", &[]), agent("audit", &[])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeDispatched,
1,
Some("service"),
&[],
),
opened(2, "service", "s-abc", "onevcs/s-abc"),
pipeline(journal::PipelineKind::NodeDispatched, 3, Some("audit"), &[]),
pipeline(
journal::PipelineKind::DriverAdopted,
4,
None,
&[("adoption", json!(1))],
),
];
let state = fold(&events);
let left = &state.abandoned["service"];
assert_eq!(left.token(), &onevcs::SessionToken("s-abc".into()));
assert_eq!(left.branch().as_str(), "onevcs/s-abc");
assert!(
!state.abandoned.contains_key("audit"),
"a dispatch that opened no session was reported as work left somewhere"
);
assert_eq!(
state
.graph
.get("service")
.and_then(|node| node.branch.clone()),
Some("onevcs/s-abc".to_string()),
"the cleared node was not pinned to the branch its dispatch committed on"
);
assert_eq!(state.statuses()["service"], NodeStatus::Ready);
assert!(state.sessions.is_empty());
}
#[test]
fn a_session_an_earlier_attempt_finished_with_is_not_where_the_next_one_is_working() {
let plan = plan_of_nodes(vec![agent("service", &[])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeDispatched,
1,
Some("service"),
&[],
),
opened(2, "service", "s-first", "onevcs/s-first"),
pipeline(
journal::PipelineKind::NodeSettled,
3,
Some("service"),
&[("status", json!("failed"))],
),
pipeline(
journal::PipelineKind::NodeDispatched,
4,
Some("service"),
&[],
),
pipeline(
journal::PipelineKind::DriverAdopted,
5,
None,
&[("adoption", json!(1))],
),
];
let state = fold(&events);
assert!(
state.abandoned.is_empty(),
"an adoption named a session the current dispatch never opened: {:?}",
state.abandoned
);
}
#[test]
fn a_session_record_this_run_cannot_place_is_left_out_of_the_fold() {
let plan = plan_of_nodes(vec![agent("service", &[])]);
let mut unlabelled = opened(2, "service", "s-abc", "onevcs/s-abc");
unlabelled.labels.node = None;
let mut branchless = opened(3, "service", "s-abc", "");
branchless.payload.remove("branch");
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeDispatched,
1,
Some("service"),
&[],
),
unlabelled,
branchless,
pipeline(
journal::PipelineKind::DriverAdopted,
4,
None,
&[("adoption", json!(1))],
),
]);
assert!(state.abandoned.is_empty(), "{:?}", state.abandoned);
assert!(
state
.graph
.get("service")
.and_then(|node| node.branch.clone())
.is_none(),
"a node was pinned to a branch no record named"
);
}
#[test]
fn an_adoption_ends_a_cancellation_the_driver_before_it_was_waiting_on() {
let plan = plan_of_nodes(vec![agent("sweep", &[])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::NodeDispatched, 1, Some("sweep"), &[]),
pipeline(
journal::PipelineKind::EditCommitted,
2,
None,
&[(
"operations",
json!([Operation::NodeParked {
node: "sweep".into(),
by: crate::channel::Author::Planner,
reason: None
}]),
)],
),
];
assert!(
fold(&events).recorded["sweep"].cancelling_since().is_some(),
"the cancel left nothing for the adoption to end"
);
let mut adopted = events;
adopted.push(pipeline(
journal::PipelineKind::DriverAdopted,
3,
None,
&[("adoption", json!(1))],
));
let state = fold(&adopted);
assert_eq!(
state.recorded["sweep"],
Recorded::At(NodeStatus::Parked),
"the run is still waiting on a dispatch that went with its driver"
);
}
#[test]
fn a_settlement_that_preserved_a_branch_pins_the_node_to_it() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeSettled,
1,
Some("build"),
&[
("status", json!("failed")),
("branch", json!("onepipeline/build")),
("completed_steps", json!(["implement"])),
],
),
]);
let node = state.graph.get("build").expect("build");
assert_eq!(node.branch.as_deref(), Some("onepipeline/build"));
let resume = node.resume.as_ref().expect("the node resumes its branch");
assert_eq!(resume.branch, "onepipeline/build");
assert_eq!(resume.completed_steps, vec!["implement".to_string()]);
}
#[test]
fn a_node_that_completed_is_not_pinned_to_a_branch_to_continue() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeSettled,
1,
Some("build"),
&[
("status", json!("done")),
("branch", json!("onepipeline/build")),
],
),
]);
assert_eq!(state.graph.get("build").expect("build").resume, None);
}
#[test]
fn attestations_completions_surfaces_and_stops_are_all_folded() {
let plan = plan_of_nodes(vec![Node {
id: "approve".into(),
kind: crate::plan::NodeKind::Human,
task: Some("approve it".into()),
..Node::default()
}]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::PlannerSurfaceQueued, 1, None, &[]),
pipeline(journal::PipelineKind::PlannerSurfaced, 2, None, &[]),
pipeline(
journal::PipelineKind::HumanAttested,
3,
None,
&[("ref", json!("approve"))],
),
pipeline(
journal::PipelineKind::CompletionRequested,
4,
None,
&[("reason", json!("verified"))],
),
pipeline(
journal::PipelineKind::UpstreamModified,
5,
Some("consumer"),
&[("dependency", json!("run:o#n"))],
),
pipeline(journal::PipelineKind::RunStopped, 6, None, &[]),
];
let state = fold(&events);
assert_eq!(state.surfaces_queued, 1);
assert_eq!(state.surfaces_read, 1);
assert!(state.last_surface_at.is_some());
assert!(state.attestations.contains("approve"));
assert_eq!(state.recorded["approve"].status(), NodeStatus::Done);
assert_eq!(state.completion_requests, vec!["verified".to_string()]);
assert_eq!(state.cross_dag_watches["run:o#n"], 1);
assert!(state.stop_recorded());
}
#[test]
fn an_identity_declined_stop_leaves_worker_state_undetermined() {
let state = fold(&[pipeline(
journal::PipelineKind::RunStopped,
0,
None,
&[("teardown", json!("identity-declined"))],
)]);
assert_eq!(state.stop, StopState::WorkersUndetermined);
assert!(state.stop_recorded());
}
#[test]
fn a_relayed_sibling_envelope_is_evidence_of_work_and_nothing_more() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let mut relayed = pipeline(
journal::PipelineKind::NodeSettled,
1,
Some("build"),
&[("status", json!("done"))],
);
relayed.source = Source::Agentgraph;
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
relayed,
]);
assert!(
state.recorded.is_empty(),
"a sibling's envelope decided this crate's graph state"
);
assert!(state.last_write_at.is_some());
assert_eq!(
state.activity["build"]
.progress
.expect("the relay counted")
.events(),
1
);
}
#[test]
fn a_relayed_turn_activity_says_what_the_node_is_doing_now() {
let plan = plan_of_nodes(vec![agent("build", &[])]);
let activity = |seq: u64, name: &str, detail: &str| {
let mut event = pipeline(
journal::PipelineKind::NodeDispatched,
seq,
Some("build"),
&[("name", json!(name)), ("detail", json!(detail))],
);
event.source = Source::Agentgraph;
event.kind = crate::event::EventKind(TURN_ACTIVITY.into());
event
};
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(journal::PipelineKind::NodeDispatched, 1, Some("build"), &[]),
activity(2, "Bash", "cargo llvm-cov --workspace"),
activity(3, "Read", "src/engine.rs"),
]);
let seen = &state.activity["build"];
assert_eq!(seen.doing.as_deref(), Some("Read src/engine.rs"));
assert_eq!(
seen.progress.expect("the activities counted").events(),
2,
"the node-dispatched was counted as activity"
);
assert_eq!(
Some(seen.progress.expect("the activities counted").last_at()),
millis_of(&activity(3, "Read", "x").ts)
);
}
#[test]
fn an_invocation_is_recorded_only_where_the_whole_record_reads() {
let published = |member: Option<Value>, node: Option<&str>, payload: Value| {
let mut event = pipeline(journal::PipelineKind::NodeDispatched, 1, node, &[]);
event.source = Source::Agentgraph;
event.kind = crate::event::EventKind("oneharness-session".into());
event.payload = match payload {
Value::Object(fields) => fields,
other => panic!("a payload is not an object: {other:?}"),
};
if let Some(member) = member {
event.labels.extra.insert("member".into(), member);
}
event
};
let whole = || {
json!({
"role": "judge", "turn": 2, "identity": "codex:alternate",
"history_id": "record-1", "history_dir": "/store",
"history_project": "project", "history_session": "record-1",
})
};
let state = fold(&[
published(Some(json!("worker")), Some("build"), whole()),
published(None, Some("build"), whole()),
published(Some(json!(7)), Some("build"), whole()),
]);
assert_eq!(
state.served["build"]
.iter()
.map(|served| served.member.clone())
.collect::<Vec<_>>(),
vec![
MemberLabel::Named("worker".into()),
MemberLabel::Unstamped,
MemberLabel::Unreadable,
]
);
let first = &state.served["build"][0].session;
assert_eq!(first.role, oneagentgraph::event::Role::Judge);
assert_eq!(first.turn, 2);
assert_eq!(first.identity, "codex:alternate");
let mut wrong_kind = published(None, Some("build"), whole());
wrong_kind.kind = crate::event::EventKind("oneharness-sessions".into());
let mut wrong_source = published(None, Some("build"), whole());
wrong_source.source = Source::Vcs;
assert!(fold(&[
wrong_kind,
wrong_source,
published(None, None, whole()),
published(None, Some("build"), json!({"identity": "codex"})),
])
.served
.is_empty());
}
#[test]
fn an_activity_naming_no_tool_does_not_erase_the_one_before_it() {
let mut nameless = pipeline(journal::PipelineKind::NodeDispatched, 3, Some("build"), &[]);
nameless.source = Source::Agentgraph;
nameless.kind = crate::event::EventKind(TURN_ACTIVITY.into());
let mut named = nameless.clone();
named.seq = 2;
named.payload = payload(&[("name", json!("Bash")), ("detail", json!("just check"))]);
let state = fold(&[named, nameless]);
assert_eq!(
state.activity["build"].doing.as_deref(),
Some("Bash just check")
);
assert_eq!(
state.activity["build"]
.progress
.expect("the relays counted")
.events(),
2
);
}
#[test]
fn a_nodes_activity_accumulates_across_the_attempts_it_was_dispatched_for() {
let activity = |seq: u64| {
let mut event = pipeline(
journal::PipelineKind::NodeDispatched,
seq,
Some("build"),
&[],
);
event.source = Source::Agentgraph;
event.kind = crate::event::EventKind(TURN_ACTIVITY.into());
event
};
let state = fold(&[
activity(1),
pipeline(journal::PipelineKind::NodeDispatched, 2, Some("build"), &[]),
activity(3),
]);
assert_eq!(
state.activity["build"]
.progress
.expect("the relays counted")
.events(),
2
);
}
#[test]
fn a_heartbeat_is_folded_as_liveness_rather_than_as_work() {
let relayed = |seq: u64, kind: &str| {
let mut event = pipeline(
journal::PipelineKind::NodeDispatched,
seq,
Some("build"),
&[("name", json!("Bash")), ("detail", json!("just check"))],
);
event.source = Source::Agentgraph;
event.kind = crate::event::EventKind(kind.into());
event
};
let beat = oneagentgraph::event::EventKind::MemberHeartbeat.as_str();
let state = fold(&[
relayed(1, TURN_ACTIVITY),
relayed(2, beat),
relayed(3, beat),
]);
let seen = &state.activity["build"];
let progress = seen.progress.expect("the activity counted");
assert_eq!(progress.events(), 1, "a heartbeat was counted as work");
assert_eq!(
Some(progress.last_at()),
millis_of(&relayed(1, TURN_ACTIVITY).ts),
"a heartbeat advanced the age of the work"
);
assert_eq!(
seen.last_heartbeat_at,
millis_of(&relayed(3, beat).ts),
"the dispatch's liveness was dropped rather than recorded"
);
assert_eq!(seen.doing.as_deref(), Some("Bash just check"));
}
#[test]
fn only_a_cancel_that_left_a_dispatch_behind_records_a_cancellation_in_flight() {
let plan = plan_of_nodes(vec![agent("sweep", &[]), agent("later", &[])]);
let started = pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
);
let park = |seq: u64, node: &str| {
pipeline(
journal::PipelineKind::EditCommitted,
seq,
None,
&[(
"operations",
json!([Operation::NodeParked {
node: node.into(),
by: crate::channel::Author::Planner,
reason: None
}]),
)],
)
};
let dispatched = pipeline(journal::PipelineKind::NodeDispatched, 1, Some("sweep"), &[]);
let state = fold(&[
started.clone(),
dispatched.clone(),
park(2, "sweep"),
park(3, "later"),
]);
assert_eq!(
state.recorded["sweep"],
Recorded::Cancelling {
since: millis_of(&park(2, "sweep").ts).expect("the park is stamped")
},
"a cancel that left a dispatch running recorded no wait"
);
assert_eq!(
state.recorded["later"],
Recorded::At(NodeStatus::Parked),
"a cancel of a node that never started reported a dispatch to wait for"
);
let settled = pipeline(
journal::PipelineKind::NodeSettled,
4,
Some("sweep"),
&[("status", json!("cancelled"))],
);
let state = fold(&[started, dispatched, park(2, "sweep"), settled]);
assert_eq!(
state.recorded["sweep"],
Recorded::At(NodeStatus::Cancelled),
"the settlement left the node still waiting on the dispatch that settled it"
);
}
#[test]
fn parking_and_requeueing_move_the_node_in_and_out_of_the_frontier() {
let plan = plan_of_nodes(vec![agent("sweep", &[])]);
let park = pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([Operation::NodeParked {
node: "sweep".into(),
by: crate::channel::Author::Planner,
reason: None
}]),
)],
);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
park.clone(),
]);
assert_eq!(state.recorded["sweep"].status(), NodeStatus::Parked);
assert!(state.graph.get("sweep").expect("sweep").parked);
let requeue = pipeline(
journal::PipelineKind::EditCommitted,
2,
None,
&[(
"operations",
json!([Operation::NodeRequeued {
node: "sweep".into(),
amend: None
}]),
)],
);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
park,
requeue,
]);
assert!(!state.recorded.contains_key("sweep"));
assert!(!state.graph.get("sweep").expect("sweep").parked);
}
#[test]
fn a_parks_author_and_reason_fold_onto_the_frontier_and_a_requeue_clears_them() {
let disk = "a third very large build would fill the disk this host has 8G left on";
let plan = plan_of_nodes(vec![agent("build", &[])]);
let started = pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
);
let park = pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([Operation::NodeParked {
node: "build".into(),
by: crate::channel::Author::Monitor,
reason: Some(disk.into())
}]),
)],
);
let state = fold(&[started.clone(), park.clone()]);
assert_eq!(
state.frontier().parks.get("build"),
Some(&edits::Park::of(
crate::channel::Author::Monitor,
Some(disk)
)),
"the park's own account of itself did not reach the frontier"
);
let old = pipeline(
journal::PipelineKind::EditCommitted,
1,
None,
&[(
"operations",
json!([{"kind": "node-parked", "node": "build"}]),
)],
);
let before = fold(&[started.clone(), old]);
assert_eq!(
before.frontier().parks.get("build"),
Some(&edits::Park::default())
);
assert_eq!(
before.graph, state.graph,
"a park written before those fields existed replays into another graph"
);
let requeue = pipeline(
journal::PipelineKind::EditCommitted,
2,
None,
&[(
"operations",
json!([Operation::NodeRequeued {
node: "build".into(),
amend: None
}]),
)],
);
let state = fold(&[started, park, requeue]);
assert!(
state.frontier().parks.is_empty(),
"a requeued node is still held by the park it returned from"
);
}
#[test]
fn a_settlement_from_evidence_moves_the_record_and_releases_the_dependents() {
let evidence = "the change merged at 3f9a1c2 while the dispatch was dying";
let plan = plan_of_nodes(vec![agent("publish", &[]), agent("announce", &["publish"])]);
let events = vec![
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeSettled,
1,
Some("publish"),
&[
("status", json!("failed")),
("outcome", json!("task-failed")),
],
),
pipeline(
journal::PipelineKind::EditCommitted,
2,
None,
&[(
"operations",
json!([Operation::SettledFromEvidence {
node: "publish".into(),
outcome: crate::channel::SettleOutcome::Done,
evidence: evidence.into()
}]),
)],
),
];
let state = fold(&events);
assert_eq!(state.recorded["publish"].status(), NodeStatus::Done);
assert_eq!(
state.outcomes["publish"],
journal::SETTLED_FROM_EVIDENCE,
"the settlement reads as one a dispatch reported"
);
assert_eq!(
state.statuses()["announce"],
NodeStatus::Ready,
"the dependent is still held by a settlement that has been corrected"
);
assert_eq!(
state.graph.get("announce").map(|node| node.deps.clone()),
Some(vec!["publish".to_string()])
);
}
#[test]
fn the_frontier_and_derived_statuses_come_off_the_same_fold() {
let plan = plan_of_nodes(vec![agent("build", &[]), agent("ship", &["build"])]);
let state = fold(&[
pipeline(
journal::PipelineKind::RunStarted,
0,
None,
&[("plan", json!(plan))],
),
pipeline(
journal::PipelineKind::NodeSettled,
1,
Some("build"),
&[("status", json!("done"))],
),
]);
assert_eq!(state.frontier().recorded["build"], NodeStatus::Done);
assert_eq!(state.statuses()["ship"], NodeStatus::Ready);
let upstream = |_: &str| Some(NodeStatus::Done);
assert_eq!(state.statuses_with(&upstream)["ship"], NodeStatus::Ready);
}
}