use super::*;
use crate::config::{Action, Event, Workflow};
fn shipped() -> (Workflow, Workflow) {
let basic_raw = crate::template::TEMPLATE
.get_file("workflow.yaml")
.expect("the template ships workflow.yaml")
.contents_utf8()
.expect("workflow.yaml is UTF-8");
let basic = Workflow::parse(basic_raw, Path::new("template/workflow.yaml"))
.expect("the basic agentic loop parses");
let learning = Workflow::parse(
LEARNING_LOOP_YAML,
Path::new("workflows/learning-loop.yaml"),
)
.expect("the learning loop parses");
(basic, learning)
}
#[test]
fn the_learning_loop_is_the_basic_loop_plus_the_reviewer() {
let (basic, learning) = shipped();
let mut rest = learning.clone();
rest.events = basic.events.clone();
assert_eq!(
rest, basic,
"learning-loop.yaml diverged from the basic agentic loop outside \
its events: block — re-derive it from template/workflow.yaml"
);
for (event, actions) in &basic.events {
let mut expected = actions.clone();
if *event == Event::WorkerFlush {
expected.push("dispatch(reviewer)".into());
}
assert_eq!(
Some(&expected),
learning.events.get(event),
"{} diverged",
event.as_str()
);
}
assert_eq!(
learning.actions_for(Event::ReviewerReturn),
vec![Action::StageProposal],
"the reviewer's landing is the one binding the learning loop adds"
);
let added: Vec<_> = learning
.events
.keys()
.filter(|e| !basic.events.contains_key(e))
.collect();
assert_eq!(added, vec![&Event::ReviewerReturn], "one event was added");
}
#[test]
fn prime_seeds_the_learning_loop_beside_the_default() {
let home = TempDir::new().unwrap();
prime(&collapsed(home.path())).unwrap();
let pool = home.path().join(WORKFLOWS_DIR);
assert!(pool.join(BASIC_AGENTIC_LOOP).is_file());
let seeded = pool.join(LEARNING_LOOP);
assert_eq!(fs::read_to_string(&seeded).unwrap(), LEARNING_LOOP_YAML);
fs::write(&seeded, "events: {}\n").unwrap();
prime(&collapsed(home.path())).unwrap();
assert_eq!(fs::read_to_string(&seeded).unwrap(), "events: {}\n");
}
#[test]
fn the_default_still_dispatches_no_reviewer() {
let (basic, _) = shipped();
assert_eq!(
basic.actions_for(Event::WorkerFlush),
vec![Action::Dispatch {
role: "compactor".into(),
with: None,
mode: None,
}],
"the basic agentic loop forks a compactor and nobody else"
);
assert!(basic.actions_for(Event::ReviewerReturn).is_empty());
}
#[test]
fn the_seeded_asset_claims_nothing_the_interpreter_refuses() {
for dead in [
"Vocabulary today",
"interpreter declines",
"until its landing ships",
] {
assert!(
!LEARNING_LOOP_YAML.contains(dead),
"learning-loop.yaml still tells the operator {dead:?}, and every action it \
binds is implemented"
);
}
}