fn supervising_machine(states: &str, transitions: &str) -> String {
format!(
r#"
name: supervising-test
version: 1.0
states:
{states}
transitions:
{transitions}
profiles:
default:
initial: supervising
allowed: [supervising, review, human-review, completed, cancelled]
node_policy:
root: default
default: default
"#
)
}
fn canonical_states() -> &'static str {
r#" supervising:
description: Supervise the subtree
execute_on: descendant-terminal
agent: pi
visits: 12
review:
description: Review
agent: claude-code
human-review:
description: Human call
gating: true
completed:
description: Done
final: true
cancelled:
description: Dropped
final: true"#
}
fn canonical_transitions() -> &'static str {
r#" - { from: supervising, to: human-review, description: Budget exhausted, condition: visitCount >= visits }
- { from: supervising, to: completed, description: Subtree done, condition: openDescendants < 1 }
- { from: supervising, to: supervising, description: Released }
- { from: review, to: completed, description: Reviewed }
- { from: "*", to: cancelled, description: Dropped }"#
}
#[test]
fn accepts_the_canonical_supervisor() {
let yaml = supervising_machine(canonical_states(), canonical_transitions());
let machine = StateMachine::from_yaml_str(&yaml).expect("canonical supervisor is valid");
assert_eq!(
machine.states.get("supervising").and_then(|def| def.execute_on()),
Some(ExecuteOn::DescendantTerminal)
);
assert_eq!(machine.states.get("review").and_then(|def| def.execute_on()), None);
}
#[test]
fn accepts_every_scope_and_event_pair() {
for (value, scope, event) in [
("child-terminal", SupervisionScope::Child, SupervisionEvent::Terminal),
("child-transition", SupervisionScope::Child, SupervisionEvent::Transition),
("descendant-terminal", SupervisionScope::Descendant, SupervisionEvent::Terminal),
(
"descendant-transition",
SupervisionScope::Descendant,
SupervisionEvent::Transition,
),
] {
let yaml = supervising_machine(
&canonical_states()
.replace("execute_on: descendant-terminal", &format!("execute_on: {value}")),
canonical_transitions(),
);
let machine =
StateMachine::from_yaml_str(&yaml).unwrap_or_else(|err| panic!("{value}: {err}"));
let execute_on = machine
.states
.get("supervising")
.and_then(|def| def.execute_on())
.unwrap_or_else(|| panic!("{value} declares a supervisor"));
assert_eq!(execute_on.as_str(), value);
assert_eq!(execute_on.scope(), scope, "{value} scope");
assert_eq!(execute_on.event(), event, "{value} event");
}
}
#[test]
fn rejects_an_unknown_execute_on_value() {
for value in ["subtree", "task", "state", "child", "terminal", "child_terminal"] {
let yaml = supervising_machine(
&canonical_states()
.replace("execute_on: descendant-terminal", &format!("execute_on: {value}")),
canonical_transitions(),
);
let err =
StateMachine::from_yaml_str(&yaml).expect_err("only the four values are legal");
let message = err.to_string();
for legal in [
"child-terminal",
"child-transition",
"descendant-terminal",
"descendant-transition",
] {
assert!(message.contains(legal), "'{value}' must name {legal}; got: {message}");
}
}
}
#[test]
fn rejects_execute_on_on_a_state_with_no_executor() {
let yaml =
supervising_machine(&canonical_states().replace(" agent: pi\n", ""), canonical_transitions());
let err = StateMachine::from_yaml_str(&yaml).expect_err("no executor");
assert!(err.to_string().contains("not agent-bearing"), "got: {err}");
}
#[test]
fn rejects_execute_on_on_a_final_state() {
let states = canonical_states()
.replace(" agent: pi\n", " target: pi:openai:gpt-5\n final: true\n");
let err = StateMachine::from_yaml_str(&supervising_machine(&states, canonical_transitions()))
.expect_err("final supervisor");
assert!(err.to_string().contains("is final and cannot declare 'execute_on'"), "got: {err}");
}
#[test]
fn rejects_execute_on_on_a_gating_state() {
let states = canonical_states().replace(" execute_on: descendant-terminal\n", " execute_on: descendant-terminal\n gating: true\n");
let err = StateMachine::from_yaml_str(&supervising_machine(&states, canonical_transitions()))
.expect_err("gating supervisor");
assert!(err.to_string().contains("is gating and cannot declare 'execute_on'"), "got: {err}");
}
#[test]
fn rejects_execute_on_on_a_program_state() {
let states = canonical_states().replace(" agent: pi\n", " program: \"./check.sh\"\n");
let err = StateMachine::from_yaml_str(&supervising_machine(&states, canonical_transitions()))
.expect_err("program supervisor");
assert!(err.to_string().contains("declares both 'program' and 'execute_on'"), "got: {err}");
}
#[test]
fn rejects_execute_on_on_a_poll_state() {
let states = canonical_states().replace(
" visits: 12\n",
" poll: { interval: 5m, max_attempts: 3 }\n",
);
let err = StateMachine::from_yaml_str(&supervising_machine(&states, canonical_transitions()))
.expect_err("poll supervisor");
assert!(
err.to_string().contains(
"a state has one trigger: `poll:` (time) or `execute_on:` (its subtree)"
),
"got: {err}"
);
}
#[test]
fn rejects_execute_on_combined_with_fanout() {
let states =
canonical_states().replace(" agent: pi\n", " all_targets: [\"pi:openai:gpt-5\", \"codex:openai:gpt-5\"]\n");
let err = StateMachine::from_yaml_str(&supervising_machine(&states, canonical_transitions()))
.expect_err("fanout supervisor");
assert!(err.to_string().contains("not a fanout"), "got: {err}");
}
#[test]
fn rejects_a_supervising_state_without_a_self_loop() {
let transitions = canonical_transitions()
.replace(" - { from: supervising, to: supervising, description: Released }\n", "");
let err = StateMachine::from_yaml_str(&supervising_machine(canonical_states(), &transitions))
.expect_err("no release edge");
assert!(err.to_string().contains("no self-loop transition"), "got: {err}");
}
fn supervision_warnings_for(yaml: &str) -> Vec<String> {
let machine = StateMachine::from_yaml_str(yaml).expect("machine loads");
let rhei =
rhei_core::parse("# Rhei: T\n\n## Tasks\n\n### Task 1: Root\n**State:** supervising\n")
.expect("plan parses");
validate_with_machine(&rhei, &machine).warnings
}
#[test]
fn warns_when_no_transition_finishes_the_supervisor() {
let transitions = canonical_transitions().replace(
" - { from: supervising, to: completed, description: Subtree done, condition: openDescendants < 1 }\n",
"",
);
let warnings = supervision_warnings_for(&supervising_machine(canonical_states(), &transitions));
assert!(
warnings.iter().any(|w| w.contains("no way to finish")),
"expected the no-terminal-edge warning; got: {warnings:?}"
);
}
#[test]
fn warns_when_neither_visits_nor_an_exhaustion_edge_is_declared() {
let states = canonical_states().replace(" visits: 12\n", "");
let transitions = canonical_transitions().replace(
" - { from: supervising, to: human-review, description: Budget exhausted, condition: visitCount >= visits }\n",
"",
);
let warnings = supervision_warnings_for(&supervising_machine(&states, &transitions));
assert!(
warnings.iter().any(|w| w.contains("no safety valve")),
"expected the unbounded-supervisor warning; got: {warnings:?}"
);
}
#[test]
fn warns_when_a_self_loop_has_neither_a_budget_nor_a_counted_exit() {
let states = canonical_states().replace(" visits: 12\n", "");
let transitions = canonical_transitions().replace(
" - { from: supervising, to: human-review, description: Budget exhausted, condition: visitCount >= visits }\n",
"",
);
let warnings = supervision_warnings_for(&supervising_machine(&states, &transitions));
assert!(
warnings.iter().any(|w| w.contains("nothing ends the loop")),
"expected the unbounded-self-loop warning; got: {warnings:?}"
);
let budgeted =
supervision_warnings_for(&supervising_machine(canonical_states(), &transitions));
assert!(
budgeted.iter().all(|w| !w.contains("nothing ends the loop")),
"a `visits:` budget ends the loop; got: {budgeted:?}"
);
}
#[test]
fn the_canonical_supervisor_warns_about_nothing() {
let warnings =
supervision_warnings_for(&supervising_machine(canonical_states(), canonical_transitions()));
assert!(
warnings.iter().all(|w| !w.contains("execute_on")),
"the canonical supervisor is warning-free; got: {warnings:?}"
);
}