use converge_kernel::formation::{
FormationCatalog, FormationTemplate, FormationTemplateQuery, SuggestorCapability,
};
use organism_intent::IntentPacket;
use organism_intent::problem::{ProblemClass, ProblemClassification, classify};
use serde::{Deserialize, Serialize};
use crate::templates::{CostHint, cost_hint_for};
#[derive(Debug, Clone)]
pub struct GuruSelection<'cat> {
pub primary: &'cat FormationTemplate,
pub alternates: Vec<&'cat FormationTemplate>,
pub classification: ProblemClassification,
pub trace: SelectionTrace,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SelectionTrace {
pub problem_class: ProblemClass,
pub matched_keywords: Vec<String>,
pub defaulted: bool,
pub query_keywords: Vec<String>,
pub query_capabilities: Vec<SuggestorCapability>,
pub considered: Vec<String>,
pub scores: Vec<CandidateScore>,
pub primary_id: String,
pub primary_reason: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CandidateScore {
pub template_id: String,
pub catalog_rank: usize,
pub capability_surplus: usize,
pub cost_hint: CostHint,
pub composite: i32,
}
#[derive(Debug, thiserror::Error)]
pub enum GuruError {
#[error("no formation template matches problem class {class} with available capabilities")]
NoMatch { class: ProblemClass },
}
pub struct FormationGuru<'a> {
catalog: &'a FormationCatalog,
}
impl<'a> FormationGuru<'a> {
#[must_use]
pub fn new(catalog: &'a FormationCatalog) -> Self {
Self { catalog }
}
pub fn select(
&self,
intent: &IntentPacket,
available_capabilities: &[SuggestorCapability],
) -> Result<GuruSelection<'a>, GuruError> {
let classification = classify(intent);
let query_keywords = query_keywords_for(&classification);
let mut query = FormationTemplateQuery::new();
for kw in &query_keywords {
query = query.with_keyword(kw.clone());
}
let raw_matches = self.catalog.matches(&query);
let filtered: Vec<&FormationTemplate> = raw_matches
.into_iter()
.filter(|t| host_satisfies(t, available_capabilities))
.collect();
if filtered.is_empty() {
return Err(GuruError::NoMatch {
class: classification.class,
});
}
let mut scored: Vec<(CandidateScore, &FormationTemplate)> = filtered
.iter()
.enumerate()
.map(|(rank, template)| {
let surplus = capability_surplus(template, available_capabilities);
let cost = cost_hint_for(template.id());
let catalog_bonus =
i32::try_from(filtered.len().saturating_sub(rank)).unwrap_or(i32::MAX);
let composite = catalog_bonus * 10
+ i32::try_from(surplus).unwrap_or(0)
+ cost.cheapness_bonus();
(
CandidateScore {
template_id: template.id().to_owned(),
catalog_rank: rank,
capability_surplus: surplus,
cost_hint: cost,
composite,
},
*template,
)
})
.collect();
scored.sort_by_key(|s| std::cmp::Reverse(s.0.composite));
let primary = scored[0].1;
let alternates: Vec<&FormationTemplate> =
scored.iter().skip(1).take(2).map(|(_, t)| *t).collect();
let considered: Vec<String> = scored.iter().map(|(s, _)| s.template_id.clone()).collect();
let candidate_scores: Vec<CandidateScore> = scored.iter().map(|(s, _)| s.clone()).collect();
let primary_reason = if classification.defaulted {
format!(
"no problem-class keywords matched; defaulted to {} and picked {} (composite {})",
classification.class,
primary.id(),
candidate_scores[0].composite,
)
} else {
format!(
"{} matched {} → top template {} (composite {}, surplus {}, cost {:?})",
classification.class,
classification
.matched_keywords
.first()
.map_or("(no kw)", String::as_str),
primary.id(),
candidate_scores[0].composite,
candidate_scores[0].capability_surplus,
candidate_scores[0].cost_hint,
)
};
let trace = SelectionTrace {
problem_class: classification.class,
matched_keywords: classification.matched_keywords.clone(),
defaulted: classification.defaulted,
query_keywords,
query_capabilities: available_capabilities.to_vec(),
considered,
scores: candidate_scores,
primary_id: primary.id().to_owned(),
primary_reason,
};
Ok(GuruSelection {
primary,
alternates,
classification,
trace,
})
}
}
fn capability_surplus(template: &FormationTemplate, available: &[SuggestorCapability]) -> usize {
let required = &template.metadata().required_capabilities;
available
.iter()
.filter(|cap| !required.contains(cap))
.count()
}
fn query_keywords_for(classification: &ProblemClassification) -> Vec<String> {
let mut keywords = vec![classification.class.as_str().to_owned()];
for kw in &classification.matched_keywords {
if !keywords.contains(kw) {
keywords.push(kw.clone());
}
}
keywords
}
fn host_satisfies(template: &FormationTemplate, available: &[SuggestorCapability]) -> bool {
template
.metadata()
.required_capabilities
.iter()
.all(|cap| available.contains(cap))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::templates::standard_formation_catalog;
use chrono::{Duration, Utc};
fn intent(outcome: &str) -> IntentPacket {
IntentPacket::new(outcome, Utc::now() + Duration::hours(1))
}
fn caps() -> Vec<SuggestorCapability> {
vec![
SuggestorCapability::LlmReasoning,
SuggestorCapability::Analytics,
SuggestorCapability::PolicyEnforcement,
SuggestorCapability::KnowledgeRetrieval,
SuggestorCapability::HumanInTheLoop,
]
}
#[test]
fn picks_decision_for_decision_intent() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(&intent("decide which vendor to approve"), &caps())
.expect("decision intent matches");
assert_eq!(selection.primary.id(), "organism-decision");
assert_eq!(selection.classification.class, ProblemClass::Decision);
assert_eq!(selection.trace.primary_id, "organism-decision");
}
#[test]
fn picks_diligence_for_vetting_intent() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(&intent("vet the acquisition target end-to-end"), &caps())
.expect("diligence intent matches");
assert_eq!(selection.primary.id(), "organism-diligence");
}
#[test]
fn picks_research_for_open_ended_intent() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(&intent("research the competitive landscape"), &caps())
.expect("research intent matches");
assert_eq!(selection.primary.id(), "organism-research");
}
#[test]
fn picks_decision_for_incident_intent() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(
&intent("respond to the production incident and stabilize the system"),
&caps(),
)
.expect("incident intent must match a template");
assert_eq!(selection.classification.class, ProblemClass::Incident);
assert_eq!(selection.primary.id(), "organism-decision");
assert!(!selection.trace.defaulted);
assert!(
selection
.trace
.matched_keywords
.iter()
.any(|k| k == "incident")
);
}
#[test]
fn picks_research_for_strategy_intent() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(
&intent("set our 3-year strategy and define the long-term vision"),
&caps(),
)
.expect("strategy intent must match a template");
assert_eq!(selection.classification.class, ProblemClass::Strategy);
assert_eq!(selection.primary.id(), "organism-research");
assert!(!selection.trace.defaulted);
assert!(
selection
.trace
.matched_keywords
.iter()
.any(|k| k == "strategy")
);
}
#[test]
fn missing_capabilities_filters_template_out() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let limited = vec![
SuggestorCapability::LlmReasoning,
SuggestorCapability::Analytics,
SuggestorCapability::PolicyEnforcement,
SuggestorCapability::KnowledgeRetrieval,
];
let result = guru.select(&intent("vet the acquisition target"), &limited);
if let Ok(selection) = result {
assert_ne!(selection.primary.id(), "organism-diligence");
}
}
#[test]
fn defaulted_classification_records_defaulted_in_trace() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(&intent("doing the thing today"), &caps())
.expect("default classification still matches a template");
assert!(selection.trace.defaulted);
assert_eq!(selection.classification.class, ProblemClass::Decision);
assert!(selection.trace.primary_reason.contains("defaulted"));
}
#[test]
fn alternates_capped_at_two() {
let catalog = standard_formation_catalog();
let guru = FormationGuru::new(&catalog);
let selection = guru
.select(&intent("decide and evaluate the proposal"), &caps())
.expect("matches");
assert!(selection.alternates.len() <= 2);
}
}