use chrono::Utc;
use crate::{
AdmissionController, AdmissionResult, FeasibilityAssessment, FeasibilityDimension, IntentError,
IntentPacket, Reversibility,
};
#[derive(Debug)]
pub enum Admission {
Admit,
Reject(IntentError),
}
pub fn admit(intent: &IntentPacket) -> Admission {
if intent.is_expired(Utc::now()) {
return Admission::Reject(IntentError::Expired(intent.expires));
}
if intent.outcome.trim().is_empty() {
return Admission::Reject(IntentError::Infeasible("empty outcome".into()));
}
Admission::Admit
}
pub struct DefaultAdmissionController {
known_capabilities: Vec<String>,
}
impl DefaultAdmissionController {
pub fn new() -> Self {
Self {
known_capabilities: Vec::new(),
}
}
pub fn with_capabilities(mut self, capabilities: Vec<String>) -> Self {
self.known_capabilities = capabilities;
self
}
fn assess_capability(&self, intent: &IntentPacket) -> FeasibilityAssessment {
if let Some(arr) = intent
.context
.get("required_capabilities")
.and_then(|v| v.as_array())
{
let missing: Vec<_> = arr
.iter()
.filter_map(|v| v.as_str())
.filter(|cap| {
!self.known_capabilities.is_empty()
&& !self.known_capabilities.iter().any(|k| k == cap)
})
.collect();
if !missing.is_empty() {
return FeasibilityAssessment::infeasible(
FeasibilityDimension::Capability,
format!("unknown capabilities: {}", missing.join(", ")),
);
}
}
FeasibilityAssessment::feasible(
FeasibilityDimension::Capability,
"all capabilities available",
)
}
#[allow(clippy::unused_self)]
fn assess_context(&self, intent: &IntentPacket) -> FeasibilityAssessment {
if intent.outcome.trim().is_empty() {
return FeasibilityAssessment::infeasible(
FeasibilityDimension::Context,
"empty outcome",
);
}
if intent.is_expired(Utc::now()) {
return FeasibilityAssessment::infeasible(
FeasibilityDimension::Context,
"intent already expired",
);
}
FeasibilityAssessment::feasible(FeasibilityDimension::Context, "context well-formed")
}
#[allow(clippy::unused_self)]
fn assess_resources(&self, intent: &IntentPacket) -> FeasibilityAssessment {
let remaining = intent.expires - Utc::now();
if remaining.num_seconds() < 60 {
return FeasibilityAssessment::with_constraints(
FeasibilityDimension::Resources,
format!(
"only {}s remaining — tight deadline",
remaining.num_seconds()
),
);
}
FeasibilityAssessment::feasible(FeasibilityDimension::Resources, "adequate time budget")
}
#[allow(clippy::unused_self)]
fn assess_authority(&self, intent: &IntentPacket) -> FeasibilityAssessment {
if intent.reversibility == Reversibility::Irreversible && intent.authority.is_empty() {
return FeasibilityAssessment::uncertain(
FeasibilityDimension::Authority,
"irreversible action with no declared authority scope",
);
}
FeasibilityAssessment::feasible(FeasibilityDimension::Authority, "authority scope declared")
}
}
impl AdmissionController for DefaultAdmissionController {
fn evaluate(&self, intent: &IntentPacket) -> AdmissionResult {
AdmissionResult::from_dimensions(vec![
self.assess_capability(intent),
self.assess_context(intent),
self.assess_resources(intent),
self.assess_authority(intent),
])
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::FeasibilityKind;
use chrono::Duration;
#[test]
fn rejects_expired() {
let intent = IntentPacket::new("ship q3", Utc::now() - Duration::seconds(1));
assert!(matches!(admit(&intent), Admission::Reject(_)));
}
#[test]
fn rejects_empty_outcome() {
let intent = IntentPacket::new("", Utc::now() + Duration::hours(1));
assert!(matches!(admit(&intent), Admission::Reject(_)));
}
#[test]
fn admits_valid() {
let intent = IntentPacket::new("ship q3", Utc::now() + Duration::hours(1));
assert!(matches!(admit(&intent), Admission::Admit));
}
#[test]
fn controller_admits_valid_intent() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new("hire senior engineer", Utc::now() + Duration::hours(24));
let result = controller.evaluate(&intent);
assert!(result.feasible);
assert_eq!(result.dimensions.len(), 4);
assert!(result.rejection_reason.is_none());
}
#[test]
fn controller_rejects_expired_intent() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new("too late", Utc::now() - Duration::hours(1));
let result = controller.evaluate(&intent);
assert!(!result.feasible);
assert!(result.rejection_reason.is_some());
}
#[test]
fn controller_rejects_empty_outcome() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new(" ", Utc::now() + Duration::hours(1));
let result = controller.evaluate(&intent);
assert!(!result.feasible);
}
#[test]
fn controller_flags_irreversible_without_authority() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new(
"delete production database",
Utc::now() + Duration::hours(1),
)
.with_reversibility(Reversibility::Irreversible);
let result = controller.evaluate(&intent);
assert!(result.feasible);
let auth_dim = result
.dimensions
.iter()
.find(|d| d.dimension == FeasibilityDimension::Authority)
.unwrap();
assert_eq!(auth_dim.kind, FeasibilityKind::Uncertain);
}
#[test]
fn controller_accepts_irreversible_with_authority() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new(
"delete production database",
Utc::now() + Duration::hours(1),
)
.with_reversibility(Reversibility::Irreversible)
.with_authority(vec!["infra-admin".into()]);
let result = controller.evaluate(&intent);
assert!(result.feasible);
let auth_dim = result
.dimensions
.iter()
.find(|d| d.dimension == FeasibilityDimension::Authority)
.unwrap();
assert_eq!(auth_dim.kind, FeasibilityKind::Feasible);
}
#[test]
fn controller_flags_missing_capabilities() {
let controller =
DefaultAdmissionController::new().with_capabilities(vec!["web".into(), "ocr".into()]);
let intent = IntentPacket::new("analyze patents", Utc::now() + Duration::hours(1))
.with_context(serde_json::json!({
"required_capabilities": ["web", "patent_search"]
}));
let result = controller.evaluate(&intent);
assert!(!result.feasible);
let cap_dim = result
.dimensions
.iter()
.find(|d| d.dimension == FeasibilityDimension::Capability)
.unwrap();
assert_eq!(cap_dim.kind, FeasibilityKind::Infeasible);
assert!(cap_dim.reason.contains("patent_search"));
}
#[test]
fn controller_flags_tight_deadline() {
let controller = DefaultAdmissionController::new();
let intent = IntentPacket::new("quick task", Utc::now() + Duration::seconds(30));
let result = controller.evaluate(&intent);
assert!(result.feasible); let res_dim = result
.dimensions
.iter()
.find(|d| d.dimension == FeasibilityDimension::Resources)
.unwrap();
assert_eq!(res_dim.kind, FeasibilityKind::FeasibleWithConstraints);
}
}