use super::*;
fn soul() -> String {
let raw = crate::template::TEMPLATE
.get_file("souls/planner.md")
.expect("the template ships souls/planner.md")
.contents_utf8()
.expect("souls/planner.md is UTF-8");
raw.split_whitespace().collect::<Vec<_>>().join(" ")
}
fn providers() -> crate::config::PerRepoProviders {
let raw = crate::template::TEMPLATE
.get_file("providers.yaml")
.expect("the template ships providers.yaml")
.contents_utf8()
.expect("providers.yaml is UTF-8");
crate::config::PerRepoProviders::parse(raw, Path::new("template/providers.yaml"))
.expect("the shipped template parses")
}
fn asserts(text: &str, phrases: &[&str], what: &str) {
for phrase in phrases {
assert!(
text.contains(phrase),
"the {what} no longer says {phrase:?} — it reads:\n{text}"
);
}
}
#[test]
fn the_planner_grant_is_its_confinement() {
let roles = providers().roles;
let planner = &roles["planner"];
let mut granted = planner.tools.clone();
granted.sort();
assert_eq!(granted, vec!["load_skill", "read_file", "search_history"]);
for withheld in [
"apply_patch",
"bash",
"python",
"dispatch",
"message",
"remember",
] {
assert!(
!planner.tools.iter().any(|t| t == withheld),
"the planner may not call {withheld}: the grant is the whole confinement"
);
}
}
#[test]
fn the_planner_thinks_on_the_workers_model() {
let roles = providers().roles;
assert_eq!(roles["planner"].model, roles["worker"].model);
}
#[test]
fn the_planner_soul_says_what_a_plan_is_and_why_it_cannot_act() {
asserts(
&soul(),
&[
"You hold no shell, no patch tool, no dispatch and no message",
"What you found",
"What you would do",
"How it would be verified",
"What you could not see",
"Do not decide",
"Do not pad",
"Do not plan around your own confinement",
"Your terminal response **is** the plan",
],
"planner soul",
);
}
#[test]
fn no_shipped_workflow_binds_the_planner() {
for (name, raw) in [
(
"template/workflow.yaml",
crate::template::TEMPLATE
.get_file("workflow.yaml")
.expect("the template ships workflow.yaml")
.contents_utf8()
.expect("utf8"),
),
("workflows/learning-loop.yaml", LEARNING_LOOP_YAML),
] {
let workflow = crate::config::Workflow::parse(raw, Path::new(name))
.unwrap_or_else(|e| panic!("{name} parses: {e}"));
for (event, actions) in workflow.typed_events() {
for action in actions {
assert!(
!matches!(&action, crate::config::action::Action::Dispatch { role, .. } if role == "planner"),
"{name} binds dispatch(planner) at {event:?}; the role ships unbound"
);
}
}
}
}
#[test]
fn the_planner_manifest_entry_composes_what_it_plans_against() {
let raw = crate::template::TEMPLATE
.get_file("manifest.yaml")
.expect("the template ships manifest.yaml")
.contents_utf8()
.expect("manifest.yaml is UTF-8");
let shipped =
crate::config::manifest::Manifest::parse(raw, Path::new("template/manifest.yaml"))
.expect("the shipped template parses");
let planner = &shipped.roles["planner"];
assert!(planner.pinned.contains(&crate::facts::FILE.to_string()));
assert!(planner.pinned.contains(&"descriptions/**".to_string()));
assert_eq!(planner.order, vec!["summary/**", "skills/**"]);
}