use crate::trit::{decide, Trit};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Subtask {
pub label: String,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct PlannedTask {
pub label: String,
pub confidence: f64,
pub trit: i8,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "mcp", derive(schemars::JsonSchema))]
pub struct Plan {
pub goal: String,
pub action_queue: Vec<PlannedTask>,
pub hold_queue: Vec<PlannedTask>,
pub overall_trit: i8,
}
pub fn plan(goal: impl Into<String>, subtasks: &[Subtask]) -> Plan {
let mut action_queue = Vec::new();
let mut hold_queue = Vec::new();
let mut trits = Vec::new();
for s in subtasks {
let evidence = (s.confidence - 0.5) * 2.0;
let (trit, _conf) = decide(&[evidence]);
trits.push(evidence);
let pt = PlannedTask {
label: s.label.clone(),
confidence: s.confidence,
trit: trit.as_i8(),
};
if trit == Trit::Reject {
hold_queue.push(pt);
} else {
action_queue.push(pt);
}
}
let (overall, _c) = decide(&trits);
Plan {
goal: goal.into(),
action_queue,
hold_queue,
overall_trit: overall.as_i8(),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn low_confidence_steps_go_to_hold_queue() {
let subtasks = vec![
Subtask { label: "decompile".into(), confidence: 0.95 },
Subtask { label: "get human sign-off".into(), confidence: 0.15 },
];
let p = plan("ship it", &subtasks);
assert_eq!(p.action_queue.len(), 1);
assert_eq!(p.hold_queue.len(), 1);
assert_eq!(p.hold_queue[0].label, "get human sign-off");
}
#[test]
fn matches_observed_runbook_split() {
let subtasks = vec![
Subtask { label: "decompile".into(), confidence: 0.95 },
Subtask { label: "classify severity".into(), confidence: 0.85 },
Subtask { label: "red-flag vs magenkraempfe".into(), confidence: 0.55 },
Subtask { label: "pick price tier".into(), confidence: 0.40 },
Subtask { label: "drei fragen block".into(), confidence: 0.80 },
Subtask { label: "thread consolidation".into(), confidence: 0.30 },
Subtask { label: "human sign-off".into(), confidence: 0.15 },
];
let p = plan("ship a companion R1", &subtasks);
assert_eq!(p.action_queue.len(), 5);
assert_eq!(p.hold_queue.len(), 2);
assert_eq!(p.hold_queue[0].label, "thread consolidation");
assert_eq!(p.hold_queue[1].label, "human sign-off");
}
}