1use crate::llm_agent::{AgentIntent, AgentRuntime, LocalLlmAgent, WebizenVerdict};
10use crate::modalities::logic::n3_compiler::{
11 compile_rules_with_shacl_gate, default_observation_shape, N3OutputMode,
12};
13use crate::modalities::logic::n3_parser::{N3Event, N3Parser};
14use crate::modalities::logic::shacl::{
15 CompiledShape, ShaclCompiler, ShaclConstraint, ShaclSeverity,
16};
17use crate::solvers::grounding::{
18 evaluate_output_grounding, GroundingResolver, GroundingThresholds, GroundingVerdict,
19};
20#[cfg(not(target_arch = "wasm32"))]
21use crate::wal::{commit_semantic_mutation, WalHandoffResult, WriteAheadLog};
22use crate::webizen::{SlgArena, SlgOpcode, VmFrame};
23use crate::{q_hash, NQuin};
24use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
25use std::sync::Arc;
26use std::thread;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub enum ModelLifecycle {
30 Discovered,
31 MappedToDisk,
32 StreamingVRAM,
33 Active,
34 Scrubbing,
35}
36
37#[derive(Debug)]
39pub enum OrchestrationResult {
40 Committed {
42 text: String,
43 provenance_quins: Vec<u64>,
44 semantic_quin: Option<NQuin>,
45 wal_committed: bool,
46 wal_suspended: bool,
47 suspended_agreement_id: Option<u64>,
48 },
49 Blocked {
51 rule_violated: u64,
52 reason: &'static str,
53 },
54 Failed(String),
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59pub enum ThermalStatus {
60 Cool,
62 Warm,
64 Critical,
66}
67
68pub trait ThermalGovernor: Send + Sync {
69 fn get_thermal_state(&self) -> ThermalStatus;
71
72 fn adjust_policy(&self, status: ThermalStatus) {
74 let _ = status; }
76}
77
78pub struct NullThermalGovernor;
79impl ThermalGovernor for NullThermalGovernor {
80 fn get_thermal_state(&self) -> ThermalStatus {
81 ThermalStatus::Cool
82 }
83}
84
85use crate::solvers::calculus::{ODEFunction, RungeKutta4Static};
86use crate::solvers::SolverConfig;
87
88struct NewtonCoolingODE {
89 ambient_temp: f64,
90 cooling_constant: f64,
91 power_input: f64,
92}
93
94impl ODEFunction for NewtonCoolingODE {
95 fn derivatives(&self, _t: f64, y: &[f64; 4]) -> [f64; 4] {
96 let temp = y[0];
97 let dt_dt = -self.cooling_constant * (temp - self.ambient_temp) + self.power_input;
98 [dt_dt, 0.0, 0.0, 0.0]
99 }
100}
101
102pub struct CalculusThermalGovernor {
103 current_temp: std::sync::Mutex<f64>,
104}
105
106impl CalculusThermalGovernor {
107 pub fn new(initial_temp: f64) -> Self {
108 Self {
109 current_temp: std::sync::Mutex::new(initial_temp),
110 }
111 }
112}
113
114impl ThermalGovernor for CalculusThermalGovernor {
115 fn get_thermal_state(&self) -> ThermalStatus {
116 let mut temp = self.current_temp.lock().unwrap();
117
118 let ode = NewtonCoolingODE {
119 ambient_temp: 25.0,
120 cooling_constant: 0.1,
121 power_input: 5.0, };
123
124 let mut solver = RungeKutta4Static::new(0.1, SolverConfig::default());
125 let final_state = solver
126 .integrate(&ode, 0.0, [*temp, 0.0, 0.0, 0.0], 1.0)
127 .unwrap();
128
129 *temp = final_state.y[0];
130
131 if *temp > 85.0 {
132 ThermalStatus::Critical
133 } else if *temp > 65.0 {
134 ThermalStatus::Warm
135 } else {
136 ThermalStatus::Cool
137 }
138 }
139}
140
141pub struct TaskOrchestrator {
142 thermal_governor: Box<dyn ThermalGovernor>,
143 pub current_model_state: Arc<std::sync::Mutex<ModelLifecycle>>,
144 pub current_model_id: Arc<std::sync::Mutex<Option<u64>>>,
145 pub resident_memory_bytes: Arc<AtomicU64>,
146 pub scrubbing_lock: Arc<AtomicBool>,
147 pub mlock_enabled: Arc<AtomicBool>,
148}
149
150impl TaskOrchestrator {
151 fn routed_shapes(intent: &AgentIntent) -> Vec<CompiledShape> {
152 let compiler = ShaclCompiler::new();
153 let mut shapes = Vec::new();
154
155 let has_namespace = |needles: &[&str]| {
156 needles.iter().any(|needle| {
157 let hash = q_hash(needle);
158 intent.context_namespaces.iter().any(|ns| *ns == hash)
159 })
160 };
161
162 if has_namespace(&[
163 "health", "medical", "clinical", "fhir", "loinc", "snomed", "anatomy",
164 ]) {
165 shapes.push(default_observation_shape());
166 shapes.push(compiler.compile_class(
167 "fhir:Observation",
168 "health:heartRate",
169 ShaclConstraint::MinInclusive(20.0),
170 ShaclSeverity::Violation,
171 ));
172 }
173
174 if has_namespace(&[
175 "legal",
176 "law",
177 "contract",
178 "guardian",
179 "guardianship",
180 "rights",
181 "agreement",
182 "consent",
183 ]) {
184 shapes.push(compiler.compile_class(
185 "q42:Agreement",
186 "q42:hasGuardian",
187 ShaclConstraint::MinCount(1),
188 ShaclSeverity::Violation,
189 ));
190 shapes.push(compiler.compile_class(
191 "q42:Agreement",
192 "q42:requiresConsensus",
193 ShaclConstraint::MinCount(1),
194 ShaclSeverity::Violation,
195 ));
196 }
197
198 if has_namespace(&["commons", "governance", "community"]) {
199 shapes.push(compiler.compile_class(
200 "q42:CommonsAgreement",
201 "q42:hasDomainScope",
202 ShaclConstraint::MinCount(1),
203 ShaclSeverity::Violation,
204 ));
205 }
206
207 shapes.push(compiler.compile_class(
210 "cog:Agent",
211 "cog:agentID",
212 ShaclConstraint::MinCount(1),
213 ShaclSeverity::Violation,
214 ));
215 shapes.push(compiler.compile_class(
216 "q42:InferenceIntent",
217 "q42:inferenceAuthorizedBy",
218 ShaclConstraint::MinCount(1),
219 ShaclSeverity::Violation,
220 ));
221 shapes.push(compiler.compile_class(
222 "q42:InferenceIntent",
223 "q42:provenanceCitations",
224 ShaclConstraint::MinInclusive(1.0),
225 ShaclSeverity::Warning,
226 ));
227
228 if shapes.is_empty() {
229 shapes.push(default_observation_shape());
230 }
231 shapes
232 }
233
234 pub fn new(thermal_governor: Box<dyn ThermalGovernor>) -> Self {
235 Self {
236 thermal_governor,
237 current_model_state: Arc::new(std::sync::Mutex::new(ModelLifecycle::Discovered)),
238 current_model_id: Arc::new(std::sync::Mutex::new(None)),
239 resident_memory_bytes: Arc::new(AtomicU64::new(0)),
240 scrubbing_lock: Arc::new(AtomicBool::new(false)),
241 mlock_enabled: Arc::new(AtomicBool::new(false)),
242 }
243 }
244
245 pub fn thermal_state_label(&self) -> &'static str {
246 match self.thermal_governor.get_thermal_state() {
247 ThermalStatus::Cool => "Cool",
248 ThermalStatus::Warm => "Warm",
249 ThermalStatus::Critical => "Critical",
250 }
251 }
252
253 pub fn resident_model_id(&self) -> Option<u64> {
254 *self.current_model_id.lock().unwrap()
255 }
256
257 pub fn resident_memory_bytes(&self) -> u64 {
258 self.resident_memory_bytes.load(Ordering::Relaxed)
259 }
260
261 pub fn register_resident_model(&self, model_id: u64, bytes: u64) {
262 *self.current_model_id.lock().unwrap() = Some(model_id);
263 self.resident_memory_bytes.store(bytes, Ordering::Relaxed);
264 }
265
266 pub fn load_model(&self, agent: &LocalLlmAgent, model_id: u64) -> Result<(), &'static str> {
267 if self.scrubbing_lock.load(Ordering::Acquire) {
268 return Err("Cannot load model: Swarm Worker is actively scrubbing memory arena");
269 }
270 if let Some(active_model) = self.resident_model_id() {
271 if active_model != model_id {
272 return Err("Cannot load model: another model is still resident; evict it first");
273 }
274 }
275
276 let mut state = self.current_model_state.lock().unwrap();
277 *state = ModelLifecycle::MappedToDisk;
278 *state = ModelLifecycle::StreamingVRAM;
279 *state = ModelLifecycle::Active;
280 drop(state);
281 *self.current_model_id.lock().unwrap() = Some(model_id);
282
283 let schema_q42 = "data/schemaorg/30.0/schemaorg-current-https.q42";
286 let schema_lex = "data/schemaorg/30.0/schemaorg-current-https.q42.lex";
287 if std::path::Path::new(schema_q42).exists() {
288 agent.configure_sieve_lex(schema_q42);
289 } else if std::path::Path::new(schema_lex).exists() {
290 agent.configure_sieve_lex(schema_lex);
291 } else if let crate::llm_agent::AgentBackend::Local { model_path, .. } = &agent.backend {
292 let mut p = std::path::PathBuf::from(model_path);
293 if let Some(stem) = p.file_stem().and_then(|s| s.to_str()).map(str::to_string) {
294 p.set_file_name(format!("{stem}.q42.lex"));
295 if p.exists() {
296 agent.configure_sieve_lex(p.to_string_lossy().into_owned());
297 }
298 }
299 }
300 Ok(())
301 }
302
303 pub fn evict_model(&self, model_id: u64) {
304 let resident = self.resident_model_id();
305 if resident.is_none() {
306 self.resident_memory_bytes.store(0, Ordering::Relaxed);
307 if let Ok(mut st) = self.current_model_state.lock() {
308 *st = ModelLifecycle::Discovered;
309 }
310 return;
311 }
312 if resident != Some(model_id) {
313 return;
314 }
315
316 if let Ok(mut state) = self.current_model_state.lock() {
317 *state = ModelLifecycle::Scrubbing;
318 }
319
320 self.scrubbing_lock.store(true, Ordering::Release);
321 let scrub_bytes = self.resident_memory_bytes.swap(0, Ordering::Relaxed);
322
323 let lock_clone = self.scrubbing_lock.clone();
324 let state_clone = self.current_model_state.clone();
325 let model_clone = self.current_model_id.clone();
326
327 thread::spawn(move || {
329 let mut scrub_block = [0u8; 4096];
330 let mut remaining = scrub_bytes.max(scrub_block.len() as u64);
331 while remaining > 0 {
332 let write_len = remaining.min(scrub_block.len() as u64) as usize;
333 for byte in scrub_block.iter_mut().take(write_len) {
334 unsafe { std::ptr::write_volatile(byte, 0) };
335 }
336 remaining = remaining.saturating_sub(write_len as u64);
337 if remaining > 0 {
338 std::thread::yield_now();
339 }
340 }
341
342 lock_clone.store(false, Ordering::Release);
344 if let Ok(mut model) = model_clone.lock() {
345 *model = None;
346 }
347 if let Ok(mut st) = state_clone.lock() {
348 *st = ModelLifecycle::Discovered;
349 }
350 crate::resident_model::clear_resident_model();
351 });
352 }
353
354 fn gate_llm_n3_output(
356 text: &str,
357 contract_hash: u64,
358 intent: &AgentIntent,
359 ) -> Result<(), &'static str> {
360 let mut rules = Vec::new();
361 let mut parser = N3Parser::new(text);
362 parser
363 .parse_all(|event| {
364 if let N3Event::LogicRule(rule) = event {
365 rules.push(rule);
366 }
367 Ok(())
368 })
369 .map_err(|_| "Invalid N3 output from LLM")?;
370
371 if rules.is_empty() {
372 return Err("LLM did not emit parseable N3 assertions");
373 }
374
375 let routed_shapes = Self::routed_shapes(intent);
376 let shapes: Vec<&CompiledShape> = routed_shapes.iter().collect();
377 let mut opcodes = [SlgOpcode::Call; 256];
378 let mut quins = [crate::NQuin::default(); 64];
379 let program =
382 compile_rules_with_shacl_gate(&rules, &shapes, &mut opcodes, &mut quins, contract_hash)
383 .map_err(|_| "SHACL validation failed for LLM N3 output")?;
384
385 let mut arena = SlgArena::new();
386 let mut frame = VmFrame::default();
387 let _ = crate::modalities::logic::n3_compiler::execute_compiled_program(
388 &mut arena,
389 &opcodes[..program.opcode_count],
390 &mut frame,
391 32,
392 )
393 .map_err(|_| "Sentinel VM memory overflow")?;
394 Ok(())
395 }
396
397 pub fn orchestrate_inference(
403 &self,
404 agent: &dyn AgentRuntime,
405 prompt: &str,
406 graph_context: &str,
407 intent: AgentIntent,
408 suspended: Option<&mut crate::crdt::SuspendedTransactionQueue>,
409 ) -> OrchestrationResult {
410 self.orchestrate_inference_inner(agent, prompt, graph_context, intent, suspended, None)
411 }
412
413 pub fn orchestrate_inference_grounded(
419 &self,
420 agent: &dyn AgentRuntime,
421 prompt: &str,
422 graph_context: &str,
423 intent: AgentIntent,
424 suspended: Option<&mut crate::crdt::SuspendedTransactionQueue>,
425 resolver: &dyn GroundingResolver,
426 thresholds: GroundingThresholds,
427 ) -> OrchestrationResult {
428 self.orchestrate_inference_inner(
429 agent,
430 prompt,
431 graph_context,
432 intent,
433 suspended,
434 Some((resolver, thresholds)),
435 )
436 }
437
438 fn orchestrate_inference_inner(
439 &self,
440 agent: &dyn AgentRuntime,
441 prompt: &str,
442 graph_context: &str,
443 intent: AgentIntent,
444 suspended: Option<&mut crate::crdt::SuspendedTransactionQueue>,
445 grounding: Option<(&dyn GroundingResolver, GroundingThresholds)>,
446 ) -> OrchestrationResult {
447 let thermal_state = self.thermal_governor.get_thermal_state();
448
449 match thermal_state {
450 ThermalStatus::Critical => {
451 if !intent.is_critical() {
452 return OrchestrationResult::Blocked {
453 rule_violated: 0xDEADBEEF, reason: "Device critical thermal state. Non-essential inference paused.",
455 };
456 }
457 }
458 ThermalStatus::Warm => {
459 }
461 ThermalStatus::Cool => {}
462 }
463
464 match agent.validate_intent(&intent) {
466 WebizenVerdict::Deny {
467 rule_violated,
468 reason,
469 conduct_record,
470 } => {
471 #[cfg(not(target_arch = "wasm32"))]
473 if let Some(quin) = conduct_record {
474 if let Ok(mut wal) = crate::wal::WriteAheadLog::open(".qualia_conduct.wal") {
475 let _ = wal.append_mutation(&quin);
476
477 let secret = [42u8; 32];
479 let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret);
480 let frame = [quin];
481 let sub_root = crate::agency::compute_scoped_merkle_root(
482 &frame,
483 intent.principal_did_hash,
484 );
485 let _signature = crate::agency::sign_agency_root(&signing_key, &sub_root);
486
487 }
489 }
490 #[cfg(target_arch = "wasm32")]
491 let _ = conduct_record;
492 return OrchestrationResult::Blocked {
493 rule_violated,
494 reason,
495 };
496 }
497 WebizenVerdict::DenyWithExplanation {
498 rule_violated,
499 reason: _,
500 explanation: _,
501 } => {
502 return OrchestrationResult::Blocked {
504 rule_violated,
505 reason: "Intent Frame Violation",
506 };
507 }
508 WebizenVerdict::RequireReconfirmation { reason: _ } => {
509 return OrchestrationResult::Blocked {
510 rule_violated: 0,
511 reason: "Reconfirmation required",
512 };
513 }
514 WebizenVerdict::Sanitised { .. } => { }
515 WebizenVerdict::Permit => {}
516 }
517
518 if let Some(reason) = crate::modalities::logic::qubo::quantum_prompt_gate(prompt) {
520 return OrchestrationResult::Blocked {
521 rule_violated: crate::q_hash("q42:QuantumTaskShape"),
522 reason,
523 };
524 }
525
526 {
530 use crate::temporal_graph::register_agent;
531 let _agent_quin = register_agent(intent.principal_did_hash, 0);
532 }
534
535 let output = match agent.infer(prompt, graph_context) {
537 Ok(o) => o,
538 Err(e) => return OrchestrationResult::Failed(format!("{:?}", e)),
539 };
540
541 if output.semantic_quin.is_none()
544 && (intent.output_mode == N3OutputMode::N3Assertions
545 || intent.output_mode == N3OutputMode::GraphMutation)
546 {
547 if let Err(reason) =
548 Self::gate_llm_n3_output(&output.text, intent.principal_did_hash, &intent)
549 {
550 return OrchestrationResult::Blocked {
551 rule_violated: crate::q_hash("q42:N3Compiler"),
552 reason,
553 };
554 }
555 }
556
557 match agent.validate_output(&output) {
559 WebizenVerdict::Deny {
560 rule_violated,
561 reason,
562 conduct_record,
563 } => {
564 #[cfg(not(target_arch = "wasm32"))]
565 if let Some(quin) = conduct_record {
566 if let Ok(mut wal) = crate::wal::WriteAheadLog::open(".qualia_conduct.wal") {
567 let _ = wal.append_mutation(&quin);
568 }
569 }
570 #[cfg(target_arch = "wasm32")]
571 let _ = conduct_record;
572 return OrchestrationResult::Blocked {
573 rule_violated,
574 reason,
575 };
576 }
577 WebizenVerdict::DenyWithExplanation {
578 rule_violated,
579 reason: _,
580 explanation: _,
581 } => {
582 return OrchestrationResult::Blocked {
583 rule_violated,
584 reason: "Output blocked due to frame bounds",
585 };
586 }
587 WebizenVerdict::RequireReconfirmation { reason: _ } => {
588 return OrchestrationResult::Blocked {
589 rule_violated: 0,
590 reason: "Output requires reconfirmation",
591 };
592 }
593 _ => {}
594 }
595
596 if let (Some((resolver, thresholds)), Some(claim)) =
602 (grounding, output.semantic_quin.as_ref())
603 {
604 match evaluate_output_grounding(claim, &output.provenance_quins, resolver, thresholds) {
605 GroundingVerdict::Grounded { .. } => {}
606 GroundingVerdict::Weak { .. } => {
607 return OrchestrationResult::Blocked {
608 rule_violated: q_hash("q42:GroundingRequiresReview"),
609 reason: "Claim only partially grounded in its cited facts — requires human review before commit.",
610 };
611 }
612 GroundingVerdict::Ungrounded { .. } => {
613 return OrchestrationResult::Blocked {
614 rule_violated: q_hash("q42:GroundingRequired"),
615 reason:
616 "Claim does not trace to its cited facts (ungrounded). Cannot commit.",
617 };
618 }
619 }
620 }
621
622 #[cfg(not(target_arch = "wasm32"))]
623 let mut semantic_quin = output.semantic_quin;
624 #[cfg(not(target_arch = "wasm32"))]
625 let mut wal_written = false;
626 #[cfg(not(target_arch = "wasm32"))]
627 let mut wal_suspended = false;
628 #[cfg(not(target_arch = "wasm32"))]
629 let mut suspended_agreement_id = None;
630
631 #[cfg(target_arch = "wasm32")]
632 let semantic_quin = output.semantic_quin;
633 #[cfg(target_arch = "wasm32")]
634 let (wal_written, wal_suspended, suspended_agreement_id) = {
635 if suspended.is_some() {
636 log::debug!(
637 "WASM orchestration received a suspended queue; persistence remains delegated to the host"
638 );
639 }
640 (false, false, None)
641 };
642
643 #[cfg(not(target_arch = "wasm32"))]
644 if let Some(ref mut quin) = semantic_quin {
645 if let Ok(mut wal) = WriteAheadLog::open(".qualia_graph_mutations.wal") {
646 let secret = [42u8; 32];
647 let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret);
648 let mut local_suspended = crate::crdt::SuspendedTransactionQueue::new();
649 let queue = suspended.unwrap_or(&mut local_suspended);
650 let agent_did_hash = q_hash(agent.agent_did());
651 match commit_semantic_mutation(
652 &mut wal,
653 quin,
654 intent.principal_did_hash,
655 agent_did_hash,
656 &signing_key,
657 queue,
658 ) {
659 Ok(WalHandoffResult::Committed) => {
660 wal_written = true;
661 }
662 Ok(WalHandoffResult::Suspended { agreement_id }) => {
663 wal_written = true;
664 wal_suspended = true;
665 suspended_agreement_id = Some(agreement_id);
666 }
667 Err(_) => {}
668 }
669 }
670 }
671
672 OrchestrationResult::Committed {
673 text: output.text,
674 provenance_quins: output.provenance_quins,
675 semantic_quin,
676 wal_committed: wal_written,
677 wal_suspended,
678 suspended_agreement_id,
679 }
680 }
681}
682
683#[cfg(test)]
684pub mod tests {
685 use super::*;
686 use crate::llm_agent::{AgentIntent, AgentRuntime, LocalLlmAgent, SANCTUARY_SCOPE_WEBIZEN};
687 use crate::modalities::logic::n3_compiler::N3OutputMode;
688
689 #[test]
690 pub fn qualia_validate_ring_buffer() {}
691
692 #[test]
693 fn test_orchestrator_full_permit_path() {
694 let agent = LocalLlmAgent::new("did:git:orch-test", "model.gguf");
695 let intent = AgentIntent {
696 intent_predicate: 0x1234,
697 requested_graph_scope: vec![0xABCD],
698 context_namespaces: vec![],
699 requires_network: false,
700 ilp_offer_micro_cents: 0,
701 principal_did_hash: 0,
702 mcp_intent_frame_hash: 0x1234,
703 output_mode: N3OutputMode::FreeText,
704 clearance_ceiling: 0,
705 max_sentinel_depth: 32,
706 active_profile: None,
707 };
708 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
709 let result = orch.orchestrate_inference(
710 &agent,
711 "Summarise my health graph.",
712 "some_graph_bytes",
713 intent,
714 None,
715 );
716 assert!(matches!(result, OrchestrationResult::Committed { .. }));
717 }
718
719 #[test]
720 fn test_orchestrator_blocks_sanctuary_intent() {
721 let agent = LocalLlmAgent::new("did:git:orch-test", "model.gguf");
722 let intent = AgentIntent {
723 intent_predicate: 0x1234,
724 requested_graph_scope: vec![SANCTUARY_SCOPE_WEBIZEN],
725 context_namespaces: vec![],
726 requires_network: false,
727 ilp_offer_micro_cents: 0,
728 principal_did_hash: 0,
729 mcp_intent_frame_hash: 0x1234,
730 output_mode: N3OutputMode::FreeText,
731 clearance_ceiling: 0,
732 max_sentinel_depth: 32,
733 active_profile: None,
734 };
735 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
736 let result =
737 orch.orchestrate_inference(&agent, "Show me sanctuary data.", "ctx", intent, None);
738 assert!(matches!(result, OrchestrationResult::Blocked { .. }));
739 }
740
741 use crate::llm_agent::{AgentBackend, AgentError, AgentOutput};
743 use crate::solvers::grounding::{GroundingResolver, GroundingThresholds};
744 use std::collections::HashMap;
745
746 fn claim_quin(s: u64, p: u64, o: u64) -> NQuin {
747 NQuin {
748 subject: s,
749 predicate: p,
750 object: o,
751 context: 0,
752 metadata: 0,
753 parity: 0,
754 }
755 }
756
757 struct ClaimAgent {
761 inner: LocalLlmAgent,
762 output: AgentOutput,
763 }
764 impl AgentRuntime for ClaimAgent {
765 fn backend(&self) -> &AgentBackend {
766 self.inner.backend()
767 }
768 fn agent_did(&self) -> &str {
769 self.inner.agent_did()
770 }
771 fn validate_intent(&self, intent: &AgentIntent) -> WebizenVerdict {
772 self.inner.validate_intent(intent)
773 }
774 fn infer(&self, _p: &str, _g: &str) -> Result<AgentOutput, AgentError> {
775 Ok(self.output.clone())
776 }
777 fn validate_output(&self, _o: &AgentOutput) -> WebizenVerdict {
778 WebizenVerdict::Permit
779 }
780 fn memory_budget_remaining(&self) -> u64 {
781 self.inner.memory_budget_remaining()
782 }
783 }
784
785 struct MapResolver(HashMap<u64, NQuin>);
786 impl GroundingResolver for MapResolver {
787 fn resolve(&self, h: u64) -> Option<NQuin> {
788 self.0.get(&h).copied()
789 }
790 }
791
792 fn grounding_intent() -> AgentIntent {
793 AgentIntent {
794 intent_predicate: 0x1234,
795 requested_graph_scope: vec![0xABCD],
796 context_namespaces: vec![],
797 requires_network: false,
798 ilp_offer_micro_cents: 0,
799 principal_did_hash: 0,
800 mcp_intent_frame_hash: 0x1234,
801 output_mode: N3OutputMode::FreeText,
802 clearance_ceiling: 0,
803 max_sentinel_depth: 32,
804 active_profile: None,
805 }
806 }
807
808 fn claim_agent(claim: NQuin, citations: Vec<u64>) -> ClaimAgent {
809 ClaimAgent {
810 inner: LocalLlmAgent::new("did:git:claim-agent", "model.gguf"),
811 output: AgentOutput {
812 text: "asserted".into(),
813 semantic_quin: Some(claim),
814 provenance_quins: citations,
815 tokens_generated: 1,
816 inference_duration_ms: 0,
817 peak_memory_bytes: 0,
818 },
819 }
820 }
821
822 #[test]
823 fn grounding_gate_commits_a_supported_claim() {
824 let claim = claim_quin(1, 2, 3);
825 let agent = claim_agent(claim, vec![0xAA]);
826 let mut m = HashMap::new();
827 m.insert(0xAA, claim_quin(1, 2, 3)); let resolver = MapResolver(m);
829 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
830 let result = orch.orchestrate_inference_grounded(
831 &agent,
832 "assert",
833 "ctx",
834 grounding_intent(),
835 None,
836 &resolver,
837 GroundingThresholds::default(),
838 );
839 assert!(
840 matches!(result, OrchestrationResult::Committed { .. }),
841 "got {result:?}"
842 );
843 }
844
845 #[test]
846 fn grounding_gate_blocks_an_ungrounded_claim() {
847 let claim = claim_quin(1, 2, 3);
848 let agent = claim_agent(claim, vec![0xBB]);
849 let mut m = HashMap::new();
850 m.insert(0xBB, claim_quin(7, 8, 9)); let resolver = MapResolver(m);
852 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
853 let result = orch.orchestrate_inference_grounded(
854 &agent,
855 "assert",
856 "ctx",
857 grounding_intent(),
858 None,
859 &resolver,
860 GroundingThresholds::default(),
861 );
862 assert!(
863 matches!(result, OrchestrationResult::Blocked { reason, .. } if reason.contains("ungrounded")),
864 "ungrounded claim must be blocked, got {result:?}"
865 );
866 }
867
868 #[test]
869 fn grounding_gate_routes_partial_support_to_review() {
870 let claim = claim_quin(1, 2, 3);
873 let agent = claim_agent(claim, vec![0xC1, 0xC2]);
874 let mut m = HashMap::new();
875 m.insert(0xC1, claim_quin(1, 50, 60));
876 m.insert(0xC2, claim_quin(60, 70, 3));
877 let resolver = MapResolver(m);
878 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
879 let result = orch.orchestrate_inference_grounded(
880 &agent,
881 "assert",
882 "ctx",
883 grounding_intent(),
884 None,
885 &resolver,
886 GroundingThresholds::default(),
887 );
888 assert!(
889 matches!(result, OrchestrationResult::Blocked { reason, .. } if reason.contains("human review")),
890 "partial grounding must require review, got {result:?}"
891 );
892 }
893
894 #[test]
895 fn ungrounded_claim_still_commits_without_a_resolver() {
896 let claim = claim_quin(1, 2, 3);
899 let agent = claim_agent(claim, vec![0xBB]);
900 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
901 let result = orch.orchestrate_inference(&agent, "assert", "ctx", grounding_intent(), None);
902 assert!(
903 matches!(result, OrchestrationResult::Committed { .. }),
904 "got {result:?}"
905 );
906 }
907
908 #[test]
909 fn test_thermal_critical_blocks_non_essential_inference() {
910 struct MockCriticalThermalGovernor;
911 impl ThermalGovernor for MockCriticalThermalGovernor {
912 fn get_thermal_state(&self) -> ThermalStatus {
913 ThermalStatus::Critical
914 }
915 }
916
917 let agent = LocalLlmAgent::new("did:git:orch-test", "model.gguf");
918 let intent = AgentIntent {
919 intent_predicate: 0x1234, requested_graph_scope: vec![],
921 context_namespaces: vec![],
922 requires_network: false,
923 ilp_offer_micro_cents: 0,
924 principal_did_hash: 0,
925 mcp_intent_frame_hash: 0x1234,
926 output_mode: N3OutputMode::FreeText,
927 clearance_ceiling: 0,
928 max_sentinel_depth: 32,
929 active_profile: None,
930 };
931 let orch = TaskOrchestrator::new(Box::new(MockCriticalThermalGovernor));
932 let result = orch.orchestrate_inference(&agent, "Query", "ctx", intent, None);
933 assert!(matches!(
934 result,
935 OrchestrationResult::Blocked {
936 rule_violated: 0xDEADBEEF,
937 ..
938 }
939 ));
940 }
941
942 #[test]
943 fn test_async_scrub_lock_invariant() {
944 let orch = TaskOrchestrator::new(Box::new(NullThermalGovernor));
945 let agent = LocalLlmAgent::new("did:git:orch-test", "model.gguf");
946
947 assert!(orch.load_model(&agent, 123).is_ok());
949
950 orch.evict_model(123);
952
953 let load_result = orch.load_model(&agent, 456);
955 assert!(
956 load_result.is_err(),
957 "Orchestrator violated mechanical sympathy! Mapped model while Swarm worker was still scrubbing."
958 );
959
960 std::thread::sleep(std::time::Duration::from_millis(50));
962
963 let second_load_result = orch.load_model(&agent, 456);
965 assert!(
966 second_load_result.is_ok(),
967 "Orchestrator failed to load model after scrubbing lock cleared."
968 );
969 assert_eq!(orch.resident_model_id(), Some(456));
970
971 let mut vm = crate::modalities::logic::core::WebizenVM::with_scrubbing_lock(
973 orch.scrubbing_lock.clone(),
974 );
975 let bytecode = vec![crate::modalities::logic::core::WebizenOpcode::LoadModel(
976 999,
977 )];
978 vm.load_bytecode(&bytecode);
979
980 let quin = crate::NQuin {
981 subject: 0,
982 predicate: 0,
983 object: 0,
984 context: 0,
985 metadata: 0,
986 parity: 0,
987 };
988
989 orch.evict_model(456);
991 let exec_result = vm.execute_implication(&quin);
992 assert!(exec_result.is_none());
993 assert_eq!(
994 vm.yielded_op,
995 Some(crate::modalities::logic::core::WebizenOpcode::LoadModel(
996 999
997 ))
998 );
999
1000 std::thread::sleep(std::time::Duration::from_millis(50));
1002 assert_eq!(orch.resident_model_id(), None);
1003 }
1004
1005 fn write_fever_lex_bytes() -> Vec<u8> {
1006 let h_sub = q_hash("Patient");
1007 let h_pred = q_hash("fever");
1008 let h_obj = q_hash("True");
1009 let entries = [(h_sub, "Patient"), (h_pred, "fever"), (h_obj, "True")];
1010 let mut sorted = entries.to_vec();
1011 sorted.sort_unstable_by_key(|(h, _)| *h);
1012 let entry_count = sorted.len() as u64;
1013 let strings_offset = 32 + entry_count * 16;
1014 let mut blob = Vec::new();
1015 let mut index = Vec::new();
1016 for (hash, text) in &sorted {
1017 let str_off = blob.len() as u64;
1018 let b = text.as_bytes();
1019 let len = b.len().min(65535) as u16;
1020 blob.push(0x01u8); blob.extend_from_slice(&len.to_le_bytes());
1024 blob.extend_from_slice(&b[..len as usize]);
1025 index.extend_from_slice(&hash.to_le_bytes());
1026 index.extend_from_slice(&str_off.to_le_bytes());
1027 }
1028 let mut out = Vec::new();
1029 out.extend_from_slice(b"Q42LEX\0\0");
1030 out.extend_from_slice(&entry_count.to_le_bytes());
1031 out.extend_from_slice(&strings_offset.to_le_bytes());
1032 out.extend_from_slice(&1u64.to_le_bytes());
1033 out.extend_from_slice(&index);
1034 out.extend_from_slice(&blob);
1035 out
1036 }
1037
1038 #[test]
1040 #[serial_test::serial]
1041 fn test_e2e_llm_to_wal_pipeline() {
1042 let _profiler = dhat::Profiler::builder().testing().build();
1043
1044 let lex_bytes = write_fever_lex_bytes();
1045 let lex_mmap = crate::q42_lex::Q42LexMmap::from_bytes(&lex_bytes).expect("lex");
1046 let tok = crate::gguf_sharder::GgufTokenizer::default();
1047 let spec = crate::neuro_symbolic_sieve::SieveLexSpec::fever_observation();
1048 let mut sieve = crate::neuro_symbolic_sieve::NeuroSymbolicSieve::from_lex_and_tokenizer(
1049 &lex_mmap, &tok, &spec,
1050 );
1051 assert!(
1052 sieve.masks_ready(),
1053 "lex must resolve fever triple token IDs"
1054 );
1055 let (sub, pred, obj) = sieve.resolved_token_triple().expect("triple");
1056
1057 let wal_file = tempfile::NamedTempFile::new().expect("wal temp");
1058 let mut wal = WriteAheadLog::open(wal_file.path()).expect("wal open");
1059 let secret = [42u8; 32];
1060 let signing_key = ed25519_dalek::SigningKey::from_bytes(&secret);
1061 let mut suspended = crate::crdt::SuspendedTransactionQueue::new();
1062 let principal = q_hash("did:q42:test-principal");
1063 let agent_did = q_hash("did:git:orch-test");
1064
1065 let stats_before = dhat::HeapStats::get();
1066
1067 assert!(sieve.apply_token(sub).is_ok());
1068 assert!(sieve.apply_token(pred).is_ok());
1069 assert!(sieve.apply_token(obj).is_ok());
1070 assert_eq!(
1071 sieve.emitted_len(),
1072 3,
1073 "must halt at exactly 3 sieve tokens"
1074 );
1075 assert!(sieve.is_complete());
1076
1077 let mut quin = sieve.assemble_quin(q_hash("clinical:fever-context"));
1078 let handoff = commit_semantic_mutation(
1079 &mut wal,
1080 &mut quin,
1081 principal,
1082 agent_did,
1083 &signing_key,
1084 &mut suspended,
1085 )
1086 .expect("wal handoff");
1087
1088 let stats_after = dhat::HeapStats::get();
1089 assert_eq!(
1090 stats_after.total_blocks - stats_before.total_blocks,
1091 0,
1092 "sieve decode + WAL write must not heap-allocate"
1093 );
1094 assert_eq!(
1095 stats_after.total_bytes - stats_before.total_bytes,
1096 0,
1097 "sieve decode + WAL write must not heap-allocate"
1098 );
1099
1100 assert_eq!(handoff, WalHandoffResult::Committed);
1101 let recovered = wal.recover().expect("recover");
1102 assert_eq!(recovered.len(), 1, "WAL must contain one 48-byte Quin");
1103 assert_eq!(recovered[0].subject, q_hash("Patient"));
1104 assert_eq!(recovered[0].predicate, q_hash("fever"));
1105 assert_eq!(recovered[0].object, q_hash("True"));
1106 assert_eq!(recovered[0].context, principal);
1107 assert_eq!(
1108 recovered[0].parity,
1109 recovered[0].subject
1110 ^ recovered[0].predicate
1111 ^ recovered[0].object
1112 ^ recovered[0].context
1113 ^ recovered[0].metadata
1114 );
1115
1116 let gemma = std::path::Path::new(
1118 "C:/Projects/qualiaDB/gemma-4-E4B-it-GGUF/gemma-4-E4B-it-Q4_K_M.gguf",
1119 );
1120 if !gemma.exists() {
1121 return;
1122 }
1123 let mut lex_tmp = tempfile::NamedTempFile::new().expect("lex temp");
1124 std::io::Write::write_all(&mut lex_tmp, &lex_bytes).expect("lex write");
1125 let agent = LocalLlmAgent::new("did:git:e2e-fever", gemma.to_string_lossy());
1126 agent.configure_sieve_lex(lex_tmp.path().to_string_lossy().into_owned());
1127 let frame_hash = q_hash("intent:fever");
1128 let fever_intent = AgentIntent {
1129 intent_predicate: frame_hash,
1130 requested_graph_scope: vec![q_hash("snomed:hasFever")],
1131 context_namespaces: vec![q_hash("health"), q_hash("snomed")],
1132 requires_network: false,
1133 ilp_offer_micro_cents: 0,
1134 principal_did_hash: principal,
1135 mcp_intent_frame_hash: frame_hash,
1136 output_mode: N3OutputMode::GraphMutation,
1137 clearance_ceiling: 0,
1138 max_sentinel_depth: 32,
1139 active_profile: None,
1140 };
1141 assert_eq!(agent.validate_intent(&fever_intent), WebizenVerdict::Permit);
1142 if let Ok(output) = AgentRuntime::infer(&agent, "The user has a fever", "clinical-context")
1143 {
1144 assert!(
1145 output.tokens_generated <= 3,
1146 "sieve must cap generation at 3 tokens"
1147 );
1148 if let Some(q) = output.semantic_quin {
1149 assert_ne!(q.subject, 0);
1150 assert_ne!(q.predicate, 0);
1151 }
1152 }
1153 }
1154}