use crate::runtime::mesh::{build_conformance_vector, AutonomicMesh};
use crate::runtime::mesh_types::{ConformanceGrade, HookEvent, InstanceId, MeshAction};
use crate::runtime::sha256::sha256;
impl AutonomicMesh {
pub(super) fn handle_repair_plan(
&self,
instance_id: &str,
params: serde_json::Value,
) -> Result<serde_json::Value, String> {
let id: String =
serde_json::from_value(params).map_err(|e| format!("Invalid params: {}", e))?;
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let actions: Vec<lsp_max_protocol::MaxCodeAction> = inst
.diagnostics
.iter()
.filter(|d| d.diagnostic_id == id || d.law_id == id)
.flat_map(|d| {
d.repair_actions
.iter()
.map(move |ra| lsp_max_protocol::MaxCodeAction {
action: lsp_types_max::CodeAction {
title: ra.description.clone(),
..Default::default()
},
preconditions: vec![],
validation_plan: lsp_max_protocol::ValidationPlan {
gates: d.verification_gates.clone(),
},
rollback_plan: lsp_max_protocol::RollbackPlan {
strategy: "revert".to_string(),
},
receipt_plan: lsp_max_protocol::ReceiptPlan {
expected_receipts: d
.receipt_obligation
.as_ref()
.map(|ro| ro.required_receipts.clone())
.unwrap_or_default(),
},
})
})
.collect();
serde_json::to_value(actions).map_err(|e| e.to_string())
}
pub(super) fn handle_export_bundle(
&self,
instance_id: &str,
params: serde_json::Value,
) -> Result<serde_json::Value, String> {
let snapshot_id: lsp_max_protocol::SnapshotId =
serde_json::from_value(params).map_err(|e| format!("Invalid params: {}", e))?;
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let mut cv = build_conformance_vector(&inst.diagnostics);
cv.score = Some(inst.conformance_score());
let bundle = lsp_max_protocol::AnalysisBundle {
snapshot_id,
capability_vector: lsp_max_protocol::MaxCapabilityVector {
client: lsp_types_max::ClientCapabilities::default(),
server: lsp_types_max::ServerCapabilities::default(),
negotiated: serde_json::Value::Null,
experimental: serde_json::Value::Null,
gaps: vec![],
},
diagnostics: inst.diagnostics.clone(),
actions: vec![],
conformance_vector: cv,
receipts: inst.receipts.clone(),
};
serde_json::to_value(bundle).map_err(|e| e.to_string())
}
pub(super) fn handle_hook_graph(&self, instance_id: &str) -> Result<serde_json::Value, String> {
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let diagnostic_ids: Vec<&str> = inst
.diagnostics
.iter()
.map(|d| d.diagnostic_id.as_str())
.collect();
let receipt_ids: Vec<&str> = inst
.receipts
.iter()
.map(|r| r.receipt_id.as_str())
.collect();
let graph: Vec<serde_json::Value> = self
.hooks
.iter()
.map(|h| {
serde_json::json!({
"hook": h.name(),
"instance_id": instance_id,
"active_diagnostic_triggers": diagnostic_ids,
"active_receipt_triggers": receipt_ids,
"pending_diagnostic_count": inst.diagnostics.len(),
"pending_receipt_count": inst.receipts.len(),
})
})
.collect();
serde_json::to_value(graph).map_err(|e| e.to_string())
}
pub(super) fn handle_chain(&self) -> Result<serde_json::Value, String> {
let mut chain: Vec<serde_json::Value> = self
.instances
.iter()
.map(|(id, inst)| {
serde_json::json!({
"id": id,
"phase": inst.phase,
"policy_state": inst.policy_state,
"conformance_score": inst.conformance_score(),
"conformance_grade": inst.conformance_grade().as_str(),
"diagnostic_count": inst.diagnostics.len(),
"receipt_count": inst.receipts.len(),
"diagnostics": inst.diagnostics.iter().map(|d| serde_json::json!({
"id": d.diagnostic_id,
"law_id": d.law_id,
"severity": format!("{:?}", d.lsp.severity),
"message": d.lsp.message,
})).collect::<Vec<_>>(),
"receipts": inst.receipts.iter().map(|r| serde_json::json!({
"receipt_id": r.receipt_id,
"hash": r.hash,
})).collect::<Vec<_>>(),
})
})
.collect();
chain.sort_by_key(|v| v["id"].as_str().unwrap_or("").to_string());
serde_json::to_value(chain).map_err(|e| e.to_string())
}
pub(super) fn handle_lawful_transition(
&self,
instance_id: &str,
params: serde_json::Value,
) -> Result<serde_json::Value, String> {
let target_phase: String =
serde_json::from_value(params).map_err(|e| format!("Invalid params: {}", e))?;
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let phase_order = [
"Uninitialized",
"Initializing",
"Initialized",
"ShutDown",
"Exited",
];
let current_idx = phase_order
.iter()
.position(|&p| p == inst.phase.to_string().as_str());
let target_idx = phase_order.iter().position(|&p| p == target_phase.as_str());
let (admitted, refused_reason) = match (current_idx, target_idx) {
(Some(ci), Some(ti)) if ti == ci + 1 => {
let blocking: Vec<_> = inst
.diagnostics
.iter()
.filter(|d| {
matches!(
d.lsp.severity,
Some(lsp_types_max::DiagnosticSeverity::ERROR)
)
})
.map(|d| d.diagnostic_id.clone())
.collect();
if blocking.is_empty() {
(true, None)
} else {
(
false,
Some(format!(
"Blocked by {} error diagnostic(s): {:?}",
blocking.len(),
blocking
)),
)
}
}
(Some(ci), Some(ti)) if ti <= ci => (
false,
Some(format!(
"Backward transitions are not lawful: {} -> {}",
inst.phase, target_phase
)),
),
(Some(ci), Some(ti)) if ti > ci + 1 => (
false,
Some(format!(
"Cannot skip phases: {} -> {} skips {} intermediate phase(s)",
inst.phase,
target_phase,
ti - ci - 1
)),
),
_ => (
false,
Some(format!(
"Unknown phase(s): current='{}', target='{}'",
inst.phase, target_phase
)),
),
};
let result = serde_json::json!({
"instance_id": instance_id,
"current_phase": inst.phase,
"requested_phase": target_phase,
"admitted": admitted,
"refused_reason": refused_reason,
"blocking_diagnostic_count": inst.diagnostics.iter()
.filter(|d| matches!(d.lsp.severity, Some(lsp_types_max::DiagnosticSeverity::ERROR)))
.count(),
"conformance_score": inst.conformance_score(),
"conformance_grade": inst.conformance_grade().as_str(),
});
Ok(result)
}
pub(super) fn handle_replay(&self, instance_id: &str) -> Result<serde_json::Value, String> {
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let events: Vec<serde_json::Value> = self
.event_log
.iter()
.filter(|e| match e {
HookEvent::StateTransition {
instance_id: id, ..
}
| HookEvent::DiagnosticEmitted {
instance_id: id, ..
}
| HookEvent::DiagnosticCleared {
instance_id: id, ..
}
| HookEvent::ReceiptEmitted {
instance_id: id, ..
}
| HookEvent::PolicyStateChanged {
instance_id: id, ..
} => id.0 == instance_id,
HookEvent::BoundedActionExecuted {
instance_id: id, ..
} => id.0 == instance_id,
HookEvent::InstanceReset { instance_id: id } => id.0 == instance_id,
})
.filter_map(|e| serde_json::to_value(e).ok())
.collect();
Ok(serde_json::json!({
"instance_id": instance_id,
"receipt_count": inst.receipts.len(),
"event_count": events.len(),
"events": events,
}))
}
pub(super) fn handle_release_actuation(
&mut self,
instance_id: &str,
) -> Result<serde_json::Value, String> {
let inst = self
.instances
.get(instance_id)
.ok_or_else(|| format!("Instance not found: {}", instance_id))?;
let score = inst.conformance_score();
let admitted = inst.diagnostics.is_empty();
if !admitted {
return Err(format!(
"Release refused: {} active diagnostics blocking conformance (score: {})",
inst.diagnostics.len(),
score
));
}
let receipt_id = format!("rcpt-release-{}", instance_id);
let hash = sha256(receipt_id.as_bytes());
let receipt = lsp_max_protocol::Receipt {
receipt_id: receipt_id.clone(),
hash,
prev_receipt_hash: None,
};
self.execute_action(MeshAction::EmitReceipt {
instance_id: InstanceId::from(instance_id),
receipt: receipt.clone(),
});
Ok(serde_json::json!({
"released": true,
"instance_id": instance_id,
"conformance_score": score,
"conformance_grade": ConformanceGrade::from_score(score).as_str(),
"release_receipt": receipt,
}))
}
}