use organism_pack::{
AgentMeta, CapabilityRequirement, ContextKey, IntentBinding, IntentPacket, IntentResolver,
InvariantClass, InvariantMeta, PackProfile, PackRequirement, ResolutionLevel, Reversibility,
};
#[derive(Debug, Clone)]
pub struct RegisteredPack {
pub name: String,
pub description: String,
pub fact_prefixes: Vec<String>,
pub agent_names: Vec<String>,
pub invariant_names: Vec<String>,
pub agent_count: usize,
pub invariant_count: usize,
pub context_keys_read: Vec<ContextKey>,
pub context_keys_written: Vec<ContextKey>,
pub has_acceptance_invariants: bool,
pub profile: PackProfile,
}
#[derive(Debug, Clone)]
pub struct RegisteredCapability {
pub name: String,
pub description: String,
}
#[derive(Debug, Clone, Default)]
pub struct Registry {
packs: Vec<RegisteredPack>,
capabilities: Vec<RegisteredCapability>,
}
impl Registry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register_pack_with_profile_if_missing(
&mut self,
name: &'static str,
agents: &[AgentMeta],
invariants: &[InvariantMeta],
profile: &PackProfile,
) {
if self.packs.iter().any(|pack| pack.name == name) {
return;
}
self.register_pack_with_profile(name, agents, invariants, profile);
}
pub fn register_pack_with_profile(
&mut self,
name: impl Into<String>,
agents: &[AgentMeta],
invariants: &[InvariantMeta],
profile: &PackProfile,
) {
let name = name.into();
let fact_prefixes: Vec<String> = agents
.iter()
.map(|a| a.fact_prefix.to_string())
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
let agent_names = agents.iter().map(|a| a.name.to_string()).collect();
let invariant_names = invariants.iter().map(|i| i.name.to_string()).collect();
let description = agents
.iter()
.map(|a| a.description)
.collect::<Vec<_>>()
.join("; ");
let context_keys_read: Vec<ContextKey> = agents
.iter()
.flat_map(|a| a.dependencies.iter().copied())
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
let context_keys_written: Vec<ContextKey> = agents
.iter()
.map(|a| a.target_key)
.collect::<std::collections::HashSet<_>>()
.into_iter()
.collect();
let has_acceptance_invariants = invariants
.iter()
.any(|i| i.class == InvariantClass::Acceptance);
self.packs.push(RegisteredPack {
name,
description,
fact_prefixes,
agent_names,
invariant_names,
agent_count: agents.len(),
invariant_count: invariants.len(),
context_keys_read,
context_keys_written,
has_acceptance_invariants,
profile: profile.clone(),
});
}
pub fn register_pack(
&mut self,
name: impl Into<String>,
agents: &[AgentMeta],
invariants: &[InvariantMeta],
) {
self.register_pack_with_profile(name, agents, invariants, &PackProfile::default());
}
pub fn register_pack_raw(&mut self, pack: RegisteredPack) {
self.packs.push(pack);
}
pub fn register_capability(&mut self, name: impl Into<String>, description: impl Into<String>) {
self.capabilities.push(RegisteredCapability {
name: name.into(),
description: description.into(),
});
}
#[must_use]
pub fn packs_for_prefix(&self, prefix: &str) -> Vec<&RegisteredPack> {
self.packs
.iter()
.filter(|p| p.fact_prefixes.iter().any(|fp| fp == prefix))
.collect()
}
#[must_use]
pub fn packs_for_prefixes(&self, prefixes: &[&str]) -> Vec<&RegisteredPack> {
self.packs
.iter()
.filter(|p| {
p.fact_prefixes
.iter()
.any(|fp| prefixes.contains(&fp.as_str()))
})
.collect()
}
#[must_use]
pub fn packs_for_entity(&self, entity: &str) -> Vec<&RegisteredPack> {
let entity = entity.to_lowercase();
self.packs
.iter()
.filter(|p| p.profile.entities.iter().any(|e| *e == entity))
.collect()
}
#[must_use]
pub fn packs_for_keyword(&self, keyword: &str) -> Vec<&RegisteredPack> {
let keyword = keyword.to_lowercase();
self.packs
.iter()
.filter(|p| p.profile.keywords.iter().any(|k| *k == keyword))
.collect()
}
#[must_use]
pub fn packs_writing_key(&self, key: ContextKey) -> Vec<&RegisteredPack> {
self.packs
.iter()
.filter(|p| p.context_keys_written.contains(&key))
.collect()
}
#[must_use]
pub fn packs_handling_irreversible(&self) -> Vec<&RegisteredPack> {
self.packs
.iter()
.filter(|p| p.profile.handles_irreversible && p.has_acceptance_invariants)
.collect()
}
#[must_use]
pub fn packs_requiring_capability(&self, capability: &str) -> Vec<&RegisteredPack> {
self.packs
.iter()
.filter(|p| p.profile.required_capabilities.contains(&capability))
.collect()
}
#[must_use]
pub fn search_packs(&self, query: &str) -> Vec<&RegisteredPack> {
let query = query.to_lowercase();
self.packs
.iter()
.filter(|p| {
p.name.to_lowercase().contains(&query)
|| p.description.to_lowercase().contains(&query)
|| p.profile.keywords.iter().any(|k| k.contains(&query))
|| p.profile.entities.iter().any(|e| e.contains(&query))
})
.collect()
}
#[must_use]
pub fn has_capability(&self, name: &str) -> bool {
self.capabilities.iter().any(|c| c.name == name)
}
#[must_use]
pub fn search_capabilities(&self, query: &str) -> Vec<&RegisteredCapability> {
let query = query.to_lowercase();
self.capabilities
.iter()
.filter(|c| {
c.name.to_lowercase().contains(&query)
|| c.description.to_lowercase().contains(&query)
})
.collect()
}
#[must_use]
pub fn packs(&self) -> &[RegisteredPack] {
&self.packs
}
#[must_use]
pub fn capabilities(&self) -> &[RegisteredCapability] {
&self.capabilities
}
}
pub struct StructuralResolver<'a> {
registry: &'a Registry,
}
const STRONG_CONTEXT_FLOW_ANCHOR_CONFIDENCE: f64 = 0.75;
impl<'a> StructuralResolver<'a> {
#[must_use]
pub fn new(registry: &'a Registry) -> Self {
Self { registry }
}
}
#[allow(clippy::too_many_lines)]
impl IntentResolver for StructuralResolver<'_> {
fn level(&self) -> ResolutionLevel {
ResolutionLevel::Structural
}
fn resolve(&self, intent: &IntentPacket, current: &IntentBinding) -> IntentBinding {
let mut binding = current.clone();
let already_bound: std::collections::HashSet<String> =
binding.packs.iter().map(|p| p.pack_name.clone()).collect();
let mut matched: Vec<(String, String, f64)> = Vec::new();
let prefixes = extract_fact_prefixes(&intent.context);
for prefix in &prefixes {
for pack in self.registry.packs_for_prefix(prefix) {
if !already_bound.contains(&pack.name) {
matched.push((
pack.name.clone(),
format!("fact prefix '{prefix}' → pack '{}'", pack.name),
0.9,
));
}
}
}
for constraint in &intent.constraints {
for pack in &self.registry.packs {
if pack.invariant_names.iter().any(|i| constraint.contains(i))
&& !already_bound.contains(&pack.name)
{
matched.push((
pack.name.clone(),
format!("constraint '{constraint}' → invariant in '{}'", pack.name),
0.85,
));
}
}
}
let entities = extract_entities(&intent.outcome);
for entity in &entities {
for pack in self.registry.packs_for_entity(entity) {
if !already_bound.contains(&pack.name) {
matched.push((
pack.name.clone(),
format!("entity '{entity}' → pack '{}'", pack.name),
0.75,
));
}
}
}
let keywords = extract_keywords(&intent.outcome);
for keyword in &keywords {
for pack in self.registry.packs_for_keyword(keyword) {
if !already_bound.contains(&pack.name) {
matched.push((
pack.name.clone(),
format!("keyword '{keyword}' → pack '{}'", pack.name),
0.65,
));
}
}
}
if intent.reversibility == Reversibility::Irreversible {
for pack in self.registry.packs_handling_irreversible() {
if !already_bound.contains(&pack.name) {
matched.push((
pack.name.clone(),
format!(
"irreversible intent → pack '{}' has Acceptance invariants",
pack.name
),
0.7,
));
}
}
}
let needed_keys = extract_context_keys(&intent.context);
let anchored_pack_names = already_bound
.iter()
.cloned()
.chain(
matched
.iter()
.filter(|(_, _, confidence)| {
*confidence >= STRONG_CONTEXT_FLOW_ANCHOR_CONFIDENCE
})
.map(|(pack_name, _, _)| pack_name.clone()),
)
.collect::<std::collections::HashSet<_>>();
let anchored_packs = anchored_pack_names
.iter()
.filter_map(|pack_name| {
self.registry
.packs
.iter()
.find(|pack| &pack.name == pack_name)
})
.collect::<Vec<_>>();
let anchor_written_keys = anchored_packs
.iter()
.flat_map(|pack| pack.context_keys_written.iter().copied())
.filter(|key| needed_keys.contains(key))
.collect::<std::collections::HashSet<_>>();
for pack in &anchored_packs {
for key in pack
.context_keys_written
.iter()
.filter(|key| needed_keys.contains(key))
{
if !already_bound.contains(&pack.name) {
matched.push((
pack.name.clone(),
format!(
"anchored context flow → pack '{}' writes needed {key:?}",
pack.name
),
0.78,
));
}
}
}
if needed_keys.len() >= 2 && !anchor_written_keys.is_empty() {
for pack in &self.registry.packs {
if already_bound.contains(&pack.name) || anchored_pack_names.contains(&pack.name) {
continue;
}
let Some(read_key) = pack
.context_keys_read
.iter()
.copied()
.find(|key| anchor_written_keys.contains(key))
else {
continue;
};
let Some(write_key) = pack
.context_keys_written
.iter()
.copied()
.find(|key| needed_keys.contains(key) && *key != read_key)
else {
continue;
};
matched.push((
pack.name.clone(),
format!("context flow bridge {read_key:?} → {write_key:?} from anchored packs"),
0.72,
));
}
}
let forbidden_keywords: Vec<String> = intent
.forbidden
.iter()
.map(|f| f.action.to_lowercase())
.collect();
matched.retain(|(pack_name, _, _)| {
if let Some(pack) = self.registry.packs.iter().find(|p| &p.name == pack_name) {
!pack.profile.keywords.iter().any(|k| {
forbidden_keywords
.iter()
.any(|f| f.contains(k) || k.contains(f.as_str()))
})
} else {
true
}
});
let mut needed_capabilities: Vec<(String, String)> = Vec::new();
let all_pack_names: Vec<String> = already_bound
.iter()
.chain(matched.iter().map(|(name, _, _)| name))
.cloned()
.collect();
for pack_name in &all_pack_names {
if let Some(pack) = self.registry.packs.iter().find(|p| &p.name == pack_name) {
for cap in pack.profile.required_capabilities {
if !binding.capabilities.iter().any(|c| c.capability == *cap)
&& !needed_capabilities.iter().any(|(c, _)| c == *cap)
{
needed_capabilities.push((
(*cap).to_string(),
format!("required by pack '{pack_name}'"),
));
}
}
}
}
let mut best: std::collections::HashMap<String, (String, f64)> =
std::collections::HashMap::new();
for (pack_name, reason, confidence) in matched {
let entry = best
.entry(pack_name.clone())
.or_insert((reason.clone(), 0.0));
if confidence > entry.1 {
*entry = (reason, confidence);
}
}
for (pack_name, (reason, confidence)) in best {
binding.packs.push(PackRequirement {
pack_name,
reason,
confidence: converge_pack::UnitInterval::clamped(confidence),
source: ResolutionLevel::Structural,
});
}
for (cap, reason) in needed_capabilities {
binding.capabilities.push(CapabilityRequirement {
capability: cap.into(),
reason,
confidence: converge_pack::UnitInterval::clamped(0.85),
source: ResolutionLevel::Structural,
});
}
if !binding
.resolution
.levels_attempted
.contains(&ResolutionLevel::Structural)
{
binding
.resolution
.levels_attempted
.push(ResolutionLevel::Structural);
binding
.resolution
.levels_contributed
.push(ResolutionLevel::Structural);
}
binding
}
}
fn extract_fact_prefixes(context: &serde_json::Value) -> Vec<String> {
let mut prefixes = Vec::new();
collect_prefixes(context, &mut prefixes);
prefixes.sort();
prefixes.dedup();
prefixes
}
fn collect_prefixes(value: &serde_json::Value, prefixes: &mut Vec<String>) {
match value {
serde_json::Value::String(s) => {
if let Some(colon) = s.find(':') {
let candidate = &s[..=colon];
if candidate.len() >= 3 && candidate.chars().next().is_some_and(char::is_alphabetic)
{
prefixes.push(candidate.to_string());
}
}
}
serde_json::Value::Array(arr) => {
for v in arr {
collect_prefixes(v, prefixes);
}
}
serde_json::Value::Object(map) => {
for (key, v) in map {
if key.ends_with(':') {
prefixes.push(key.clone());
}
collect_prefixes(v, prefixes);
}
}
_ => {}
}
}
fn extract_context_keys(context: &serde_json::Value) -> Vec<ContextKey> {
let mut keys = Vec::new();
if let Some(obj) = context.as_object() {
for key in obj.keys() {
let k = key.to_lowercase();
if k == "evaluations" || k == "evaluation" {
keys.push(ContextKey::Evaluations);
}
if k == "strategies" || k == "strategy" {
keys.push(ContextKey::Strategies);
}
if k == "proposals" || k == "proposal" {
keys.push(ContextKey::Proposals);
}
if k == "constraints" || k == "constraint" {
keys.push(ContextKey::Constraints);
}
if k == "signals" || k == "signal" {
keys.push(ContextKey::Signals);
}
}
}
keys.dedup();
keys
}
fn extract_entities(outcome: &str) -> Vec<String> {
let outcome = outcome.to_lowercase();
let known_entities = [
"lead",
"vendor",
"contract",
"employee",
"expense",
"deal",
"partner",
"subscription",
"asset",
"ticket",
"campaign",
"feature",
"release",
"incident",
"policy",
"approval",
"budget",
"team",
"persona",
"skill",
"credential",
"patent",
"signal",
"hypothesis",
"experiment",
];
known_entities
.iter()
.filter(|e| outcome.contains(**e))
.map(|e| (*e).to_string())
.collect()
}
const STOP_WORDS: &[&str] = &[
"this", "that", "with", "from", "have", "your", "into", "about", "would", "there", "their",
"they", "them", "when", "what", "where", "which", "shall", "could", "should", "will", "been",
"does", "done", "each", "every", "some", "than", "then", "also", "just", "only", "most",
"such", "very", "more", "over", "under", "after", "before", "between", "through", "during",
"without", "within", "along", "across", "against", "around", "upon", "onto", "produce",
"process", "create", "update", "manage", "handle", "ensure", "track", "check", "verify",
"analyze", "review", "prepare", "complete",
];
fn extract_keywords(outcome: &str) -> Vec<String> {
outcome
.to_lowercase()
.split_whitespace()
.filter(|w| w.len() >= 4)
.map(|w| w.trim_matches(|c: char| !c.is_alphanumeric()).to_string())
.filter(|w| !w.is_empty() && !STOP_WORDS.contains(&w.as_str()))
.collect()
}