diff --git a/src/shared/generated/runtime/CadenceHint.ts b/src/shared/generated/runtime/CadenceHint.ts new file mode 100644 index 000000000..399eaac96 --- /dev/null +++ b/src/shared/generated/runtime/CadenceHint.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * A hint a region can pass back to the governor about preferred next + * tick cadence. The governor may honor or override; it owns the + * final policy. + */ +export type CadenceHint = "faster" | "hold" | "slower" | "sleep"; diff --git a/src/shared/generated/runtime/ComputeClass.ts b/src/shared/generated/runtime/ComputeClass.ts new file mode 100644 index 000000000..056eaf3eb --- /dev/null +++ b/src/shared/generated/runtime/ComputeClass.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Compute footprint class. Drives governor decisions about which + * regions to throttle first under compute/thermal pressure. + */ +export type ComputeClass = "bookkeeping" | "cpu" | "cpu-vectorized" | "inference-light" | "inference-heavy"; diff --git a/src/shared/generated/runtime/MemoryClass.ts b/src/shared/generated/runtime/MemoryClass.ts new file mode 100644 index 000000000..8de62f074 --- /dev/null +++ b/src/shared/generated/runtime/MemoryClass.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Memory footprint class. Drives governor decisions about which + * regions to throttle first under memory pressure. + */ +export type MemoryClass = "light" | "moderate" | "heavy" | "vram-sensitive"; diff --git a/src/shared/generated/runtime/PersonaLifecycle.ts b/src/shared/generated/runtime/PersonaLifecycle.ts new file mode 100644 index 000000000..578ba7747 --- /dev/null +++ b/src/shared/generated/runtime/PersonaLifecycle.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Persona lifecycle events relevant to regions (allow regions to + * allocate / deallocate per-persona state). + */ +export type PersonaLifecycle = { "kind": "created", persona_id: string, } | { "kind": "destroyed", persona_id: string, }; diff --git a/src/shared/generated/runtime/PressureLevel.ts b/src/shared/generated/runtime/PressureLevel.ts new file mode 100644 index 000000000..948634b6e --- /dev/null +++ b/src/shared/generated/runtime/PressureLevel.ts @@ -0,0 +1,7 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Coarse system pressure level surfaced to regions so they can adjust + * internally without parsing every PressureSignal variant. + */ +export type PressureLevel = "nominal" | "moderate" | "high" | "critical"; diff --git a/src/shared/generated/runtime/PressureProfile.ts b/src/shared/generated/runtime/PressureProfile.ts new file mode 100644 index 000000000..d0c35e43a --- /dev/null +++ b/src/shared/generated/runtime/PressureProfile.ts @@ -0,0 +1,18 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ComputeClass } from "./ComputeClass"; +import type { MemoryClass } from "./MemoryClass"; +import type { PressureSignalKind } from "./PressureSignalKind"; + +/** + * What a region declares about its resource footprint at registration + * time. The governor reads this once at register, then re-queries it + * when pressure shifts (regions may report different profiles after + * adapting under load — e.g., hippocampus drops from `Heavy` to + * `Moderate` when working memory is pruned). + */ +export type PressureProfile = { memory_class: MemoryClass, compute_class: ComputeClass, +/** + * Pressure kinds this region wants `on_signal` calls for. Other + * kinds are filtered out by the governor. + */ +responds_to: Array, }; diff --git a/src/shared/generated/runtime/PressureSignalKind.ts b/src/shared/generated/runtime/PressureSignalKind.ts new file mode 100644 index 000000000..6aa7ae326 --- /dev/null +++ b/src/shared/generated/runtime/PressureSignalKind.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Which kinds of pressure signals a region wants to receive via + * `on_signal`. The governor filters and routes signals based on this. + * + * Mirrors the variants of [`PressureSignal`] but is a kind-only enum + * (no payload) so it can be declared statically by a region at + * registration time. + */ +export type PressureSignalKind = "thermal" | "battery-low" | "system-mem-high" | "vram-high" | "user-active" | "inference-queue-depth" | "speculation-miss-rate"; diff --git a/src/shared/generated/runtime/RegionId.ts b/src/shared/generated/runtime/RegionId.ts new file mode 100644 index 000000000..7f102b639 --- /dev/null +++ b/src/shared/generated/runtime/RegionId.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Stable identifier for a brain region. Used by SubstrateGovernor for + * policy lookup and by telemetry/log streams for tagging events. + * + * Carries `Cow<'static, str>` so static IDs ("hippocampus") cost + * nothing and dynamic IDs (custom regions registered at runtime) are + * still supported. + */ +export type RegionId = string; diff --git a/src/shared/generated/runtime/RegionSignal.ts b/src/shared/generated/runtime/RegionSignal.ts new file mode 100644 index 000000000..907644534 --- /dev/null +++ b/src/shared/generated/runtime/RegionSignal.ts @@ -0,0 +1,11 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PersonaLifecycle } from "./PersonaLifecycle"; +import type { PressureLevel } from "./PressureLevel"; +import type { SleepPhase } from "./SleepPhase"; + +/** + * Signals the substrate sends to regions out-of-band (not on the + * regular tick). Regions that don't care about a signal default to a + * no-op. + */ +export type RegionSignal = { "kind": "persona-lifecycle" } & PersonaLifecycle | { "kind": "sleep-transition", persona_id: string, phase: SleepPhase, } | { "kind": "system-pressure-changed", level: PressureLevel, }; diff --git a/src/shared/generated/runtime/RegionTelemetry.ts b/src/shared/generated/runtime/RegionTelemetry.ts new file mode 100644 index 000000000..70b4b5faa --- /dev/null +++ b/src/shared/generated/runtime/RegionTelemetry.ts @@ -0,0 +1,54 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PressureSignal } from "../governor/PressureSignal"; +import type { RegionId } from "./RegionId"; + +/** + * Per-tick telemetry shape every brain region emits. + * + * Emitted on every tick. The substrate routes it to: + * + * - **The governor** — reads `consumed_since_last` / `published` to + * tune region budget (yield-learning loop, algorithm 7). + * - **The operator surface** — `./jtag region/stats` / `region/yield` + * read aggregate telemetry across personas. + * - **The substrate event stream** — `RegionTickCompleted` and + * `ReadyBufferUpdated` events for cross-region awareness. + */ +export type RegionTelemetry = { +/** + * Which region this came from. Stable string id. + */ +region_id: RegionId, +/** + * Persona scope. `None` means the tick was global (background + * work not tied to a specific persona). + */ +persona_id: string | null, +/** + * When this tick started (wall clock). + */ +tick_started_at: string, +/** + * How long the tick body ran. + */ +tick_duration: string, +/** + * Items the region published to ready-buffers this tick. + */ +published: number, +/** + * Items in the region's ready-buffers consumed by handlers since + * the last tick. + */ +consumed_since_last: number, +/** + * Handler `peek` calls that returned `None` since the last tick. + * Signals to the governor that the region should be upweighted + * (handlers are asking for stuff that's not staged yet). + */ +buffer_misses_since_last: number, +/** + * Pressure the region observed (DB slow, embedding queue full, + * etc.). Surfaced to the governor for cascade evaluation. + */ +pressure_observed?: PressureSignal, }; diff --git a/src/shared/generated/runtime/SleepPhase.ts b/src/shared/generated/runtime/SleepPhase.ts new file mode 100644 index 000000000..2ee8d837b --- /dev/null +++ b/src/shared/generated/runtime/SleepPhase.ts @@ -0,0 +1,8 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +/** + * Sleep/wake phases for the persona-level cognitive cycle. The sleep + * policy region (L0-4d) emits these; other regions react by changing + * their tick body (active vs idle vs sleep consolidation). + */ +export type SleepPhase = "active" | "idle" | "sleep"; diff --git a/src/shared/generated/runtime/TickOutcome.ts b/src/shared/generated/runtime/TickOutcome.ts new file mode 100644 index 000000000..138c76919 --- /dev/null +++ b/src/shared/generated/runtime/TickOutcome.ts @@ -0,0 +1,34 @@ +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { PressureSignal } from "../governor/PressureSignal"; +import type { CadenceHint } from "./CadenceHint"; + +/** + * Yield telemetry returned by every region tick. Feeds the substrate + * governor's yield-learning loop (algorithm 7 in + * COGNITION-ALGORITHMS.md, lands in L0-4c). + * + * Regions emit this from every tick. The governor reads aggregate + * (`consumed_since_last` vs `published`) to upweight regions whose + * output is being consumed by handlers and downweight regions whose + * output is ignored. + */ +export type TickOutcome = { +/** + * Items the region pre-staged this tick (publishes to ready-buffers). + */ +published: number, +/** + * Items in the region's ready-buffer that have been consumed by + * handlers since the last tick. The denominator for yield. + */ +consumed_since_last: number, +/** + * Pressure observation. If the region detected backpressure (DB + * slow, embedding queue full, etc.), reports it here for the + * governor. + */ +pressure_observed?: PressureSignal, +/** + * Optional next-tick hint (region requests faster/slower cadence). + */ +cadence_hint?: CadenceHint, }; diff --git a/src/workers/continuum-core/src/ai/adapter.rs b/src/workers/continuum-core/src/ai/adapter.rs index c34c17ec7..547591b2a 100644 --- a/src/workers/continuum-core/src/ai/adapter.rs +++ b/src/workers/continuum-core/src/ai/adapter.rs @@ -621,9 +621,7 @@ mod tests { InferenceDevice::Gpu } fn supports_model(&self, _model: &str) -> bool { - self.model - .as_deref() - .map_or(true, |model| model == _model) + self.model.as_deref().map_or(true, |model| model == _model) } } diff --git a/src/workers/continuum-core/src/airc/inbound_attach.rs b/src/workers/continuum-core/src/airc/inbound_attach.rs index abcb1b0d5..95b904bcf 100644 --- a/src/workers/continuum-core/src/airc/inbound_attach.rs +++ b/src/workers/continuum-core/src/airc/inbound_attach.rs @@ -7,7 +7,7 @@ use std::path::PathBuf; use std::sync::Arc; -use airc_ipc::{AttachRequest, DaemonClient, Response, codec::read_frame}; +use airc_ipc::{codec::read_frame, AttachRequest, DaemonClient, Response}; use tracing::warn; use crate::airc::realtime_wire::{bus_event_from_envelope, envelope_from_event}; @@ -87,7 +87,7 @@ mod tests { Body, ClientId, EventId, MentionTarget, PeerId, RoomId, TranscriptEvent, TranscriptKind, }; use serde_json::json; - use tokio::time::{Duration, timeout}; + use tokio::time::{timeout, Duration}; use uuid::Uuid; fn transcript_event(body: Option, headers: airc_core::Headers) -> TranscriptEvent { @@ -158,11 +158,9 @@ mod tests { publish_transcript_event(&event, &bus).await.unwrap(); - assert!( - timeout(Duration::from_millis(20), receiver.recv()) - .await - .is_err() - ); + assert!(timeout(Duration::from_millis(20), receiver.recv()) + .await + .is_err()); } #[tokio::test] @@ -177,10 +175,8 @@ mod tests { publish_transcript_event(&event, &bus).await.unwrap(); - assert!( - timeout(Duration::from_millis(20), receiver.recv()) - .await - .is_err() - ); + assert!(timeout(Duration::from_millis(20), receiver.recv()) + .await + .is_err()); } } diff --git a/src/workers/continuum-core/src/bin/cargo-continuum-vdd.rs b/src/workers/continuum-core/src/bin/cargo-continuum-vdd.rs index 784c049a9..5f1b9ed18 100644 --- a/src/workers/continuum-core/src/bin/cargo-continuum-vdd.rs +++ b/src/workers/continuum-core/src/bin/cargo-continuum-vdd.rs @@ -1,6 +1,6 @@ use continuum_core::vdd::{ - ArtifactWriter, ChatRoundtripConfig, ChatRoundtripHarness, HARNESS_SPECS, HarnessId, - HarnessStatus, LiveChatProbe, + ArtifactWriter, ChatRoundtripConfig, ChatRoundtripHarness, HarnessId, HarnessStatus, + LiveChatProbe, HARNESS_SPECS, }; use std::str::FromStr; diff --git a/src/workers/continuum-core/src/cognition/generate_recipe/mod.rs b/src/workers/continuum-core/src/cognition/generate_recipe/mod.rs index 81ee29b55..93df85661 100644 --- a/src/workers/continuum-core/src/cognition/generate_recipe/mod.rs +++ b/src/workers/continuum-core/src/cognition/generate_recipe/mod.rs @@ -48,7 +48,7 @@ pub use orchestrator::{generate_recipe_with_ai, GenerateRecipeOrchestratorParams pub use parser::{parse_recipe_from_ai_response, ParseError}; pub use prompt::{build_recipe_system_prompt, build_recipe_user_prompt}; pub use types::{ - RecipeDefinitionShape, RecipeGenerateHints, RecipeGenerationRequest, - RecipeGenerationResponse, RecipeTemplateInfo, + RecipeDefinitionShape, RecipeGenerateHints, RecipeGenerationRequest, RecipeGenerationResponse, + RecipeTemplateInfo, }; pub use validator::{validate_recipe_structure, ValidationError}; diff --git a/src/workers/continuum-core/src/cognition/generate_recipe/orchestrator.rs b/src/workers/continuum-core/src/cognition/generate_recipe/orchestrator.rs index 4bde3a1be..4d8b86b71 100644 --- a/src/workers/continuum-core/src/cognition/generate_recipe/orchestrator.rs +++ b/src/workers/continuum-core/src/cognition/generate_recipe/orchestrator.rs @@ -94,9 +94,7 @@ pub async fn generate_recipe_with_ai( let (system_prompt, user_prompt) = build_prompts(&request); let provider_id = provider.as_deref().unwrap_or(DEFAULT_PROVIDER).to_string(); - let model_id = model.unwrap_or_else(|| { - default_model_for_provider(&provider_id).to_string() - }); + let model_id = model.unwrap_or_else(|| default_model_for_provider(&provider_id).to_string()); let inference_request = TextGenerationRequest { messages: vec![ @@ -178,7 +176,10 @@ mod tests { "claude-sonnet-4-5-20250929" ); assert_eq!(default_model_for_provider("openai"), "gpt-4o"); - assert_eq!(default_model_for_provider("groq"), "llama-3.3-70b-versatile"); + assert_eq!( + default_model_for_provider("groq"), + "llama-3.3-70b-versatile" + ); assert_eq!(default_model_for_provider("deepseek"), "deepseek-chat"); assert_eq!(default_model_for_provider("google"), "gemini-2.5-flash"); assert_eq!(default_model_for_provider("xai"), "grok-3"); diff --git a/src/workers/continuum-core/src/cognition/generate_recipe/parser.rs b/src/workers/continuum-core/src/cognition/generate_recipe/parser.rs index 9871b17ff..df8ba00e1 100644 --- a/src/workers/continuum-core/src/cognition/generate_recipe/parser.rs +++ b/src/workers/continuum-core/src/cognition/generate_recipe/parser.rs @@ -18,9 +18,8 @@ use regex::Regex; /// the response, including newlines. Mirrors TS `/\{[\s\S]*\}/` exactly. NOT /// anchored — the AI may emit prose before/after the JSON despite the prompt /// rule "Output ONLY the JSON object", so the matcher tolerates it. -static JSON_ENVELOPE_RE: Lazy = Lazy::new(|| { - Regex::new(r"(?s)\{.*\}").expect("static regex compiles") -}); +static JSON_ENVELOPE_RE: Lazy = + Lazy::new(|| Regex::new(r"(?s)\{.*\}").expect("static regex compiles")); /// Typed parse failure. Carrier for the TS shim's `validationErrors` array /// when surfaced through PR-2's IPC handler. Avoids the silent @@ -73,11 +72,11 @@ pub fn parse_recipe_from_ai_response( ) -> Result { let preview = preview(response_text); - let envelope = JSON_ENVELOPE_RE.find(response_text).ok_or( - ParseError::NoJsonEnvelope { + let envelope = JSON_ENVELOPE_RE + .find(response_text) + .ok_or(ParseError::NoJsonEnvelope { raw_preview: preview.clone(), - }, - )?; + })?; serde_json::from_str::(envelope.as_str()).map_err(|err| { ParseError::MalformedJson { @@ -229,7 +228,8 @@ Hope that helps!"#; /// human-readable messages. #[test] fn missing_optional_fields_default_to_none_or_empty() { - let response = r#"{"uniqueId": "minimal", "name": "M", "displayName": "M", "description": "min"}"#; + let response = + r#"{"uniqueId": "minimal", "name": "M", "displayName": "M", "description": "min"}"#; let shape = parse_recipe_from_ai_response(response).expect("partial parses"); assert_eq!(shape.unique_id, "minimal"); assert_eq!(shape.version, None); diff --git a/src/workers/continuum-core/src/cognition/generate_recipe/prompt.rs b/src/workers/continuum-core/src/cognition/generate_recipe/prompt.rs index 4e4982803..518038983 100644 --- a/src/workers/continuum-core/src/cognition/generate_recipe/prompt.rs +++ b/src/workers/continuum-core/src/cognition/generate_recipe/prompt.rs @@ -149,13 +149,9 @@ Most recipes follow this pipeline:\n\ /// Build the user prompt from the natural language description + optional hints. /// Mirrors TS `buildUserPrompt` exactly. -pub fn build_recipe_user_prompt( - description: &str, - hints: Option<&RecipeGenerateHints>, -) -> String { - let mut prompt = format!( - "Generate a RecipeDefinition JSON for the following activity:\n\n{description}" - ); +pub fn build_recipe_user_prompt(description: &str, hints: Option<&RecipeGenerateHints>) -> String { + let mut prompt = + format!("Generate a RecipeDefinition JSON for the following activity:\n\n{description}"); if let Some(h) = hints { let mut hint_parts: Vec = Vec::new(); @@ -223,7 +219,10 @@ mod tests { #[test] fn system_prompt_contains_role_and_schema_header() { let p = build_recipe_system_prompt(&fixture_templates()); - assert!(p.starts_with("You are a recipe generator"), "header missing"); + assert!( + p.starts_with("You are a recipe generator"), + "header missing" + ); assert!(p.contains("## RecipeDefinition Schema")); assert!(p.contains("```typescript")); } @@ -235,7 +234,9 @@ mod tests { #[test] fn system_prompt_renders_template_list_with_required_fields() { let p = build_recipe_system_prompt(&fixture_templates()); - assert!(p.contains(" - research-loop: Iterative research with verification (required: topic, depth)")); + assert!(p.contains( + " - research-loop: Iterative research with verification (required: topic, depth)" + )); assert!(p.contains(" - code-review: Review code with TDD feedback (required: target)")); } diff --git a/src/workers/continuum-core/src/cognition/generate_recipe/validator.rs b/src/workers/continuum-core/src/cognition/generate_recipe/validator.rs index fd8412092..3a9b4a061 100644 --- a/src/workers/continuum-core/src/cognition/generate_recipe/validator.rs +++ b/src/workers/continuum-core/src/cognition/generate_recipe/validator.rs @@ -49,10 +49,21 @@ const VALID_ROLE_TYPES: &[&str] = &["organizational", "perceptual", "creative"]; #[derive(Debug, Clone, PartialEq)] pub enum ValidationError { Missing(&'static str), - InvalidFormat { field: &'static str, value: String, expected: &'static str }, - InvalidEnumValue { field: &'static str, value: String, allowed: &'static [&'static str] }, + InvalidFormat { + field: &'static str, + value: String, + expected: &'static str, + }, + InvalidEnumValue { + field: &'static str, + value: String, + allowed: &'static [&'static str], + }, PipelineEmpty, - PipelineStepMissingField { index: usize, field: &'static str }, + PipelineStepMissingField { + index: usize, + field: &'static str, + }, DuplicateUniqueId(String), } @@ -136,10 +147,7 @@ pub fn validate_recipe_structure( field: "command", }); } - let has_params_object = step - .get("params") - .map(|v| v.is_object()) - .unwrap_or(false); + let has_params_object = step.get("params").map(|v| v.is_object()).unwrap_or(false); if !has_params_object { errors.push(ValidationError::PipelineStepMissingField { index: idx, @@ -250,10 +258,7 @@ pub fn validate_recipe_structure( // The filesystem collision check stays TS-side (RecipeLoader.getInstance(). // getAllRecipes()), but the in-request check using the carrier list runs // here so the AI can be told "that ID is taken" without an extra IPC trip. - if !recipe.unique_id.is_empty() - && existing_recipe_ids - .iter() - .any(|id| id == &recipe.unique_id) + if !recipe.unique_id.is_empty() && existing_recipe_ids.iter().any(|id| id == &recipe.unique_id) { errors.push(ValidationError::DuplicateUniqueId(recipe.unique_id.clone())); } @@ -298,10 +303,7 @@ mod tests { fn happy_path_well_formed_recipe_validates_clean() { let recipe = valid_minimal_recipe(); let errors = validate_recipe_structure(&recipe, &[]); - assert!( - errors.is_empty(), - "expected no errors, got: {errors:?}" - ); + assert!(errors.is_empty(), "expected no errors, got: {errors:?}"); } /// What this catches: missing top-level required fields are surfaced @@ -358,7 +360,7 @@ mod tests { let mut recipe = valid_minimal_recipe(); recipe.pipeline = vec![ json!({"command": "rag/build", "params": {}}), - json!({}), // step 1 has neither command nor params + json!({}), // step 1 has neither command nor params json!({"command": "ai/generate"}), // step 2 has command but no params ]; let errors = validate_recipe_structure(&recipe, &[]); diff --git a/src/workers/continuum-core/src/cognition/host_capability_probe.rs b/src/workers/continuum-core/src/cognition/host_capability_probe.rs index 37a9e3055..40e2a5595 100644 --- a/src/workers/continuum-core/src/cognition/host_capability_probe.rs +++ b/src/workers/continuum-core/src/cognition/host_capability_probe.rs @@ -25,8 +25,8 @@ //! `metal` / `cuda` / `vulkan`. Tests can pass `platform = "mock"` to //! bypass. -use crate::cognition::model_resolver::{HostCapability, HwCapabilityTier}; use crate::cognition::adaptive_throughput::TargetSilicon; +use crate::cognition::model_resolver::{HostCapability, HwCapabilityTier}; use crate::gpu::monitor::GpuMonitor; use serde::{Deserialize, Serialize}; use sysinfo::System; @@ -95,7 +95,10 @@ pub fn detect_host_capability( let (hw_capability_tier, primary_target_silicon) = match platform { "metal" => { let cpu_brand = first_cpu_brand(system_info); - (apple_silicon_tier(&cpu_brand, total_mem_mb), TargetSilicon::UnifiedMemory) + ( + apple_silicon_tier(&cpu_brand, total_mem_mb), + TargetSilicon::UnifiedMemory, + ) } "cuda" => (nvidia_sm_tier(device_name, platform)?, TargetSilicon::Gpu), "vulkan" => (HwCapabilityTier::VulkanAmd, TargetSilicon::Gpu), @@ -289,7 +292,10 @@ mod tests { fn nvidia_unknown_sku_errors_no_silent_fallback() { let err = nvidia_sm_tier("NVIDIA Voodoo 5 6000", "cuda").unwrap_err(); match err { - ProbeError::UnknownGpuDevice { platform, device_name } => { + ProbeError::UnknownGpuDevice { + platform, + device_name, + } => { assert_eq!(platform, "cuda"); assert_eq!(device_name, "NVIDIA Voodoo 5 6000"); } diff --git a/src/workers/continuum-core/src/cognition/mod.rs b/src/workers/continuum-core/src/cognition/mod.rs index add5dd20e..2075059ef 100644 --- a/src/workers/continuum-core/src/cognition/mod.rs +++ b/src/workers/continuum-core/src/cognition/mod.rs @@ -45,8 +45,8 @@ pub mod throughput_lease; pub mod tool_embedding; pub mod tool_executor; pub mod turn_batch; -pub mod validate_response; pub mod types; +pub mod validate_response; pub mod vision_describe; pub use adaptive_throughput::*; diff --git a/src/workers/continuum-core/src/cognition/model_resolver/mod.rs b/src/workers/continuum-core/src/cognition/model_resolver/mod.rs index cc52ed93d..ddb5cb0bd 100644 --- a/src/workers/continuum-core/src/cognition/model_resolver/mod.rs +++ b/src/workers/continuum-core/src/cognition/model_resolver/mod.rs @@ -43,7 +43,6 @@ use crate::cognition::adaptive_throughput::TargetSilicon; use crate::model_registry::types::{Capability, Model, Provider, ProviderKind}; use std::collections::HashMap; - fn derive_target_silicon( model: &Model, provider_kinds: &HashMap<&str, ProviderKind>, @@ -794,7 +793,10 @@ mod tests { assert!(req.required_capabilities.contains(&Capability::Vision)); assert!(req.required_capabilities.contains(&Capability::AudioInput)); assert!(req.required_capabilities.contains(&Capability::AudioOutput)); - assert_eq!(req.silicon_residency, SiliconResidencyRequirement::GpuOrUnifiedMemoryOnly); + assert_eq!( + req.silicon_residency, + SiliconResidencyRequirement::GpuOrUnifiedMemoryOnly + ); assert_eq!(req.provider_policy, LocalOrCloudPolicy::PreferLocal); } @@ -804,7 +806,10 @@ mod tests { assert_eq!(req.provider_policy, LocalOrCloudPolicy::LocalOnly); // Bar fields still bundled. assert!(req.required_capabilities.contains(&Capability::Vision)); - assert_eq!(req.silicon_residency, SiliconResidencyRequirement::GpuOrUnifiedMemoryOnly); + assert_eq!( + req.silicon_residency, + SiliconResidencyRequirement::GpuOrUnifiedMemoryOnly + ); } #[test] @@ -830,7 +835,9 @@ mod tests { "error must name Vision capability: {required_sensory_capabilities:?}" ); assert!( - required_sensory_capabilities.iter().any(|c| c == "AudioInput"), + required_sensory_capabilities + .iter() + .any(|c| c == "AudioInput"), "error must name AudioInput capability: {required_sensory_capabilities:?}" ); } diff --git a/src/workers/continuum-core/src/cognition/rate_proposals/orchestrator.rs b/src/workers/continuum-core/src/cognition/rate_proposals/orchestrator.rs index bb1bcc799..e6d7c8c22 100644 --- a/src/workers/continuum-core/src/cognition/rate_proposals/orchestrator.rs +++ b/src/workers/continuum-core/src/cognition/rate_proposals/orchestrator.rs @@ -123,11 +123,8 @@ pub async fn rate_proposals_with_ai( let registry_guard = registry.read().await; let response = generate_text(®istry_guard, inference_request).await?; - let ratings = parse_ratings_from_ai_response( - &response.text, - &context.proposals, - &ParseConfig::default(), - ); + let ratings = + parse_ratings_from_ai_response(&response.text, &context.proposals, &ParseConfig::default()); Ok(RateProposalsResponse { ratings }) } diff --git a/src/workers/continuum-core/src/cognition/rate_proposals/parser.rs b/src/workers/continuum-core/src/cognition/rate_proposals/parser.rs index 21ccb5e50..9f4c90ef0 100644 --- a/src/workers/continuum-core/src/cognition/rate_proposals/parser.rs +++ b/src/workers/continuum-core/src/cognition/rate_proposals/parser.rs @@ -82,7 +82,11 @@ pub fn parse_ratings_from_ai_response( ratings } -fn parse_one_section(section: &str, proposal: &ResponseProposal, config: &ParseConfig) -> ProposalRating { +fn parse_one_section( + section: &str, + proposal: &ResponseProposal, + config: &ParseConfig, +) -> ProposalRating { // Score: floating-point, clamped to [0, 1] per TS. let score_re = Regex::new(r"(?i)Score:\s*([0-9.]+)").expect("static regex"); let score = score_re @@ -164,7 +168,10 @@ Reasoning: Different approach, valuable alternative assert_eq!(ratings[0].proposal_id, "p-1"); assert!((ratings[0].score - 0.85).abs() < 1e-9); assert!(ratings[0].should_post); - assert_eq!(ratings[0].reasoning, "High quality response with good technical detail"); + assert_eq!( + ratings[0].reasoning, + "High quality response with good technical detail" + ); assert_eq!(ratings[1].proposal_id, "p-2"); assert!((ratings[1].score - 0.60).abs() < 1e-9); assert!(!ratings[1].should_post); diff --git a/src/workers/continuum-core/src/cognition/shared_analysis/error.rs b/src/workers/continuum-core/src/cognition/shared_analysis/error.rs index d94af60a4..37652957d 100644 --- a/src/workers/continuum-core/src/cognition/shared_analysis/error.rs +++ b/src/workers/continuum-core/src/cognition/shared_analysis/error.rs @@ -82,7 +82,10 @@ mod tests { field: "summary".to_string(), }; let msg = err.to_string(); - assert!(msg.contains("summary"), "expected field name in message: {msg}"); + assert!( + msg.contains("summary"), + "expected field name in message: {msg}" + ); assert!( msg.contains("missing required field"), "expected variant context in message: {msg}" diff --git a/src/workers/continuum-core/src/cognition/shared_analysis/prompt.rs b/src/workers/continuum-core/src/cognition/shared_analysis/prompt.rs index 79d0d39d8..d5bbeee07 100644 --- a/src/workers/continuum-core/src/cognition/shared_analysis/prompt.rs +++ b/src/workers/continuum-core/src/cognition/shared_analysis/prompt.rs @@ -127,17 +127,17 @@ pub(super) fn build_prompt(input: &AnalysisInput) -> String { // ── Header + history ──────────────────────────────────────────── buf.push_str("Recent conversation:\n"); - let history_count = input - .recent_history - .len() - .min(HISTORY_SNAPSHOT_SIZE); + let history_count = input.recent_history.len().min(HISTORY_SNAPSHOT_SIZE); if history_count == 0 { buf.push_str("(no prior messages)\n"); } else { // Same logical slice as `iter().rev().take(N).rev()`: the LAST // N messages in chronological order. Compute the start index // directly to avoid the double-rev allocation pattern. - let start = input.recent_history.len().saturating_sub(HISTORY_SNAPSHOT_SIZE); + let start = input + .recent_history + .len() + .saturating_sub(HISTORY_SNAPSHOT_SIZE); for m in &input.recent_history[start..] { sanitize_into(&mut buf, &m.sender_name); buf.push_str(": "); diff --git a/src/workers/continuum-core/src/cognition/tool_embedding.rs b/src/workers/continuum-core/src/cognition/tool_embedding.rs index ec0e464dc..fcf618ff4 100644 --- a/src/workers/continuum-core/src/cognition/tool_embedding.rs +++ b/src/workers/continuum-core/src/cognition/tool_embedding.rs @@ -263,9 +263,7 @@ pub enum ToolEmbeddingError { CacheEmpty, /// Provider returned fewer embedding vectors than requested. Pins /// the wire contract; partial responses are typed errors here. - #[error( - "provider returned {got} embeddings, expected {expected} (1 per requested tool)" - )] + #[error("provider returned {got} embeddings, expected {expected} (1 per requested tool)")] EmbeddingCountMismatch { got: usize, expected: usize }, } @@ -404,13 +402,9 @@ pub async fn semantic_search_tools( .await .map_err(ToolEmbeddingError::EmbeddingFailed)?; - let query_vector = response - .embeddings - .into_iter() - .next() - .ok_or_else(|| { - ToolEmbeddingError::EmbeddingFailed("provider returned no query embedding".to_string()) - })?; + let query_vector = response.embeddings.into_iter().next().ok_or_else(|| { + ToolEmbeddingError::EmbeddingFailed("provider returned no query embedding".to_string()) + })?; let mut results: Vec = cached_embeddings .iter() diff --git a/src/workers/continuum-core/src/cognition/tool_executor/types.rs b/src/workers/continuum-core/src/cognition/tool_executor/types.rs index 2e2956955..ceae57484 100644 --- a/src/workers/continuum-core/src/cognition/tool_executor/types.rs +++ b/src/workers/continuum-core/src/cognition/tool_executor/types.rs @@ -228,10 +228,7 @@ pub struct ParsedToolBatch { // can `if (err.error === 'ToolNotFound')` directly. `data` holds // the structured fields. Same pattern as `AdmissionDecision`. #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../../shared/generated/cognition/ToolError.ts" -)] +#[ts(export, export_to = "../../../shared/generated/cognition/ToolError.ts")] #[serde(tag = "error", content = "data")] pub enum ToolError { /// Caller named a tool that isn't in the registry. @@ -276,8 +273,14 @@ impl std::fmt::Display for ToolError { ToolError::Forbidden { tool, reason } => { write!(f, "tool '{tool}' forbidden: {reason}") } - ToolError::ParseFailed { raw_preview, reason } => { - write!(f, "tool parse failed ({reason}); raw preview: {raw_preview}") + ToolError::ParseFailed { + raw_preview, + reason, + } => { + write!( + f, + "tool parse failed ({reason}); raw preview: {raw_preview}" + ) } ToolError::StoreFailed { tool, underlying } => { write!(f, "tool '{tool}' store failed: {underlying}") diff --git a/src/workers/continuum-core/src/cognition/turn_batch.rs b/src/workers/continuum-core/src/cognition/turn_batch.rs index e128378b9..fefd6a391 100644 --- a/src/workers/continuum-core/src/cognition/turn_batch.rs +++ b/src/workers/continuum-core/src/cognition/turn_batch.rs @@ -291,10 +291,14 @@ pub fn plan_turn_batch(req: RecipeTurnBatchRequest) -> RecipeTurnBatchPlan { .max() .unwrap_or(0); - let first_response_budget_ms = - effective_budget_ms(req.first_response_budget_ms, default_first_response_budget_ms()); - let all_responses_budget_ms = - effective_budget_ms(req.all_responses_budget_ms, default_all_responses_budget_ms()); + let first_response_budget_ms = effective_budget_ms( + req.first_response_budget_ms, + default_first_response_budget_ms(), + ); + let all_responses_budget_ms = effective_budget_ms( + req.all_responses_budget_ms, + default_all_responses_budget_ms(), + ); RecipeTurnBatchPlan { turn_key, @@ -611,21 +615,13 @@ mod tests { let mut req = request(); req.local_inference_capacity = 1; req.personas = vec![ - candidate( - "11111111-1111-4111-8111-111111111111", - "Local One", - "local", - ), + candidate("11111111-1111-4111-8111-111111111111", "Local One", "local"), candidate( "22222222-2222-4222-8222-222222222222", "Cloud One", "anthropic", ), - candidate( - "33333333-3333-4333-8333-333333333333", - "Local Two", - "local", - ), + candidate("33333333-3333-4333-8333-333333333333", "Local Two", "local"), ]; req.personas[1].model = "claude-opus-4.1".to_string(); diff --git a/src/workers/continuum-core/src/cognition/validate_response.rs b/src/workers/continuum-core/src/cognition/validate_response.rs index a346a7517..cec822ba9 100644 --- a/src/workers/continuum-core/src/cognition/validate_response.rs +++ b/src/workers/continuum-core/src/cognition/validate_response.rs @@ -302,7 +302,10 @@ mod tests { #[test] fn parse_clarify_wins_when_present() { assert_eq!(parse_decision("CLARIFY"), ResponseDecision::Clarify); - assert_eq!(parse_decision("clarify, not sure"), ResponseDecision::Clarify); + assert_eq!( + parse_decision("clarify, not sure"), + ResponseDecision::Clarify + ); } /// SILENT recognized over SUBMIT, but CLARIFY takes precedence over @@ -360,7 +363,10 @@ mod tests { assert_eq!(g.model.as_deref(), Some(DEFAULT_VALIDATE_MODEL)); assert_eq!(g.temperature, Some(VALIDATE_TEMPERATURE)); assert_eq!(g.max_tokens, Some(VALIDATE_MAX_TOKENS)); - assert_eq!(g.purpose.as_deref(), Some("cognition/validate-response-decision")); + assert_eq!( + g.purpose.as_deref(), + Some("cognition/validate-response-decision") + ); assert_eq!(g.messages.len(), 2); assert_eq!(g.messages[0].role, "system"); assert_eq!(g.messages[1].role, "user"); diff --git a/src/workers/continuum-core/src/cognition/vision_describe.rs b/src/workers/continuum-core/src/cognition/vision_describe.rs index 007b097b2..a7a943c06 100644 --- a/src/workers/continuum-core/src/cognition/vision_describe.rs +++ b/src/workers/continuum-core/src/cognition/vision_describe.rs @@ -181,8 +181,7 @@ fn select_vision_model(opts: &VisionDescribeOptions) -> Option<(String, String)> }) .collect(); - pick_vision_candidate(&candidates, opts) - .map(|c| (c.model_id.clone(), c.provider_id.clone())) + pick_vision_candidate(&candidates, opts).map(|c| (c.model_id.clone(), c.provider_id.clone())) } /// Build the describe prompt from option flags. diff --git a/src/workers/continuum-core/src/concurrency/policy.rs b/src/workers/continuum-core/src/concurrency/policy.rs index 3939e8e7b..70c98825e 100644 --- a/src/workers/continuum-core/src/concurrency/policy.rs +++ b/src/workers/continuum-core/src/concurrency/policy.rs @@ -360,13 +360,18 @@ mod tests { // the panic. With the guard, the key is fresh and the new // work runs cleanly. let result = policy - .single_flight( - key.clone(), - async move { Ok::(99) }.boxed(), - ) + .single_flight(key.clone(), async move { Ok::(99) }.boxed()) .await; - assert_eq!(result, Ok(99), "second call after panic should succeed cleanly"); - assert_eq!(policy.in_flight_count(), 0, "second call should also clean up"); + assert_eq!( + result, + Ok(99), + "second call after panic should succeed cleanly" + ); + assert_eq!( + policy.in_flight_count(), + 0, + "second call should also clean up" + ); } /// What this catches: regression in the #1235 fix. The previous diff --git a/src/workers/continuum-core/src/events/event_class.rs b/src/workers/continuum-core/src/events/event_class.rs index cb1fbb2d2..c2f0b907c 100644 --- a/src/workers/continuum-core/src/events/event_class.rs +++ b/src/workers/continuum-core/src/events/event_class.rs @@ -24,7 +24,10 @@ use ts_rs::TS; /// separately from the Rust-canonical config.) #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/events/EventClassChannelStrategy.ts")] +#[ts( + export, + export_to = "../../../shared/generated/events/EventClassChannelStrategy.ts" +)] pub enum EventClassChannelStrategy { Local, Global, @@ -38,7 +41,10 @@ pub enum EventClassChannelStrategy { /// of never silently swallowing evidence. #[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/events/EventClassUnknownSchemaPolicy.ts")] +#[ts( + export, + export_to = "../../../shared/generated/events/EventClassUnknownSchemaPolicy.ts" +)] pub enum EventClassUnknownSchemaPolicy { Warn, #[default] @@ -49,7 +55,10 @@ pub enum EventClassUnknownSchemaPolicy { /// conservative defaults (no broadcast, no airc cost). #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/events/EventClassConfig.ts")] +#[ts( + export, + export_to = "../../../shared/generated/events/EventClassConfig.ts" +)] pub struct EventClassConfig { /// Distribute this event class through the airc transport in addition /// to the local + WebSocket transports? @@ -89,7 +98,10 @@ pub struct EventClassConfig { /// What the registry stores + what the TS side caches. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/events/ResolvedEventClassConfig.ts")] +#[ts( + export, + export_to = "../../../shared/generated/events/ResolvedEventClassConfig.ts" +)] pub struct ResolvedEventClassConfig { pub name: String, pub broadcast: bool, @@ -150,14 +162,12 @@ pub fn resolve_event_class_config( } let broadcast = config.broadcast; - let channel = config - .channel - .unwrap_or(if broadcast { - // Will fail validation below — broadcast requires explicit channel. - EventClassChannelStrategy::Local - } else { - EventClassChannelStrategy::Local - }); + let channel = config.channel.unwrap_or(if broadcast { + // Will fail validation below — broadcast requires explicit channel. + EventClassChannelStrategy::Local + } else { + EventClassChannelStrategy::Local + }); if broadcast && channel == EventClassChannelStrategy::Local { return Err(EventClassDeclareError::BroadcastWithoutChannel { @@ -217,8 +227,8 @@ mod tests { #[test] fn resolves_broadcast_global() { - let r = resolve_event_class_config("presence:peer-manifest", &cfg_broadcast_global()) - .unwrap(); + let r = + resolve_event_class_config("presence:peer-manifest", &cfg_broadcast_global()).unwrap(); assert!(r.broadcast); assert_eq!(r.channel, EventClassChannelStrategy::Global); } diff --git a/src/workers/continuum-core/src/events/event_class_registry.rs b/src/workers/continuum-core/src/events/event_class_registry.rs index f1bfc6de8..5117c2f0b 100644 --- a/src/workers/continuum-core/src/events/event_class_registry.rs +++ b/src/workers/continuum-core/src/events/event_class_registry.rs @@ -146,28 +146,24 @@ impl EventClassRegistry { .cloned() .ok_or_else(|| EventClassChannelResolveError::Undeclared(name.to_string()))?; if !entry.config.broadcast { - return Err(EventClassChannelResolveError::NotBroadcast(name.to_string())); + return Err(EventClassChannelResolveError::NotBroadcast( + name.to_string(), + )); } match entry.config.channel { EventClassChannelStrategy::Global => Ok("global".to_string()), - EventClassChannelStrategy::ByRoomId => { - extract_string_field(payload, "roomId").ok_or_else(|| { - EventClassChannelResolveError::MissingPayloadField { - name: name.to_string(), - channel: EventClassChannelStrategy::ByRoomId, - required_field: "roomId", - } - }) - } - EventClassChannelStrategy::ByPeerId => { - extract_string_field(payload, "peerId").ok_or_else(|| { - EventClassChannelResolveError::MissingPayloadField { - name: name.to_string(), - channel: EventClassChannelStrategy::ByPeerId, - required_field: "peerId", - } - }) - } + EventClassChannelStrategy::ByRoomId => extract_string_field(payload, "roomId") + .ok_or_else(|| EventClassChannelResolveError::MissingPayloadField { + name: name.to_string(), + channel: EventClassChannelStrategy::ByRoomId, + required_field: "roomId", + }), + EventClassChannelStrategy::ByPeerId => extract_string_field(payload, "peerId") + .ok_or_else(|| EventClassChannelResolveError::MissingPayloadField { + name: name.to_string(), + channel: EventClassChannelStrategy::ByPeerId, + required_field: "peerId", + }), EventClassChannelStrategy::Custom => { Err(EventClassChannelResolveError::CustomResolverUnsupported { name: name.to_string(), @@ -333,7 +329,9 @@ mod tests { let err = r.declare("foo:bar", &conflict).unwrap_err(); assert!(matches!( err, - EventClassRegistryError::Declare(EventClassDeclareError::ConflictingRedeclaration { .. }) + EventClassRegistryError::Declare( + EventClassDeclareError::ConflictingRedeclaration { .. } + ) )); } @@ -380,7 +378,10 @@ mod tests { .unwrap_err(); assert!(matches!( err, - EventClassChannelResolveError::MissingPayloadField { required_field: "roomId", .. } + EventClassChannelResolveError::MissingPayloadField { + required_field: "roomId", + .. + } )); } @@ -400,7 +401,10 @@ mod tests { let err = r .resolve_channel("widget:mounted", &serde_json::json!({})) .unwrap_err(); - assert!(matches!(err, EventClassChannelResolveError::NotBroadcast(_))); + assert!(matches!( + err, + EventClassChannelResolveError::NotBroadcast(_) + )); } #[test] diff --git a/src/workers/continuum-core/src/forge/artifact.rs b/src/workers/continuum-core/src/forge/artifact.rs index 2fe15f761..471d99133 100644 --- a/src/workers/continuum-core/src/forge/artifact.rs +++ b/src/workers/continuum-core/src/forge/artifact.rs @@ -30,7 +30,9 @@ use serde::{Deserialize, Serialize}; use ts_rs::TS; use uuid::Uuid; -use super::recipe::{AlloyHardware, AlloySource, BenchmarkDef, CorpusRef, PriorBaseline, QuantTier}; +use super::recipe::{ + AlloyHardware, AlloySource, BenchmarkDef, CorpusRef, PriorBaseline, QuantTier, +}; //============================================================================= // HARDWARE PROFILE — verified post-run @@ -43,7 +45,10 @@ use super::recipe::{AlloyHardware, AlloySource, BenchmarkDef, CorpusRef, PriorBa /// Mirrors the existing Python `HardwareProfile` shape; Phase 2 makes /// the Rust type the source of truth. #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts(export, export_to = "../../../shared/generated/forge/HardwareProfile.ts")] +#[ts( + export, + export_to = "../../../shared/generated/forge/HardwareProfile.ts" +)] pub struct HardwareProfile { /// Device label (e.g., "m5-pro", "rtx-5090", "linux-amd64"). pub device: String, @@ -78,14 +83,12 @@ pub struct HardwareProfile { #[ts(export, export_to = "../../../shared/generated/forge/ForgeArtifact.ts")] pub struct ForgeArtifact { //--- Identity ---------------------------------------------------------- - /// Stable artifact id (different from recipe id — one recipe can /// produce many artifacts across multiple runs / hardware tiers). #[ts(type = "string")] pub id: Uuid, //--- Recipe lineage (frozen at run time) ------------------------------ - /// Which recipe produced this artifact. #[ts(type = "string")] pub recipe_id: Uuid, @@ -106,7 +109,6 @@ pub struct ForgeArtifact { // field after this artifact was forged, this artifact's snapshot // stays as-was — the recipe lineage points to the recipe-version // that was current at run time. - /// Paragraph for the README/card. pub description: String, /// One-line plain-English headline. @@ -141,7 +143,6 @@ pub struct ForgeArtifact { pub hardware: AlloyHardware, //--- Execution outputs (only the foundry knows these) ----------------- - /// When the foundry started this run (epoch milliseconds UTC). #[ts(type = "number")] pub forged_at_ms: u64, @@ -328,7 +329,10 @@ mod tests { let back: ForgeArtifact = serde_json::from_str(&json).expect("deserialize"); assert!(back.results.is_none()); assert!(back.alloy_hash.is_none()); - assert_eq!(back.recipe_id, artifact.recipe_id, "lineage preserved even on partial"); + assert_eq!( + back.recipe_id, artifact.recipe_id, + "lineage preserved even on partial" + ); } /// What this catches: recipe_id + recipe_version pinning means a @@ -340,7 +344,10 @@ mod tests { // recipe_id + recipe_version + recipe_name. This test is the // runtime spec that they're populated. let artifact = sample_artifact(); - assert!(!artifact.recipe_version.is_empty(), "recipe_version is required"); + assert!( + !artifact.recipe_version.is_empty(), + "recipe_version is required" + ); assert!(!artifact.recipe_name.is_empty(), "recipe_name is required"); } diff --git a/src/workers/continuum-core/src/forge/recipe.rs b/src/workers/continuum-core/src/forge/recipe.rs index 4d2aab1a1..efdaf8f6c 100644 --- a/src/workers/continuum-core/src/forge/recipe.rs +++ b/src/workers/continuum-core/src/forge/recipe.rs @@ -198,7 +198,6 @@ pub struct AlloyHardware { #[ts(export, export_to = "../../../shared/generated/forge/ForgeRecipe.ts")] pub struct ForgeRecipe { //--- Identity ---------------------------------------------------------- - /// Stable recipe identifier. Generated at recipe creation time. #[ts(type = "string")] pub id: Uuid, @@ -230,7 +229,6 @@ pub struct ForgeRecipe { pub license: String, //--- Methodology / falsifiability prose -------------------------------- - /// Optional link to the methodology paper. #[ts(optional)] pub methodology_paper_url: Option, @@ -244,12 +242,10 @@ pub struct ForgeRecipe { pub prior_metric_baselines: Vec, //--- Source ----------------------------------------------------------- - /// Base model + architecture metadata. pub source: AlloySource, //--- Pipeline --------------------------------------------------------- - /// Ordered pipeline of recipe stages. v1 carries stages as opaque /// JSON values matching the existing `AlloyStage` discriminated /// union in `forge-alloy/python/forge_alloy/types.py`. Phase 2 @@ -265,7 +261,6 @@ pub struct ForgeRecipe { pub cycles: u32, //--- Calibration / eval inputs ---------------------------------------- - /// Held-out corpus pointer (importance profile + LoRA training). pub calibration_corpus: CorpusRef, @@ -280,12 +275,10 @@ pub struct ForgeRecipe { pub evaluation_benchmarks: Vec, //--- Hardware target -------------------------------------------------- - /// Target hardware envelope (VRAM, device list, CPU fallback). pub hardware: AlloyHardware, //--- Lineage ---------------------------------------------------------- - /// Parent recipe id, if this recipe was forked from another. None /// for net-new recipes. v1 lineage is one-directional (recipe → /// recipe); bidirectional lineage (recipe ← artifact) is a future @@ -294,7 +287,6 @@ pub struct ForgeRecipe { pub parent_recipe_id: Option, //--- Timestamps ------------------------------------------------------- - /// When the recipe was authored (epoch milliseconds UTC). Same /// convention as `Engram.admitted_at_ms` from the engram thread — /// `u64` epoch ms, not chrono::DateTime. @@ -362,7 +354,11 @@ mod tests { calibration_corpus: sample_corpus(), quant_tiers: vec![QuantTier { format: "gguf".to_string(), - variants: vec!["Q4_K_M".to_string(), "Q5_K_M".to_string(), "Q8_0".to_string()], + variants: vec![ + "Q4_K_M".to_string(), + "Q5_K_M".to_string(), + "Q8_0".to_string(), + ], target_devices: vec!["m1-8gb".to_string(), "m5-pro".to_string()], }], evaluation_benchmarks: vec![BenchmarkDef { diff --git a/src/workers/continuum-core/src/genome/blob.rs b/src/workers/continuum-core/src/genome/blob.rs index 3fbd1e8a2..56b7d4edd 100644 --- a/src/workers/continuum-core/src/genome/blob.rs +++ b/src/workers/continuum-core/src/genome/blob.rs @@ -73,10 +73,7 @@ impl ArtifactBlob { /// minimum. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/Provenance.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/Provenance.ts")] pub struct Provenance { pub artifact_id: ArtifactId, #[ts(type = "number")] diff --git a/src/workers/continuum-core/src/genome/bus.rs b/src/workers/continuum-core/src/genome/bus.rs index 1bf631963..44f6034c7 100644 --- a/src/workers/continuum-core/src/genome/bus.rs +++ b/src/workers/continuum-core/src/genome/bus.rs @@ -80,11 +80,7 @@ pub const ACCESS_DENIED_KEY: &str = "genome/working_set.access_denied"; /// serialize cleanly, so a failure here would indicate substrate /// corruption, not a user-visible bug. The trace bus still fires /// (with empty payload) so subscribers see something happened. -pub async fn publish_page_fault( - bus: &MessageBus, - registry: &ModuleRegistry, - fault: &PageFault, -) { +pub async fn publish_page_fault(bus: &MessageBus, registry: &ModuleRegistry, fault: &PageFault) { let payload = serde_json::to_value(fault).unwrap_or(serde_json::Value::Null); bus.publish(PAGE_FAULT_KEY, payload, registry).await; } @@ -157,9 +153,7 @@ mod tests { //! dispatch path end-to-end for genome events. use super::*; use crate::genome::tier::{EvictionPolicy, TierRole}; - use crate::genome::working_set::{ - ArtifactId, PageKind, PageOffset, PageRef, PersonaId, - }; + use crate::genome::working_set::{ArtifactId, PageKind, PageOffset, PageRef, PersonaId}; use crate::runtime::runtime::Runtime; use crate::runtime::service_module::{ CommandResult, ModuleConfig, ModulePriority, ServiceModule, @@ -202,10 +196,7 @@ mod tests { tick_interval: None, } } - async fn initialize( - &self, - _ctx: &crate::runtime::ModuleContext, - ) -> Result<(), String> { + async fn initialize(&self, _ctx: &crate::runtime::ModuleContext) -> Result<(), String> { Ok(()) } async fn handle_command( @@ -223,7 +214,9 @@ mod tests { key: &ArtifactKey, payload: serde_json::Value, ) -> Result<(), String> { - self.captured.lock().push((key.as_str().to_string(), payload)); + self.captured + .lock() + .push((key.as_str().to_string(), payload)); Ok(()) } fn as_any(&self) -> &dyn Any { @@ -299,10 +292,7 @@ mod tests { publish_page_fault(runtime.bus(), runtime.registry(), &fault).await; let events = captured.lock().clone(); - let fault_events: Vec<_> = events - .iter() - .filter(|(k, _)| k == PAGE_FAULT_KEY) - .collect(); + let fault_events: Vec<_> = events.iter().filter(|(k, _)| k == PAGE_FAULT_KEY).collect(); assert_eq!(fault_events.len(), 1); let (_, payload) = fault_events[0]; // Payload round-trips back into PageFault — the serde shape @@ -336,8 +326,7 @@ mod tests { .filter(|(k, _)| k == EVICTION_RECORD_KEY) .collect(); assert_eq!(evict_events.len(), 1); - let back: EvictionRecord = - serde_json::from_value(evict_events[0].1.clone()).unwrap(); + let back: EvictionRecord = serde_json::from_value(evict_events[0].1.clone()).unwrap(); assert_eq!(back, record); } @@ -365,8 +354,7 @@ mod tests { .filter(|(k, _)| k == ACCESS_DENIED_KEY) .collect(); assert_eq!(denied_events.len(), 1); - let back: AccessDenied = - serde_json::from_value(denied_events[0].1.clone()).unwrap(); + let back: AccessDenied = serde_json::from_value(denied_events[0].1.clone()).unwrap(); assert_eq!(back, denied); } @@ -440,10 +428,7 @@ mod tests { tick_interval: None, } } - async fn initialize( - &self, - _: &crate::runtime::ModuleContext, - ) -> Result<(), String> { + async fn initialize(&self, _: &crate::runtime::ModuleContext) -> Result<(), String> { Ok(()) } async fn handle_command( @@ -494,7 +479,11 @@ mod tests { publish_eviction_record(runtime.bus(), runtime.registry(), &evict).await; let events = captured.lock().clone(); - assert_eq!(events.len(), 1, "only one event delivered to selective subscriber"); + assert_eq!( + events.len(), + 1, + "only one event delivered to selective subscriber" + ); assert_eq!(events[0], PAGE_FAULT_KEY); } } diff --git a/src/workers/continuum-core/src/genome/local_manager.rs b/src/workers/continuum-core/src/genome/local_manager.rs index 2cdd85698..74296291b 100644 --- a/src/workers/continuum-core/src/genome/local_manager.rs +++ b/src/workers/continuum-core/src/genome/local_manager.rs @@ -170,11 +170,7 @@ impl LocalWorkingSetManager { #[async_trait] impl WorkingSetManager for LocalWorkingSetManager { - async fn page_in( - &self, - persona: PersonaId, - page: PageRef, - ) -> Result { + async fn page_in(&self, persona: PersonaId, page: PageRef) -> Result { // Already resident? — fast path. { let working_sets = self.working_sets.read(); @@ -311,11 +307,7 @@ impl WorkingSetManager for LocalWorkingSetManager { None } - fn audit_access( - &self, - persona: PersonaId, - page: PageRef, - ) -> Result<(), AccessDenied> { + fn audit_access(&self, persona: PersonaId, page: PageRef) -> Result<(), AccessDenied> { let result: Result<(), AccessDenied> = match self.page_owners.read().get(&page).copied() { Some(owner) if owner != persona => Err(AccessDenied { actor: persona, @@ -549,11 +541,7 @@ mod tests { let fast = StubTier::new(TierRole::Fast, vec![]); let bench = StubTier::new(TierRole::Bench, vec![]); let cold = StubTier::new(TierRole::Cold, vec![page]); - let mgr = LocalWorkingSetManager::new(vec![ - fast.clone(), - bench.clone(), - cold.clone(), - ]); + let mgr = LocalWorkingSetManager::new(vec![fast.clone(), bench.clone(), cold.clone()]); let persona = make_persona(8); mgr.register_persona(persona, capacity_uma()); @@ -742,9 +730,7 @@ mod tests { // ─── PR-5 bus-publishing tests ────────────────────────────── - use crate::genome::bus::{ - all_genome_artifact_selectors, ACCESS_DENIED_KEY, PAGE_FAULT_KEY, - }; + use crate::genome::bus::{all_genome_artifact_selectors, ACCESS_DENIED_KEY, PAGE_FAULT_KEY}; use crate::runtime::artifact_handle::{ArtifactKey, ArtifactSelector}; use crate::runtime::runtime::Runtime; use crate::runtime::service_module::{ @@ -781,10 +767,7 @@ mod tests { tick_interval: None, } } - async fn initialize( - &self, - _ctx: &crate::runtime::ModuleContext, - ) -> Result<(), String> { + async fn initialize(&self, _ctx: &crate::runtime::ModuleContext) -> Result<(), String> { Ok(()) } async fn handle_command( @@ -802,7 +785,9 @@ mod tests { key: &ArtifactKey, payload: serde_json::Value, ) -> Result<(), String> { - self.captured.lock().push((key.as_str().to_string(), payload)); + self.captured + .lock() + .push((key.as_str().to_string(), payload)); Ok(()) } fn as_any(&self) -> &dyn Any { @@ -843,8 +828,7 @@ mod tests { async fn page_in_true_cold_miss_with_bus_publishes_page_fault() { let cold = StubTier::new(TierRole::Cold, vec![]); let fast = StubTier::new(TierRole::Fast, vec![]); - let (mgr, _runtime, captured) = - wire_manager_to_runtime(vec![fast, cold]).await; + let (mgr, _runtime, captured) = wire_manager_to_runtime(vec![fast, cold]).await; let persona = make_persona(30); mgr.register_persona(persona, capacity_uma()); @@ -862,10 +846,7 @@ mod tests { } let events = captured.lock().clone(); - let faults: Vec<_> = events - .iter() - .filter(|(k, _)| k == PAGE_FAULT_KEY) - .collect(); + let faults: Vec<_> = events.iter().filter(|(k, _)| k == PAGE_FAULT_KEY).collect(); assert_eq!(faults.len(), 1, "exactly one PageFault published"); let fault: PageFault = serde_json::from_value(faults[0].1.clone()).unwrap(); assert_eq!(fault.from_role, None, "true cold miss has no from_role"); @@ -882,8 +863,7 @@ mod tests { let page = make_page(40); let cold = StubTier::new(TierRole::Cold, vec![page]); let fast = StubTier::new(TierRole::Fast, vec![]); - let (mgr, _runtime, captured) = - wire_manager_to_runtime(vec![fast, cold]).await; + let (mgr, _runtime, captured) = wire_manager_to_runtime(vec![fast, cold]).await; let persona = make_persona(41); mgr.register_persona(persona, capacity_uma()); @@ -898,10 +878,7 @@ mod tests { } let events = captured.lock().clone(); - let faults: Vec<_> = events - .iter() - .filter(|(k, _)| k == PAGE_FAULT_KEY) - .collect(); + let faults: Vec<_> = events.iter().filter(|(k, _)| k == PAGE_FAULT_KEY).collect(); assert_eq!(faults.len(), 1); let fault: PageFault = serde_json::from_value(faults[0].1.clone()).unwrap(); assert_eq!(fault.from_role, Some(TierRole::Cold)); @@ -930,7 +907,11 @@ mod tests { } } assert_eq!( - captured.lock().iter().filter(|(k, _)| k == PAGE_FAULT_KEY).count(), + captured + .lock() + .iter() + .filter(|(k, _)| k == PAGE_FAULT_KEY) + .count(), 1 ); @@ -942,7 +923,11 @@ mod tests { tokio::task::yield_now().await; } assert_eq!( - captured.lock().iter().filter(|(k, _)| k == PAGE_FAULT_KEY).count(), + captured + .lock() + .iter() + .filter(|(k, _)| k == PAGE_FAULT_KEY) + .count(), 1, "resident-hit path must not publish" ); @@ -985,8 +970,7 @@ mod tests { .filter(|(k, _)| k == ACCESS_DENIED_KEY) .collect(); assert_eq!(denied_events.len(), 1, "exactly one AccessDenied published"); - let denied: AccessDenied = - serde_json::from_value(denied_events[0].1.clone()).unwrap(); + let denied: AccessDenied = serde_json::from_value(denied_events[0].1.clone()).unwrap(); assert_eq!(denied.actor, intruder); assert_eq!(denied.owner, Some(owner)); } diff --git a/src/workers/continuum-core/src/genome/manager.rs b/src/workers/continuum-core/src/genome/manager.rs index 6ed32644d..e97e36fd5 100644 --- a/src/workers/continuum-core/src/genome/manager.rs +++ b/src/workers/continuum-core/src/genome/manager.rs @@ -37,9 +37,7 @@ use async_trait::async_trait; use super::tier::{TierError, TierRole}; -use super::working_set::{ - AccessDenied, PageFault, PageHandle, PageRef, PersonaId, WorkingSet, -}; +use super::working_set::{AccessDenied, PageFault, PageHandle, PageRef, PersonaId, WorkingSet}; /// The single trait every working-set implementation satisfies. The /// PR-3 implementor will be a per-substrate-process singleton holding @@ -63,11 +61,7 @@ pub trait WorkingSetManager: Send + Sync { /// as success-with-trace-event. A future PR may relax this /// signature (e.g. return `Result<(PageHandle, Option), /// TierError>`) if downstream feedback wants both. - async fn page_in( - &self, - persona: PersonaId, - page: PageRef, - ) -> Result; + async fn page_in(&self, persona: PersonaId, page: PageRef) -> Result; /// Demote a page out of the working set toward the named tier /// role. Used by composition when it's done with a page (e.g. @@ -108,11 +102,7 @@ pub trait WorkingSetManager: Send + Sync { /// log, regardless of whether the calling persona caught + logged /// it itself. Compartmentalization audit trail per /// GENOME-FOUNDRY-SENTINEL Part 4. - fn audit_access( - &self, - persona: PersonaId, - page: PageRef, - ) -> Result<(), AccessDenied>; + fn audit_access(&self, persona: PersonaId, page: PageRef) -> Result<(), AccessDenied>; } #[cfg(test)] @@ -124,9 +114,7 @@ mod tests { //! against real semantics; PR-2 only proves the seam. use super::*; - use crate::genome::working_set::{ - ArtifactId, PageKind, PageOffset, WorkingSetCapacity, - }; + use crate::genome::working_set::{ArtifactId, PageKind, PageOffset, WorkingSetCapacity}; use std::collections::HashMap; use std::sync::Arc; use uuid::Uuid; @@ -170,19 +158,13 @@ mod tests { self.working_sets.get(&persona) } - fn audit_access( - &self, - persona: PersonaId, - page: PageRef, - ) -> Result<(), AccessDenied> { + fn audit_access(&self, persona: PersonaId, page: PageRef) -> Result<(), AccessDenied> { match self.page_owners.get(&page) { Some(owner) if *owner != persona => Err(AccessDenied { actor: persona, page, owner: Some(*owner), - reason: format!( - "cross-persona read attempt blocked by working-set MMU" - ), + reason: format!("cross-persona read attempt blocked by working-set MMU"), }), _ => Ok(()), } diff --git a/src/workers/continuum-core/src/genome/recall.rs b/src/workers/continuum-core/src/genome/recall.rs index 550a719eb..04fff5748 100644 --- a/src/workers/continuum-core/src/genome/recall.rs +++ b/src/workers/continuum-core/src/genome/recall.rs @@ -107,15 +107,21 @@ impl PeerId { export_to = "../../../shared/generated/genome/ResidencyHint.ts" )] pub enum ResidencyHint { - Hot { role: TierRole }, - Local { role: TierRole }, + Hot { + role: TierRole, + }, + Local { + role: TierRole, + }, GridPeer { peer: PeerId, #[serde(rename = "estLatencyMs")] #[ts(rename = "estLatencyMs", type = "number")] est_latency_ms: u32, }, - NotResident { acquirable_from: AcquireSource }, + NotResident { + acquirable_from: AcquireSource, + }, } /// Where the substrate would have to get an artifact from if it @@ -153,10 +159,7 @@ pub enum AcquireSource { /// bounded; defaults sum to 1.0). #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/RecallScore.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/RecallScore.ts")] pub struct RecallScore { /// Cosine similarity between query embedding and artifact /// metadata embedding. Range [0.0, 1.0]; 1.0 = identical. @@ -187,10 +190,7 @@ pub struct RecallScore { /// federation-scope plumbing through every caller. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(tag = "kind", rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/RecallScope.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/RecallScope.ts")] pub enum RecallScope { /// Never leave this machine. Fastest; may return a thinner /// RankedPool if local artifacts don't cover the query well. @@ -251,10 +251,7 @@ pub enum FreshnessTarget { /// hasn't named — recall treats them with default weights. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/TaskKind.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/TaskKind.ts")] pub enum TaskKind { Chat, Code, @@ -271,10 +268,7 @@ pub enum TaskKind { /// can map a peer to. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/TrustClass.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/TrustClass.ts")] pub enum TrustClass { /// The persona's own artifacts. Always full trust. Local, @@ -295,10 +289,7 @@ pub enum TrustClass { /// context needed to debug. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)] #[serde(tag = "kind", rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/RecallError.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/RecallError.ts")] pub enum RecallError { /// The query's resource budget couldn't be satisfied by any /// combination of available artifacts. @@ -395,12 +386,16 @@ mod tests { /// rename of a variant breaks every consumer. #[test] fn residency_hint_serializes_with_kind_tag() { - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let j = serde_json::to_string(&hot).unwrap(); assert!(j.contains("\"kind\":\"hot\""), "got {j}"); assert!(j.contains("\"role\":\"fast\""), "got {j}"); - let local = ResidencyHint::Local { role: TierRole::Cold }; + let local = ResidencyHint::Local { + role: TierRole::Cold, + }; let j = serde_json::to_string(&local).unwrap(); assert!(j.contains("\"kind\":\"local\""), "got {j}"); assert!(j.contains("\"role\":\"cold\""), "got {j}"); diff --git a/src/workers/continuum-core/src/genome/recall_impl.rs b/src/workers/continuum-core/src/genome/recall_impl.rs index 3d8d4e1c7..2649edb00 100644 --- a/src/workers/continuum-core/src/genome/recall_impl.rs +++ b/src/workers/continuum-core/src/genome/recall_impl.rs @@ -51,8 +51,7 @@ use super::recall::{RecallError, RecallScore, ResidencyHint}; use super::recall_scoring::{score, DEFAULT_RECENCY_HALF_LIFE_MS}; use super::recall_trait::{ CapabilityQuery, CompositionHint, DemandAlignedRecall, EngramRef, LoRALayerRef, MoEExpertRef, - RankedPool, RecallContext, RecallScoreWeights, - RecallTrace, + RankedPool, RecallContext, RecallScoreWeights, RecallTrace, }; use super::working_set::{ArtifactId, PageKind}; @@ -211,11 +210,7 @@ impl LocalDemandAlignedRecall { /// `SystemTime::now`) so callers can replay with snapshotted /// clocks — the spec requires replay determinism, and reading /// `now()` inside the ranker would break that. - pub fn rank( - &self, - now_ms: u64, - candidates: Vec, - ) -> RankedPool { + pub fn rank(&self, now_ms: u64, candidates: Vec) -> RankedPool { let mut layers: Vec<(LoRALayerRef, RecallScore, ResidencyHint)> = Vec::new(); let mut experts: Vec<(MoEExpertRef, RecallScore, ResidencyHint)> = Vec::new(); let mut engrams: Vec<(EngramRef, RecallScore, ResidencyHint)> = Vec::new(); @@ -238,9 +233,7 @@ impl LocalDemandAlignedRecall { PageKind::MoEExpert => { experts.push((MoEExpertRef(c.artifact_id), scored, c.residency)) } - PageKind::Engram => { - engrams.push((EngramRef(c.artifact_id), scored, c.residency)) - } + PageKind::Engram => engrams.push((EngramRef(c.artifact_id), scored, c.residency)), PageKind::KVCache => { // Spec's RankedPool has three sub-pools; KV // cache pages are working-set state, not recall @@ -437,7 +430,9 @@ mod tests { #[test] fn rank_partitions_by_kind_into_correct_sub_pool() { let r = LocalDemandAlignedRecall::new(); - let residency = ResidencyHint::Hot { role: TierRole::Fast }; + let residency = ResidencyHint::Hot { + role: TierRole::Fast, + }; let candidates = vec![ cand(PageKind::LoRALayer, 1, 0.9, 0.5, residency.clone()), cand(PageKind::MoEExpert, 2, 0.8, 0.5, residency.clone()), @@ -459,7 +454,9 @@ mod tests { #[test] fn rank_sorts_each_sub_pool_descending_by_combined() { let r = LocalDemandAlignedRecall::new(); - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let candidates = vec![ // Lower semantic cand(PageKind::LoRALayer, 10, 0.2, 0.5, hot.clone()), @@ -493,7 +490,9 @@ mod tests { #[test] fn rank_silently_drops_kvcache_candidates() { let r = LocalDemandAlignedRecall::new(); - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let candidates = vec![ cand(PageKind::LoRALayer, 1, 0.9, 0.5, hot.clone()), cand(PageKind::KVCache, 2, 0.9, 0.5, hot.clone()), @@ -513,7 +512,9 @@ mod tests { #[test] fn rank_score_factors_match_pr3a_for_each_candidate() { let r = LocalDemandAlignedRecall::new(); - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let candidates = vec![cand(PageKind::LoRALayer, 1, 0.9, 0.8, hot.clone())]; let now = 1_000_000; let pool = r.rank(now, candidates); @@ -535,7 +536,9 @@ mod tests { #[test] fn rank_is_deterministic_across_calls() { let r = LocalDemandAlignedRecall::new(); - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let candidates = vec![ cand(PageKind::LoRALayer, 1, 0.9, 0.5, hot.clone()), cand(PageKind::LoRALayer, 2, 0.5, 0.5, hot), @@ -553,7 +556,9 @@ mod tests { #[test] fn rank_includes_not_resident_candidates_at_lower_score() { let r = LocalDemandAlignedRecall::new(); - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; let not_res = ResidencyHint::NotResident { acquirable_from: AcquireSource::SentinelRefinement, }; @@ -586,21 +591,27 @@ mod tests { 1, 0.5, 0.5, - ResidencyHint::Local { role: TierRole::Frozen }, + ResidencyHint::Local { + role: TierRole::Frozen, + }, ), cand( PageKind::LoRALayer, 2, 0.5, 0.5, - ResidencyHint::Hot { role: TierRole::Fast }, + ResidencyHint::Hot { + role: TierRole::Fast, + }, ), cand( PageKind::LoRALayer, 3, 0.5, 0.5, - ResidencyHint::Local { role: TierRole::Bench }, + ResidencyHint::Local { + role: TierRole::Bench, + }, ), ]; let pool = r.rank(1000, candidates); @@ -640,10 +651,10 @@ mod tests { // ─── PR-3c: trait impl + CandidateSource tests ───────────── + use crate::genome::recall::{FreshnessTarget, RecallError, RecallScope, TaskKind}; use crate::genome::recall_trait::{ CapabilityQuery, DemandAlignedRecall, DomainHint, RecallBudget, RecallContext, RecallTrace, }; - use crate::genome::recall::{FreshnessTarget, RecallError, RecallScope, TaskKind}; use crate::genome::working_set::PersonaId; use parking_lot::Mutex; @@ -703,8 +714,7 @@ mod tests { /// use. #[tokio::test] async fn recall_dispatches_through_dyn_demand_aligned_recall() { - let recall: Arc = - Arc::new(LocalDemandAlignedRecall::new()); + let recall: Arc = Arc::new(LocalDemandAlignedRecall::new()); let ctx = RecallContext::cold_start(sample_persona()); let pool = recall.recall(&sample_query(), &ctx).await.unwrap(); assert!(pool.layers.is_empty()); @@ -730,7 +740,9 @@ mod tests { /// source's canned candidates land in the resulting pool. #[tokio::test] async fn recall_with_source_dispatches_to_fetch_and_ranks() { - let hot = ResidencyHint::Hot { role: super::super::tier::TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: super::super::tier::TierRole::Fast, + }; let cand = CandidateArtifact { kind: PageKind::LoRALayer, artifact_id: ArtifactId::new(Uuid::from_u128(42)), @@ -748,7 +760,7 @@ mod tests { assert_eq!(source.fetch_count(), 1, "source.fetch must be called once"); assert_eq!(pool.layers.len(), 1); - assert_eq!(pool.layers[0].0.0.as_uuid(), Uuid::from_u128(42)); + assert_eq!(pool.layers[0].0 .0.as_uuid(), Uuid::from_u128(42)); } /// What this catches: with_config_and_source preserves all diff --git a/src/workers/continuum-core/src/genome/recall_scoring.rs b/src/workers/continuum-core/src/genome/recall_scoring.rs index 81e08dd2f..4a3e60203 100644 --- a/src/workers/continuum-core/src/genome/recall_scoring.rs +++ b/src/workers/continuum-core/src/genome/recall_scoring.rs @@ -127,9 +127,7 @@ pub fn tier_proximity_for(residency: &ResidencyHint) -> f32 { match residency { ResidencyHint::Hot { .. } => 1.0, ResidencyHint::Local { role } => local_role_score(*role), - ResidencyHint::GridPeer { - est_latency_ms, .. - } => grid_penalty(*est_latency_ms), + ResidencyHint::GridPeer { est_latency_ms, .. } => grid_penalty(*est_latency_ms), ResidencyHint::NotResident { .. } => 0.0, } } @@ -373,10 +371,14 @@ mod tests { /// GridPeer=grid_penalty, NotResident=0.0. #[test] fn tier_proximity_dispatches_by_residency_variant() { - let hot = ResidencyHint::Hot { role: TierRole::Fast }; + let hot = ResidencyHint::Hot { + role: TierRole::Fast, + }; assert_eq!(tier_proximity_for(&hot), 1.0); - let local = ResidencyHint::Local { role: TierRole::Cold }; + let local = ResidencyHint::Local { + role: TierRole::Cold, + }; assert!((tier_proximity_for(&local) - 0.3).abs() < 1e-6); let grid = ResidencyHint::GridPeer { @@ -408,16 +410,18 @@ mod tests { // now > half_life so subtraction doesn't underflow. let now = DEFAULT_RECENCY_HALF_LIFE_MS + 1_000_000; let last_used = now - DEFAULT_RECENCY_HALF_LIFE_MS; // exactly 1 half-life ago - let residency = ResidencyHint::Hot { role: TierRole::Fast }; + let residency = ResidencyHint::Hot { + role: TierRole::Fast, + }; let s = score( - 0.9, // semantic - 0.8, // outcome_history + 0.9, // semantic + 0.8, // outcome_history last_used, now, DEFAULT_RECENCY_HALF_LIFE_MS, &residency, - 0.7, // provenance_trust + 0.7, // provenance_trust &weights, ); @@ -451,11 +455,13 @@ mod tests { fn score_all_factors_one_with_default_weights_gives_one() { let weights = RecallScoreWeights::default(); let now = 1000; - let residency = ResidencyHint::Hot { role: TierRole::Fast }; + let residency = ResidencyHint::Hot { + role: TierRole::Fast, + }; let s = score( 1.0, 1.0, - now, // last_used = now → recency 1.0 + now, // last_used = now → recency 1.0 now, DEFAULT_RECENCY_HALF_LIFE_MS, &residency, @@ -475,7 +481,9 @@ mod tests { #[test] fn score_is_deterministic_across_calls() { let weights = RecallScoreWeights::default(); - let residency = ResidencyHint::Local { role: TierRole::Bench }; + let residency = ResidencyHint::Local { + role: TierRole::Bench, + }; let s1 = score(0.6, 0.7, 1000, 2000, 1000, &residency, 0.5, &weights); let s2 = score(0.6, 0.7, 1000, 2000, 1000, &residency, 0.5, &weights); assert!((s1.combined - s2.combined).abs() < 1e-9); @@ -501,9 +509,9 @@ mod tests { // for NotResident). let now = 1000 * DEFAULT_RECENCY_HALF_LIFE_MS; // 1000 half-lives in let s = score( - 1.0, // perfect semantic match + 1.0, // perfect semantic match 0.0, - 0, // last_used: 0 → recency near 0 + 0, // last_used: 0 → recency near 0 now, DEFAULT_RECENCY_HALF_LIFE_MS, &residency, @@ -522,6 +530,10 @@ mod tests { // shows WHY this artifact scored low (it's not resident). assert_eq!(s.tier_proximity, 0.0); // recency near zero — pin the isolation. - assert!(s.recency < 1e-3, "recency should be near zero, got {}", s.recency); + assert!( + s.recency < 1e-3, + "recency should be near zero, got {}", + s.recency + ); } } diff --git a/src/workers/continuum-core/src/genome/recall_source_composite.rs b/src/workers/continuum-core/src/genome/recall_source_composite.rs index 4fc790973..79b528f6a 100644 --- a/src/workers/continuum-core/src/genome/recall_source_composite.rs +++ b/src/workers/continuum-core/src/genome/recall_source_composite.rs @@ -121,10 +121,7 @@ impl CandidateSource for CompositeCandidateSource { .collect(); let per_source_results = futures::future::join_all(futures).await; - let mut merged: Vec = per_source_results - .into_iter() - .flatten() - .collect(); + let mut merged: Vec = per_source_results.into_iter().flatten().collect(); match self.dedup { DedupPolicy::None => merged, @@ -143,9 +140,7 @@ mod tests { //! order, dedup policy correctness, and pass-through for //! single-source / empty-source cases. use super::*; - use crate::genome::recall::{ - FreshnessTarget, RecallScope, ResidencyHint, TaskKind, - }; + use crate::genome::recall::{FreshnessTarget, RecallScope, ResidencyHint, TaskKind}; use crate::genome::recall_trait::{DomainHint, RecallBudget, RecallContext}; use crate::genome::tier::TierRole; use crate::genome::working_set::PersonaId; @@ -191,7 +186,9 @@ mod tests { semantic_factor: 0.5, outcome_history_factor: 0.5, last_used_ms: 0, - residency: ResidencyHint::Hot { role: TierRole::Fast }, + residency: ResidencyHint::Hot { + role: TierRole::Fast, + }, provenance_trust_factor: 0.5, } } @@ -218,8 +215,7 @@ mod tests { /// "configure later" state, not a failure. #[tokio::test] async fn empty_composite_returns_empty_vec() { - let composite = - CompositeCandidateSource::new(Vec::new(), DedupPolicy::ByArtifactId); + let composite = CompositeCandidateSource::new(Vec::new(), DedupPolicy::ByArtifactId); let results = composite.fetch(&query(), &ctx()).await; assert!(results.is_empty()); assert_eq!(composite.source_count(), 0); @@ -231,8 +227,7 @@ mod tests { #[tokio::test] async fn single_source_composite_passes_through() { let src = StubSource::new(vec![cand(1, PageKind::LoRALayer)]); - let composite = - CompositeCandidateSource::new(vec![src.clone()], DedupPolicy::ByArtifactId); + let composite = CompositeCandidateSource::new(vec![src.clone()], DedupPolicy::ByArtifactId); let results = composite.fetch(&query(), &ctx()).await; assert_eq!(results.len(), 1); assert_eq!(results[0].artifact_id, art(1)); @@ -270,10 +265,15 @@ mod tests { /// dedup (first hit wins). #[tokio::test] async fn merge_preserves_source_iteration_order() { - let src_a = StubSource::new(vec![cand(1, PageKind::LoRALayer), cand(2, PageKind::LoRALayer)]); - let src_b = StubSource::new(vec![cand(3, PageKind::LoRALayer), cand(4, PageKind::LoRALayer)]); - let composite = - CompositeCandidateSource::new(vec![src_a, src_b], DedupPolicy::None); + let src_a = StubSource::new(vec![ + cand(1, PageKind::LoRALayer), + cand(2, PageKind::LoRALayer), + ]); + let src_b = StubSource::new(vec![ + cand(3, PageKind::LoRALayer), + cand(4, PageKind::LoRALayer), + ]); + let composite = CompositeCandidateSource::new(vec![src_a, src_b], DedupPolicy::None); let results = composite.fetch(&query(), &ctx()).await; assert_eq!(results.len(), 4); @@ -307,12 +307,13 @@ mod tests { #[tokio::test] async fn dedup_by_artifact_id_keeps_first_occurrence_only() { let src_a = StubSource::new(vec![cand(7, PageKind::LoRALayer)]); - let src_b = StubSource::new(vec![cand(7, PageKind::LoRALayer), cand(8, PageKind::LoRALayer)]); + let src_b = StubSource::new(vec![ + cand(7, PageKind::LoRALayer), + cand(8, PageKind::LoRALayer), + ]); let src_c = StubSource::new(vec![cand(7, PageKind::LoRALayer)]); - let composite = CompositeCandidateSource::new( - vec![src_a, src_b, src_c], - DedupPolicy::ByArtifactId, - ); + let composite = + CompositeCandidateSource::new(vec![src_a, src_b, src_c], DedupPolicy::ByArtifactId); let results = composite.fetch(&query(), &ctx()).await; // artifact 7 from src_a wins; artifact 8 from src_b kept; // artifact 7 from src_b and src_c dropped. @@ -331,8 +332,7 @@ mod tests { cand(7, PageKind::LoRALayer), cand(7, PageKind::Engram), ]); - let composite = - CompositeCandidateSource::new(vec![src], DedupPolicy::ByArtifactId); + let composite = CompositeCandidateSource::new(vec![src], DedupPolicy::ByArtifactId); let results = composite.fetch(&query(), &ctx()).await; assert_eq!( results.len(), @@ -359,9 +359,8 @@ mod tests { #[tokio::test] async fn composite_is_object_safe_as_dyn_candidate_source() { let src = StubSource::new(vec![cand(1, PageKind::LoRALayer)]); - let composite: Arc = Arc::new( - CompositeCandidateSource::with_default_dedup(vec![src]), - ); + let composite: Arc = + Arc::new(CompositeCandidateSource::with_default_dedup(vec![src])); let results = composite.fetch(&query(), &ctx()).await; assert_eq!(results.len(), 1); } diff --git a/src/workers/continuum-core/src/genome/recall_source_must_include.rs b/src/workers/continuum-core/src/genome/recall_source_must_include.rs index 6b6be233c..f8e75848f 100644 --- a/src/workers/continuum-core/src/genome/recall_source_must_include.rs +++ b/src/workers/continuum-core/src/genome/recall_source_must_include.rs @@ -128,19 +128,17 @@ mod tests { //! verify the composite-with-dedup pattern works as expected //! when a working-set source has overlapping artifacts. use super::*; + use crate::genome::blob::{ArtifactBlob, Provenance}; use crate::genome::local_manager::LocalWorkingSetManager; use crate::genome::manager::WorkingSetManager; use crate::genome::recall::{FreshnessTarget, RecallScope, TaskKind}; - use crate::genome::recall_source_composite::{ - CompositeCandidateSource, DedupPolicy, - }; + use crate::genome::recall_source_composite::{CompositeCandidateSource, DedupPolicy}; use crate::genome::recall_source_working_set::WorkingSetCandidateSource; use crate::genome::recall_trait::{ DomainHint, EngramRef, LoRALayerRef, MoEExpertRef, RecallBudget, RecallContext, }; use crate::genome::store::TierStore; use crate::genome::tier::{EvictionRecord, TierCapacity, TierError, TierRole}; - use crate::genome::blob::{ArtifactBlob, Provenance}; use crate::genome::working_set::{ ArtifactId, PageHandle, PageOffset, PageRef, PersonaId, WorkingSetCapacity, }; @@ -197,9 +195,18 @@ mod tests { let candidates = src.fetch(&query, &ctx()).await; assert_eq!(candidates.len(), 3); - let layers: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::LoRALayer).collect(); - let experts: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::MoEExpert).collect(); - let engrams: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::Engram).collect(); + let layers: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::LoRALayer) + .collect(); + let experts: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::MoEExpert) + .collect(); + let engrams: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::Engram) + .collect(); assert_eq!(layers.len(), 1); assert_eq!(experts.len(), 1); assert_eq!(engrams.len(), 1); @@ -293,12 +300,7 @@ mod tests { Err(TierError::PageNotFound { page }) } } - async fn write( - &self, - _: PageRef, - _: ArtifactBlob, - _: Provenance, - ) -> Result<(), TierError> { + async fn write(&self, _: PageRef, _: ArtifactBlob, _: Provenance) -> Result<(), TierError> { Ok(()) } async fn evict(&self, _: usize) -> Vec { @@ -368,13 +370,19 @@ mod tests { // non-resident artifact 200 (NotResident). assert_eq!(candidates.len(), 2); - let c_100 = candidates.iter().find(|c| c.artifact_id == art(100)).unwrap(); + let c_100 = candidates + .iter() + .find(|c| c.artifact_id == art(100)) + .unwrap(); match &c_100.residency { ResidencyHint::Hot { role } => assert_eq!(*role, TierRole::Fast), other => panic!("artifact 100 should be Hot (working-set won dedup), got {other:?}"), } - let c_200 = candidates.iter().find(|c| c.artifact_id == art(200)).unwrap(); + let c_200 = candidates + .iter() + .find(|c| c.artifact_id == art(200)) + .unwrap(); match &c_200.residency { ResidencyHint::NotResident { acquirable_from } => { assert_eq!(*acquirable_from, AcquireSource::SentinelRefinement); diff --git a/src/workers/continuum-core/src/genome/recall_source_working_set.rs b/src/workers/continuum-core/src/genome/recall_source_working_set.rs index 6ed4f0dad..8532e7d77 100644 --- a/src/workers/continuum-core/src/genome/recall_source_working_set.rs +++ b/src/workers/continuum-core/src/genome/recall_source_working_set.rs @@ -119,7 +119,9 @@ impl CandidateSource for WorkingSetCandidateSource { semantic_factor: NEUTRAL_FACTOR_STUB, outcome_history_factor: NEUTRAL_FACTOR_STUB, last_used_ms: resident.last_access_ms, - residency: ResidencyHint::Hot { role: resident.role }, + residency: ResidencyHint::Hot { + role: resident.role, + }, provenance_trust_factor: NEUTRAL_FACTOR_STUB, }) .collect() @@ -133,13 +135,13 @@ mod tests { //! source returns them as candidates that the LocalDemand //! AlignedRecall ranks correctly. use super::*; + use crate::genome::blob::{ArtifactBlob, Provenance}; + use crate::genome::manager::WorkingSetManager; use crate::genome::recall::{FreshnessTarget, RecallScope, TaskKind}; use crate::genome::recall_impl::LocalDemandAlignedRecall; use crate::genome::recall_trait::{ DemandAlignedRecall, DomainHint, RecallBudget, RecallContext, }; - use crate::genome::blob::{ArtifactBlob, Provenance}; - use crate::genome::manager::WorkingSetManager; use crate::genome::store::TierStore; use crate::genome::tier::{EvictionRecord, TierCapacity, TierError, TierRole}; use crate::genome::working_set::{ @@ -338,9 +340,18 @@ mod tests { assert_eq!(candidates.len(), 3); // Group by kind. - let layers: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::LoRALayer).collect(); - let experts: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::MoEExpert).collect(); - let engrams: Vec<_> = candidates.iter().filter(|c| c.kind == PageKind::Engram).collect(); + let layers: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::LoRALayer) + .collect(); + let experts: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::MoEExpert) + .collect(); + let engrams: Vec<_> = candidates + .iter() + .filter(|c| c.kind == PageKind::Engram) + .collect(); assert_eq!(layers.len(), 1); assert_eq!(experts.len(), 1); assert_eq!(engrams.len(), 1); @@ -383,8 +394,7 @@ mod tests { async fn source_is_object_safe_for_arc_dyn_dispatch() { let tier = AlwaysPresentTier::new(TierRole::Fast); let mgr = Arc::new(LocalWorkingSetManager::new(vec![tier])); - let source: Arc = - Arc::new(WorkingSetCandidateSource::new(mgr)); + let source: Arc = Arc::new(WorkingSetCandidateSource::new(mgr)); let ctx = RecallContext::cold_start(sample_persona(99)); // Round-trip through the dyn dispatch. let candidates = source.fetch(&sample_query(), &ctx).await; diff --git a/src/workers/continuum-core/src/genome/tier.rs b/src/workers/continuum-core/src/genome/tier.rs index 57b8684dc..64f8b2e78 100644 --- a/src/workers/continuum-core/src/genome/tier.rs +++ b/src/workers/continuum-core/src/genome/tier.rs @@ -37,10 +37,7 @@ use super::working_set::PageRef; /// preserved. Never on the hot path; GC during sleep. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "lowercase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/TierRole.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/TierRole.ts")] pub enum TierRole { Fast, Warm, @@ -123,10 +120,7 @@ impl EvictionPolicy { /// the tier triggers eviction. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/TierCapacity.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/TierCapacity.ts")] pub struct TierCapacity { /// Bytes currently in use by this tier's backing store. #[ts(type = "number")] @@ -195,10 +189,7 @@ pub struct EvictionRecord { /// the shape; PR-2's `TierStore` trait returns it. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(tag = "kind", rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/TierError.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/TierError.ts")] pub enum TierError { /// The requested page isn't in this tier and a higher tier /// couldn't be paged in (chain exhausted). @@ -255,7 +246,10 @@ mod tests { fn tier_role_serializes_lowercase() { assert_eq!(serde_json::to_string(&TierRole::Fast).unwrap(), "\"fast\""); assert_eq!(serde_json::to_string(&TierRole::Warm).unwrap(), "\"warm\""); - assert_eq!(serde_json::to_string(&TierRole::Bench).unwrap(), "\"bench\""); + assert_eq!( + serde_json::to_string(&TierRole::Bench).unwrap(), + "\"bench\"" + ); assert_eq!(serde_json::to_string(&TierRole::Cold).unwrap(), "\"cold\""); assert_eq!( serde_json::to_string(&TierRole::Frozen).unwrap(), @@ -380,7 +374,10 @@ mod tests { role: TierRole::Warm, }; let json = serde_json::to_string(&e).unwrap(); - assert!(json.contains("\"kind\":\"roleNotConfigured\""), "got {json}"); + assert!( + json.contains("\"kind\":\"roleNotConfigured\""), + "got {json}" + ); assert!(json.contains("\"role\":\"warm\""), "got {json}"); } } diff --git a/src/workers/continuum-core/src/genome/working_set.rs b/src/workers/continuum-core/src/genome/working_set.rs index b55f29f8d..d889aaace 100644 --- a/src/workers/continuum-core/src/genome/working_set.rs +++ b/src/workers/continuum-core/src/genome/working_set.rs @@ -74,10 +74,7 @@ impl ArtifactId { /// differently from a `LoRALayer` page even within the same tier). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/PageKind.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/PageKind.ts")] pub enum PageKind { /// One layer slice of a LoRA adapter (Q, K, V, or O projection of /// a transformer block). @@ -100,10 +97,7 @@ pub enum PageKind { /// a hook to enforce "this PageRef points inside ArtifactId X". #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(tag = "kind", rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/PageOffset.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/PageOffset.ts")] pub enum PageOffset { /// The page IS the whole artifact (LoRA layer adapter, single /// engram). No sub-artifact split. @@ -134,10 +128,7 @@ pub enum PageOffset { /// `WorkingSet.pages`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/PageRef.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/PageRef.ts")] pub struct PageRef { pub kind: PageKind, pub artifact: ArtifactId, @@ -151,10 +142,7 @@ pub struct PageRef { /// pin the handle (Fast / Warm) or stream-read it (Cold / Frozen). #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/PageHandle.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/PageHandle.ts")] pub struct PageHandle { pub page: PageRef, pub tier_role: TierRole, @@ -177,10 +165,7 @@ pub struct PageHandle { /// in caller-side `Instant`s. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/ResidentPage.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/ResidentPage.ts")] pub struct ResidentPage { pub page: PageRef, pub role: TierRole, @@ -229,10 +214,7 @@ pub struct WorkingSetCapacity { /// instead of BTreeMap because access is by exact match, not range. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/WorkingSet.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/WorkingSet.ts")] pub struct WorkingSet { pub persona: PersonaId, /// All resident pages for this persona, keyed by a stringified @@ -261,8 +243,7 @@ impl WorkingSet { pub fn invariants_hold(&self) -> bool { for (key, page) in &self.pages { // PageRef key serialization matches the stored page. - let expected_key = - serde_json::to_string(&page.page).unwrap_or_default(); + let expected_key = serde_json::to_string(&page.page).unwrap_or_default(); if key != &expected_key { return false; } @@ -287,10 +268,7 @@ impl WorkingSet { /// page existed in `role` and got moved up. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/PageFault.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/PageFault.ts")] pub struct PageFault { pub page: PageRef, /// Where the page was before the fault. `None` for true cold @@ -324,10 +302,7 @@ pub struct PageFault { /// its `AccessDenied` audit-log inputs. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/AccessDenied.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/AccessDenied.ts")] pub struct AccessDenied { /// Which persona attempted the access. pub actor: PersonaId, @@ -436,7 +411,10 @@ mod tests { serde_json::to_string(&PageKind::KVCache).unwrap(), "\"kVCache\"" ); - assert_eq!(serde_json::to_string(&PageKind::Engram).unwrap(), "\"engram\""); + assert_eq!( + serde_json::to_string(&PageKind::Engram).unwrap(), + "\"engram\"" + ); } /// What this catches: PageOffset's tagged enum form on the wire. diff --git a/src/workers/continuum-core/src/governor/cascade.rs b/src/workers/continuum-core/src/governor/cascade.rs index 618a1fca5..4a332f571 100644 --- a/src/workers/continuum-core/src/governor/cascade.rs +++ b/src/workers/continuum-core/src/governor/cascade.rs @@ -64,7 +64,10 @@ pub const CASCADE_STEP_MAX: u8 = 5; /// rewrite the policy. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase", tag = "kind")] -#[ts(export, export_to = "../../../shared/generated/governor/CascadeAction.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/CascadeAction.ts" +)] pub enum CascadeAction { /// Keep the current step. The pressure signal didn't cross any /// threshold (or didn't cross it for long enough). @@ -90,11 +93,14 @@ pub enum CascadeAction { /// for the M-Air anchor + 5090 anchor). #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/CascadeThresholds.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/CascadeThresholds.ts" +)] pub struct CascadeThresholds { // Step 1: speculation miss + queue depth + VRAM - pub spec_miss_rate_advance: f32, // > → advance to step 1 - pub spec_miss_rate_retreat: f32, // < → retreat from step 1 + pub spec_miss_rate_advance: f32, // > → advance to step 1 + pub spec_miss_rate_retreat: f32, // < → retreat from step 1 #[ts(type = "number")] pub inference_queue_depth_advance: u32, // > → advance #[ts(type = "number")] @@ -416,8 +422,11 @@ pub fn apply_cascade_step_to_policy( // Step 2+: personas_concurrent -= 1, defer non-realtime if step >= 2 { - policy.concurrency_caps.personas_concurrent = - base.concurrency_caps.personas_concurrent.saturating_sub(1).max(1); + policy.concurrency_caps.personas_concurrent = base + .concurrency_caps + .personas_concurrent + .saturating_sub(1) + .max(1); // delayed + background cadence stretched (max with 2.0 so // already-stretched values aren't shrunk) policy.cadence_multipliers.delayed = base.cadence_multipliers.delayed.max(2.0); @@ -439,8 +448,7 @@ pub fn apply_cascade_step_to_policy( // Step 5: consolidation Manual if step >= 5 { - policy.consolidation_schedule = - crate::governor::types::ConsolidationSchedule::Manual; + policy.consolidation_schedule = crate::governor::types::ConsolidationSchedule::Manual; } policy @@ -557,11 +565,7 @@ mod tests { /// What this catches: VRAM > 85% triggers Advance. #[test] fn vram_high_at_step_0_advances() { - let action = evaluate_next_step( - 0, - &PressureSignal::VRAMHigh { used_pct: 90 }, - &thresh(), - ); + let action = evaluate_next_step(0, &PressureSignal::VRAMHigh { used_pct: 90 }, &thresh()); assert_eq!(action, CascadeAction::Advance); } @@ -569,11 +573,7 @@ mod tests { /// advance. Boundary. #[test] fn vram_at_threshold_doesnt_advance() { - let action = evaluate_next_step( - 0, - &PressureSignal::VRAMHigh { used_pct: 85 }, - &thresh(), - ); + let action = evaluate_next_step(0, &PressureSignal::VRAMHigh { used_pct: 85 }, &thresh()); assert_eq!(action, CascadeAction::Hold); } @@ -602,7 +602,11 @@ mod tests { &PressureSignal::SpeculationMissRate { rate: *rate }, &thresh(), ); - assert_eq!(action, CascadeAction::Hold, "rate {rate} should Hold in gap"); + assert_eq!( + action, + CascadeAction::Hold, + "rate {rate} should Hold in gap" + ); } } @@ -620,11 +624,7 @@ mod tests { /// What this catches: VRAM < 70 at step 1 retreats. #[test] fn vram_low_at_step_1_retreats() { - let action = evaluate_next_step( - 1, - &PressureSignal::VRAMHigh { used_pct: 60 }, - &thresh(), - ); + let action = evaluate_next_step(1, &PressureSignal::VRAMHigh { used_pct: 60 }, &thresh()); assert_eq!(action, CascadeAction::Retreat); } @@ -667,7 +667,11 @@ mod tests { }, &thresh(), ); - assert_eq!(action, CascadeAction::Retreat, "severity={severity:?} should retreat"); + assert_eq!( + action, + CascadeAction::Retreat, + "severity={severity:?} should retreat" + ); } } @@ -732,7 +736,11 @@ mod tests { }, &thresh(), ); - assert_eq!(action, CascadeAction::Hold, "severity={non_cool:?} at max step holds"); + assert_eq!( + action, + CascadeAction::Hold, + "severity={non_cool:?} at max step holds" + ); } } @@ -745,11 +753,8 @@ mod tests { fn user_active_holds_at_every_step() { for step in 0..=CASCADE_STEP_MAX { for foreground in [true, false] { - let action = evaluate_next_step( - step, - &PressureSignal::UserActive { foreground }, - &thresh(), - ); + let action = + evaluate_next_step(step, &PressureSignal::UserActive { foreground }, &thresh()); assert_eq!( action, CascadeAction::Hold, @@ -774,7 +779,10 @@ mod tests { fn apply_advance_bumps_one_capped_at_max() { assert_eq!(apply_action(0, CascadeAction::Advance), 1); assert_eq!(apply_action(3, CascadeAction::Advance), 4); - assert_eq!(apply_action(CASCADE_STEP_MAX, CascadeAction::Advance), CASCADE_STEP_MAX); + assert_eq!( + apply_action(CASCADE_STEP_MAX, CascadeAction::Advance), + CASCADE_STEP_MAX + ); } /// What this catches: Retreat drops by 1, saturated at MIN. @@ -931,12 +939,18 @@ mod tests { let base = base_policy_5090(); let after = apply_cascade_step_to_policy(&base, 0); assert_eq!(after.cascade_step, 0); - assert_eq!(after.speculation_aggressiveness, base.speculation_aggressiveness); + assert_eq!( + after.speculation_aggressiveness, + base.speculation_aggressiveness + ); assert_eq!( after.concurrency_caps.personas_concurrent, base.concurrency_caps.personas_concurrent ); - assert_eq!(after.tier_sizes.l1_lora_layers, base.tier_sizes.l1_lora_layers); + assert_eq!( + after.tier_sizes.l1_lora_layers, + base.tier_sizes.l1_lora_layers + ); assert_eq!(after.consolidation_schedule, base.consolidation_schedule); } @@ -946,7 +960,10 @@ mod tests { #[test] fn apply_step_1_drops_speculation_aggressive_to_balanced() { let base = base_policy_5090(); - assert_eq!(base.speculation_aggressiveness, SpeculationLevel::Aggressive); + assert_eq!( + base.speculation_aggressiveness, + SpeculationLevel::Aggressive + ); let after = apply_cascade_step_to_policy(&base, 1); assert_eq!(after.cascade_step, 1); assert_eq!(after.speculation_aggressiveness, SpeculationLevel::Balanced); @@ -982,7 +999,7 @@ mod tests { let after = apply_cascade_step_to_policy(&base, 2); assert_eq!(after.cascade_step, 2); assert_eq!(after.concurrency_caps.personas_concurrent, 7); // 8 - 1 - // Cumulative: step 1's speculation drop still applies + // Cumulative: step 1's speculation drop still applies assert_eq!(after.speculation_aggressiveness, SpeculationLevel::Balanced); } @@ -1003,7 +1020,10 @@ mod tests { fn apply_step_2_stretches_non_realtime_cadence() { let base = base_policy_5090(); let after = apply_cascade_step_to_policy(&base, 2); - assert_eq!(after.cadence_multipliers.realtime, base.cadence_multipliers.realtime); + assert_eq!( + after.cadence_multipliers.realtime, + base.cadence_multipliers.realtime + ); assert!(after.cadence_multipliers.delayed >= 2.0); assert!(after.cadence_multipliers.background >= 2.0); } @@ -1028,8 +1048,11 @@ mod tests { assert_eq!(after.cascade_step, 3); assert_eq!(after.tier_sizes.l1_lora_layers, 6); // 8 * 0.75 assert_eq!(after.tier_sizes.l1_kv_tokens, 12288); // 16384 * 0.75 - // L2/L3 untouched at step 3 - assert_eq!(after.tier_sizes.l2_lora_layers, base.tier_sizes.l2_lora_layers); + // L2/L3 untouched at step 3 + assert_eq!( + after.tier_sizes.l2_lora_layers, + base.tier_sizes.l2_lora_layers + ); } /// What this catches: l1 floor at 1 when base is already small. @@ -1133,8 +1156,7 @@ mod tests { // But tier_sizes is STILL shrunk (step 0 doesn't undo step 3's // shrink — it just doesn't re-apply it from a now-shrunk base). assert_eq!( - reset_attempt.tier_sizes.l1_lora_layers, - throttled.tier_sizes.l1_lora_layers, + reset_attempt.tier_sizes.l1_lora_layers, throttled.tier_sizes.l1_lora_layers, "step 0 from transformed policy ≠ base; caller MUST hold base separately" ); } diff --git a/src/workers/continuum-core/src/governor/local.rs b/src/workers/continuum-core/src/governor/local.rs index 19dacd5ff..d4fbdbf89 100644 --- a/src/workers/continuum-core/src/governor/local.rs +++ b/src/workers/continuum-core/src/governor/local.rs @@ -45,14 +45,14 @@ //! - Policy directory discovery (PR-3d); callers must provide explicit //! candidates via `set_candidates` -use crate::governor::PolicyFile; -use crate::governor::SubstrateGovernor; use crate::governor::cascade::{ - CascadeAction, CascadeThresholds, apply_action, apply_cascade_step_to_policy, - evaluate_next_step, + apply_action, apply_cascade_step_to_policy, evaluate_next_step, CascadeAction, + CascadeThresholds, }; -use crate::governor::policy_selector::{PolicySelectionError, select_policy}; +use crate::governor::policy_selector::{select_policy, PolicySelectionError}; use crate::governor::types::{GovernorPolicy, GovernorSnapshot, HardwareClass, PressureSignal}; +use crate::governor::PolicyFile; +use crate::governor::SubstrateGovernor; use arc_swap::ArcSwap; use std::sync::{Arc, Mutex}; diff --git a/src/workers/continuum-core/src/governor/mod.rs b/src/workers/continuum-core/src/governor/mod.rs index ef66028b4..5e53b5a9d 100644 --- a/src/workers/continuum-core/src/governor/mod.rs +++ b/src/workers/continuum-core/src/governor/mod.rs @@ -16,25 +16,26 @@ pub mod pressure_bridge; pub mod types; pub use cascade::{ - CASCADE_STEP_MAX, CASCADE_STEP_MIN, CascadeAction, CascadeThresholds, apply_action, - evaluate_next_step, + apply_action, evaluate_next_step, CascadeAction, CascadeThresholds, CASCADE_STEP_MAX, + CASCADE_STEP_MIN, }; pub use local::LocalSubstrateGovernor; pub use policy_file::{ - PolicyFile, PolicyFileError, into_governor_policy, load_policy_file, parse_policy_text, + into_governor_policy, load_policy_file, parse_policy_text, PolicyFile, PolicyFileError, }; pub use policy_selector::{ - PolicySelectionError, hardware_fingerprint, policy_matches_hardware, select_policy, + hardware_fingerprint, policy_matches_hardware, select_policy, PolicySelectionError, }; pub use policy_watcher::{ - PolicyDirectoryError, PolicyDirectoryWatcher, load_policy_directory, reload_policy_candidates, - watch_policy_directory, + load_policy_directory, reload_policy_candidates, watch_policy_directory, PolicyDirectoryError, + PolicyDirectoryWatcher, }; pub use pressure_bridge::{alert_to_signal, governor_alert_sink}; pub use types::{ - CadenceMultipliers, ConcurrencyCaps, ConsolidationSchedule, FederationCadence, GovernorPolicy, - GovernorSnapshot, HardwareClass, PowerSource, PressureSignal, RecallScoreWeights, - SpeculationLevel, TargetSilicon, ThermalClass, ThermalSeverity, TierSizes, classify_hardware, + classify_hardware, CadenceMultipliers, ConcurrencyCaps, ConsolidationSchedule, + FederationCadence, GovernorPolicy, GovernorSnapshot, HardwareClass, PowerSource, + PressureSignal, RecallScoreWeights, SpeculationLevel, TargetSilicon, ThermalClass, + ThermalSeverity, TierSizes, }; /// The trait every Substrate Governor implementation must satisfy. diff --git a/src/workers/continuum-core/src/governor/policy_watcher.rs b/src/workers/continuum-core/src/governor/policy_watcher.rs index d7c0c7d8c..d22ab52f8 100644 --- a/src/workers/continuum-core/src/governor/policy_watcher.rs +++ b/src/workers/continuum-core/src/governor/policy_watcher.rs @@ -7,7 +7,7 @@ //! errors. The watcher callback records and logs failures instead of //! replacing a good candidate set with junk. -use crate::governor::{LocalSubstrateGovernor, PolicyFile, PolicyFileError, load_policy_file}; +use crate::governor::{load_policy_file, LocalSubstrateGovernor, PolicyFile, PolicyFileError}; use notify::{Event, EventKind, RecommendedWatcher, RecursiveMode, Watcher}; use std::path::{Path, PathBuf}; use std::sync::{Arc, Mutex}; diff --git a/src/workers/continuum-core/src/governor/pressure_bridge.rs b/src/workers/continuum-core/src/governor/pressure_bridge.rs index b791e89a0..51e9d190a 100644 --- a/src/workers/continuum-core/src/governor/pressure_bridge.rs +++ b/src/workers/continuum-core/src/governor/pressure_bridge.rs @@ -175,7 +175,10 @@ mod tests { #[test] fn pressure_above_one_clamps_to_100_pct() { let signal = alert_to_signal(&alert_at("critical", 1.5)); - assert_eq!(signal, Some(PressureSignal::SystemMemHigh { used_pct: 100 })); + assert_eq!( + signal, + Some(PressureSignal::SystemMemHigh { used_pct: 100 }) + ); } /// What this catches: negative pressure clamps to used_pct = 0. A diff --git a/src/workers/continuum-core/src/governor/types.rs b/src/workers/continuum-core/src/governor/types.rs index 9453bb027..ab11b0527 100644 --- a/src/workers/continuum-core/src/governor/types.rs +++ b/src/workers/continuum-core/src/governor/types.rs @@ -40,7 +40,10 @@ use ts_rs::TS; /// rule the rest of the substrate honors. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/TargetSilicon.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/TargetSilicon.ts" +)] pub enum TargetSilicon { /// Apple Silicon (M1/M2/M3/M4/M5 + descendants). UMA — system_ram /// and "vram" are the same physical pool. @@ -66,7 +69,10 @@ pub enum TargetSilicon { /// the same hardware runs at full aggressiveness. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/PowerSource.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/PowerSource.ts" +)] pub enum PowerSource { Battery, Plugged, @@ -77,7 +83,10 @@ pub enum PowerSource { /// Probed from silicon + chassis hints at boot. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/ThermalClass.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/ThermalClass.ts" +)] pub enum ThermalClass { /// Laptop, fan-limited. MacBook Air, Surface Pro, ultrabooks. ThinAndLight, @@ -92,7 +101,10 @@ pub enum ThermalClass { /// Live thermal pressure signal. Drives cascade-step entry/exit. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/ThermalSeverity.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/ThermalSeverity.ts" +)] pub enum ThermalSeverity { Cool, Warm, @@ -104,7 +116,10 @@ pub enum ThermalSeverity { /// events. The governor selects a policy file off this fingerprint. #[derive(Debug, Clone, Serialize, Deserialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/HardwareClass.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/HardwareClass.ts" +)] pub struct HardwareClass { pub silicon: TargetSilicon, /// Human-readable model name ("M2", "RTX 5090", "Radeon RX 7900 XTX"). @@ -152,7 +167,10 @@ pub struct TierSizes { /// stays at 1.0; delayed and background stretch under pressure. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/CadenceMultipliers.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/CadenceMultipliers.ts" +)] pub struct CadenceMultipliers { pub realtime: f32, pub delayed: f32, @@ -163,7 +181,10 @@ pub struct CadenceMultipliers { /// modules read at task-dispatch time. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/ConcurrencyCaps.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/ConcurrencyCaps.ts" +)] pub struct ConcurrencyCaps { #[ts(type = "number")] pub personas_concurrent: u32, @@ -178,7 +199,10 @@ pub struct ConcurrencyCaps { /// Speculation aggressiveness. Drops under pressure (cascade step 1). #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, PartialOrd, Ord, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/SpeculationLevel.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/SpeculationLevel.ts" +)] pub enum SpeculationLevel { Off, Conservative, @@ -189,7 +213,10 @@ pub enum SpeculationLevel { /// When consolidation (artifact refinement, engram crystallization) runs. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq, Hash)] #[serde(rename_all = "kebab-case")] -#[ts(export, export_to = "../../../shared/generated/governor/ConsolidationSchedule.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/ConsolidationSchedule.ts" +)] pub enum ConsolidationSchedule { Always, Idle, @@ -200,7 +227,10 @@ pub enum ConsolidationSchedule { /// Federation pull cadence — how often a node pulls peer artifacts. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq, Eq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/FederationCadence.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/FederationCadence.ts" +)] pub struct FederationCadence { #[ts(type = "number")] pub pull_cadence_seconds: u32, @@ -210,7 +240,10 @@ pub struct FederationCadence { /// be ~1.0 by convention; the governor's policy file enforces this. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/RecallScoreWeights.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/RecallScoreWeights.ts" +)] pub struct RecallScoreWeights { pub semantic: f32, pub outcome_history: f32, @@ -224,7 +257,10 @@ pub struct RecallScoreWeights { /// changes via `arc_swap`. #[derive(Debug, Clone, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/GovernorPolicy.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/GovernorPolicy.ts" +)] pub struct GovernorPolicy { /// Monotonic; increments on every rewrite. Subscribers compare to /// detect "did the policy change since I last looked." @@ -253,7 +289,10 @@ pub struct GovernorPolicy { /// (CBAR-SUBSTRATE Lane E) emits these; governor consumes. #[derive(Debug, Clone, Copy, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase", tag = "kind")] -#[ts(export, export_to = "../../../shared/generated/governor/PressureSignal.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/PressureSignal.ts" +)] pub enum PressureSignal { Thermal { severity: ThermalSeverity, @@ -287,7 +326,10 @@ pub enum PressureSignal { /// shape). #[derive(Debug, Clone, Serialize, Deserialize, TS, PartialEq)] #[serde(rename_all = "camelCase")] -#[ts(export, export_to = "../../../shared/generated/governor/GovernorSnapshot.ts")] +#[ts( + export, + export_to = "../../../shared/generated/governor/GovernorSnapshot.ts" +)] pub struct GovernorSnapshot { pub current_policy: GovernorPolicy, /// Number of cascade-step transitions since boot. Diagnostic — high @@ -477,8 +519,14 @@ mod tests { /// Mac runs through the wrong policy. #[test] fn mac_classifies_as_apple_m() { - assert_eq!(classify_hardware(&mac_m2_air()).silicon, TargetSilicon::AppleM); - assert_eq!(classify_hardware(&m5_pro_workstation()).silicon, TargetSilicon::AppleM); + assert_eq!( + classify_hardware(&mac_m2_air()).silicon, + TargetSilicon::AppleM + ); + assert_eq!( + classify_hardware(&m5_pro_workstation()).silicon, + TargetSilicon::AppleM + ); } /// What this catches: NVIDIA + Vulkan (typical Blackwell setup) @@ -486,7 +534,10 @@ mod tests { /// present (CUDA kernels more complete in our llama.cpp build). #[test] fn nvidia_with_vulkan_classifies_as_cuda() { - assert_eq!(classify_hardware(&blackwell_5090()).silicon, TargetSilicon::NvidiaCuda); + assert_eq!( + classify_hardware(&blackwell_5090()).silicon, + TargetSilicon::NvidiaCuda + ); } /// What this catches: AMD/Intel Vulkan-only host classifies as @@ -505,7 +556,10 @@ mod tests { /// — same no_silent_fallback rule as the inference gate. #[test] fn cpu_only_classifies_as_none() { - assert_eq!(classify_hardware(&cpu_only_server()).silicon, TargetSilicon::None); + assert_eq!( + classify_hardware(&cpu_only_server()).silicon, + TargetSilicon::None + ); } // ===== UMA VRAM handling ===== @@ -558,7 +612,10 @@ mod tests { /// most aggressive thermal throttling target. #[test] fn ios_classifies_as_mobile() { - assert_eq!(classify_hardware(&vision_pro()).thermal_class, ThermalClass::Mobile); + assert_eq!( + classify_hardware(&vision_pro()).thermal_class, + ThermalClass::Mobile + ); } /// What this catches: "server" in platform → Server thermal class. @@ -577,7 +634,10 @@ mod tests { fn unknown_platform_defaults_to_workstation() { let mut hw = blackwell_5090(); hw.platform = "some-future-platform".into(); - assert_eq!(classify_hardware(&hw).thermal_class, ThermalClass::Workstation); + assert_eq!( + classify_hardware(&hw).thermal_class, + ThermalClass::Workstation + ); } // ===== defaults ===== @@ -586,7 +646,10 @@ mod tests { /// performance when undetermined). PR-2 wires real probe. #[test] fn power_source_defaults_to_plugged() { - assert_eq!(classify_hardware(&mac_m2_air()).power_source, PowerSource::Plugged); + assert_eq!( + classify_hardware(&mac_m2_air()).power_source, + PowerSource::Plugged + ); } /// What this catches: battery_pct + thermal_headroom_pct are None @@ -622,14 +685,26 @@ mod tests { /// TS wire. Wire stability — every consumer parses these strings. #[test] fn target_silicon_serializes_kebab_case() { - assert_eq!(serde_json::to_string(&TargetSilicon::AppleM).unwrap(), "\"apple-m\""); - assert_eq!(serde_json::to_string(&TargetSilicon::NvidiaCuda).unwrap(), "\"nvidia-cuda\""); - assert_eq!(serde_json::to_string(&TargetSilicon::AmdRocm).unwrap(), "\"amd-rocm\""); + assert_eq!( + serde_json::to_string(&TargetSilicon::AppleM).unwrap(), + "\"apple-m\"" + ); + assert_eq!( + serde_json::to_string(&TargetSilicon::NvidiaCuda).unwrap(), + "\"nvidia-cuda\"" + ); + assert_eq!( + serde_json::to_string(&TargetSilicon::AmdRocm).unwrap(), + "\"amd-rocm\"" + ); assert_eq!( serde_json::to_string(&TargetSilicon::IntelVulkan).unwrap(), "\"intel-vulkan\"" ); - assert_eq!(serde_json::to_string(&TargetSilicon::None).unwrap(), "\"none\""); + assert_eq!( + serde_json::to_string(&TargetSilicon::None).unwrap(), + "\"none\"" + ); } /// What this catches: HardwareClass round-trips with camelCase. diff --git a/src/workers/continuum-core/src/inference/footprint_registry/mod.rs b/src/workers/continuum-core/src/inference/footprint_registry/mod.rs index a7595e309..ec6bbc3db 100644 --- a/src/workers/continuum-core/src/inference/footprint_registry/mod.rs +++ b/src/workers/continuum-core/src/inference/footprint_registry/mod.rs @@ -38,7 +38,7 @@ pub use types::{ use crate::cognition::{ ThroughputLease, ThroughputLeaseError, ThroughputLeaseRevocationPolicy, ThroughputLeaseSnapshot, }; -use dashmap::{DashMap, mapref::entry::Entry}; +use dashmap::{mapref::entry::Entry, DashMap}; use std::collections::BTreeMap; use std::collections::HashMap; use std::sync::OnceLock; diff --git a/src/workers/continuum-core/src/inference/llm_module_bus.rs b/src/workers/continuum-core/src/inference/llm_module_bus.rs index 0d130a21e..68c86cb9b 100644 --- a/src/workers/continuum-core/src/inference/llm_module_bus.rs +++ b/src/workers/continuum-core/src/inference/llm_module_bus.rs @@ -120,7 +120,8 @@ pub async fn publish_first_token_emitted( event: &FirstTokenEmitted, ) { let payload = serde_json::to_value(event).unwrap_or(serde_json::Value::Null); - bus.publish(FIRST_TOKEN_EMITTED_KEY, payload, registry).await; + bus.publish(FIRST_TOKEN_EMITTED_KEY, payload, registry) + .await; } /// Publish a `ResidencyFault` event. Sentinel-observer subscribes @@ -234,10 +235,7 @@ mod tests { tick_interval: None, } } - async fn initialize( - &self, - _ctx: &crate::runtime::ModuleContext, - ) -> Result<(), String> { + async fn initialize(&self, _ctx: &crate::runtime::ModuleContext) -> Result<(), String> { Ok(()) } async fn handle_command( @@ -259,7 +257,9 @@ mod tests { key: &ArtifactKey, payload: serde_json::Value, ) -> Result<(), String> { - self.captured.lock().push((key.as_str().to_string(), payload)); + self.captured + .lock() + .push((key.as_str().to_string(), payload)); Ok(()) } fn as_any(&self) -> &dyn Any { diff --git a/src/workers/continuum-core/src/inference/llm_module_service.rs b/src/workers/continuum-core/src/inference/llm_module_service.rs index d1f49178c..39cf8ce8d 100644 --- a/src/workers/continuum-core/src/inference/llm_module_service.rs +++ b/src/workers/continuum-core/src/inference/llm_module_service.rs @@ -35,9 +35,7 @@ use std::any::Any; use std::sync::Arc; -use super::llm_module::{ - FinishReason, FirstTokenEmitted, InferenceComplete, InferenceRequest, -}; +use super::llm_module::{FinishReason, FirstTokenEmitted, InferenceComplete, InferenceRequest}; use super::llm_module_bus::{publish_first_token_emitted, publish_inference_complete}; use crate::ai::adapter::AIProviderAdapter; use crate::ai::types::{ @@ -47,9 +45,7 @@ use crate::ai::types::{ use crate::runtime::message_bus::MessageBus; use crate::runtime::module_context::ModuleContext; use crate::runtime::registry::ModuleRegistry; -use crate::runtime::service_module::{ - CommandResult, ModuleConfig, ModulePriority, ServiceModule, -}; +use crate::runtime::service_module::{CommandResult, ModuleConfig, ModulePriority, ServiceModule}; /// Optional bus + registry handle for auto-publishing inference /// response events. When set on `InferenceLlmModule`, every @@ -190,11 +186,7 @@ impl ServiceModule for InferenceLlmModule { Ok(()) } - async fn handle_command( - &self, - command: &str, - params: Value, - ) -> Result { + async fn handle_command(&self, command: &str, params: Value) -> Result { match command { COMMAND_REQUEST => self.handle_request(params).await, other => Err(format!( @@ -546,9 +538,7 @@ mod tests { #[tokio::test] async fn handle_command_unknown_returns_loud_error() { let m = InferenceLlmModule::new(); - let result = m - .handle_command("inference/llm/bogus", Value::Null) - .await; + let result = m.handle_command("inference/llm/bogus", Value::Null).await; match result { Err(msg) => { assert!(msg.contains("unknown command")); @@ -622,8 +612,7 @@ mod tests { // ─── PR-3b: bus auto-publish tests ───────────────────────── use crate::inference::llm_module_bus::{ - FIRST_TOKEN_EMITTED_KEY, INFERENCE_COMPLETE_KEY, - inference_response_selectors, + inference_response_selectors, FIRST_TOKEN_EMITTED_KEY, INFERENCE_COMPLETE_KEY, }; use crate::runtime::artifact_handle::{ArtifactKey, ArtifactSelector}; use crate::runtime::runtime::Runtime; @@ -657,10 +646,7 @@ mod tests { tick_interval: None, } } - async fn initialize( - &self, - _ctx: &crate::runtime::ModuleContext, - ) -> Result<(), String> { + async fn initialize(&self, _ctx: &crate::runtime::ModuleContext) -> Result<(), String> { Ok(()) } async fn handle_command( @@ -678,7 +664,9 @@ mod tests { key: &ArtifactKey, payload: serde_json::Value, ) -> Result<(), String> { - self.captured.lock().push((key.as_str().to_string(), payload)); + self.captured + .lock() + .push((key.as_str().to_string(), payload)); Ok(()) } fn as_any(&self) -> &dyn Any { @@ -696,14 +684,14 @@ mod tests { let (recorder, captured) = InferenceRecorder::new(); runtime.register(recorder); - let module = InferenceLlmModule::with_bus( - runtime.bus_arc(), - runtime.registry_arc(), - ); + let module = InferenceLlmModule::with_bus(runtime.bus_arc(), runtime.registry_arc()); let req = sample_request(); let params = serde_json::to_value(&req).unwrap(); - let _ = module.handle_command(COMMAND_REQUEST, params).await.unwrap(); + let _ = module + .handle_command(COMMAND_REQUEST, params) + .await + .unwrap(); // Yield to let the spawned publishes run. for _ in 0..50 { @@ -749,7 +737,10 @@ mod tests { let module = InferenceLlmModule::new(); let req = sample_request(); let params = serde_json::to_value(&req).unwrap(); - let _ = module.handle_command(COMMAND_REQUEST, params).await.unwrap(); + let _ = module + .handle_command(COMMAND_REQUEST, params) + .await + .unwrap(); // Yield to give any incorrectly-spawned publish a chance. for _ in 0..20 { @@ -772,10 +763,7 @@ mod tests { let (recorder, captured) = InferenceRecorder::new(); runtime.register(recorder); - let module = InferenceLlmModule::with_bus( - runtime.bus_arc(), - runtime.registry_arc(), - ); + let module = InferenceLlmModule::with_bus(runtime.bus_arc(), runtime.registry_arc()); let result = module .handle_command("inference/llm/bogus", Value::Null) @@ -802,10 +790,7 @@ mod tests { let (recorder, captured) = InferenceRecorder::new(); runtime.register(recorder); - let module = InferenceLlmModule::with_bus( - runtime.bus_arc(), - runtime.registry_arc(), - ); + let module = InferenceLlmModule::with_bus(runtime.bus_arc(), runtime.registry_arc()); let result = module .handle_command(COMMAND_REQUEST, serde_json::json!({"not": "valid"})) @@ -878,8 +863,14 @@ mod tests { let complete = super::translate_adapter_response(&req, response); assert_eq!(complete.request_id, req.request_id); assert_eq!(complete.persona, req.persona); - assert_eq!(complete.completion_text.as_deref(), Some("stub adapter completion")); - assert!(complete.completion_tokens.is_empty(), "adapter path is text, not tokens"); + assert_eq!( + complete.completion_text.as_deref(), + Some("stub adapter completion") + ); + assert!( + complete.completion_tokens.is_empty(), + "adapter path is text, not tokens" + ); assert_eq!(complete.tokens_generated, 7); assert_eq!(complete.elapsed_ms, 250); assert_eq!(complete.finish_reason, FinishReason::Stop); diff --git a/src/workers/continuum-core/src/inference_capability/gguf_loader.rs b/src/workers/continuum-core/src/inference_capability/gguf_loader.rs index b15ca9d87..db152871f 100644 --- a/src/workers/continuum-core/src/inference_capability/gguf_loader.rs +++ b/src/workers/continuum-core/src/inference_capability/gguf_loader.rs @@ -190,10 +190,10 @@ pub(crate) fn file_type_to_bytes_per_param(ft: u32) -> Result { // Source: llama.cpp ggml-quants.h ggml_ftype enum + bits-per-weight // for each quantization scheme. Divided by 8 for bytes-per-weight. match ft { - 0 => Ok(4.0), // ALL_F32 - 1 => Ok(2.0), // MOSTLY_F16 - 2 => Ok(4.5 / 8.0), // MOSTLY_Q4_0 - 3 => Ok(5.0 / 8.0), // MOSTLY_Q4_1 + 0 => Ok(4.0), // ALL_F32 + 1 => Ok(2.0), // MOSTLY_F16 + 2 => Ok(4.5 / 8.0), // MOSTLY_Q4_0 + 3 => Ok(5.0 / 8.0), // MOSTLY_Q4_1 // 4-5 removed in modern llama.cpp 7 => Ok(8.5 / 8.0), // MOSTLY_Q8_0 8 => Ok(5.5 / 8.0), // MOSTLY_Q5_0 @@ -284,7 +284,10 @@ mod tests { #[test] fn q4_k_m_bytes_per_param_within_band() { let bpp = file_type_to_bytes_per_param(15).unwrap(); - assert!(bpp > 0.55 && bpp < 0.65, "Q4_K_M bpp={bpp} outside 0.55-0.65 band"); + assert!( + bpp > 0.55 && bpp < 0.65, + "Q4_K_M bpp={bpp} outside 0.55-0.65 band" + ); } /// What this catches: FP16 (1) gives exactly 2.0 bytes/param. @@ -311,7 +314,10 @@ mod tests { let result = file_type_to_bytes_per_param(9999); assert!(result.is_err()); let msg = result.unwrap_err(); - assert!(msg.contains("9999"), "error should name the unknown value: {msg}"); + assert!( + msg.contains("9999"), + "error should name the unknown value: {msg}" + ); } /// What this catches: removed file_types (4, 5 in modern llama.cpp) @@ -387,7 +393,10 @@ mod tests { #[test] fn qwen2_and_qwen2vl_have_empty_layer_kinds() { assert_eq!(layer_kinds_for_architecture("qwen2"), Vec::::new()); - assert_eq!(layer_kinds_for_architecture("qwen2vl"), Vec::::new()); + assert_eq!( + layer_kinds_for_architecture("qwen2vl"), + Vec::::new() + ); } /// What this catches: arbitrary unknown architecture returns @@ -399,10 +408,16 @@ mod tests { /// only when the architecture-keyed rule kicks in. #[test] fn unknown_arch_returns_empty_kinds() { - assert_eq!(layer_kinds_for_architecture("mistral"), Vec::::new()); + assert_eq!( + layer_kinds_for_architecture("mistral"), + Vec::::new() + ); assert_eq!(layer_kinds_for_architecture("phi3"), Vec::::new()); assert_eq!(layer_kinds_for_architecture(""), Vec::::new()); - assert_eq!(layer_kinds_for_architecture("future-model"), Vec::::new()); + assert_eq!( + layer_kinds_for_architecture("future-model"), + Vec::::new() + ); } /// What this catches: layer-kind table stays stable for the @@ -452,7 +467,9 @@ mod tests { .ok() .map(|d| d.join("Cargo.toml")) .filter(|p| p.exists()); - let Some(path) = path else { return; }; + let Some(path) = path else { + return; + }; let result = read_qwen_model_metadata(&path); assert!(result.is_err(), "non-GGUF file should Err, got Ok"); } diff --git a/src/workers/continuum-core/src/inference_capability/hw_probe.rs b/src/workers/continuum-core/src/inference_capability/hw_probe.rs index 853edc37a..f86e1a42b 100644 --- a/src/workers/continuum-core/src/inference_capability/hw_probe.rs +++ b/src/workers/continuum-core/src/inference_capability/hw_probe.rs @@ -174,7 +174,10 @@ fn try_detect_cuda() -> Option<(u64, String)> { { use std::process::Command; let output = Command::new("nvidia-smi") - .args(["--query-gpu=memory.total,name", "--format=csv,noheader,nounits"]) + .args([ + "--query-gpu=memory.total,name", + "--format=csv,noheader,nounits", + ]) .output() .ok()?; let stdout = String::from_utf8(output.stdout).ok()?; @@ -271,7 +274,11 @@ mod tests { assert!(!hw.has_metal); assert!(hw.has_cuda); assert!(hw.has_vulkan); - assert_eq!(hw.total_vram_bytes, 32 * 1024 * 1024 * 1024, "MAX of CUDA+Vulkan reports"); + assert_eq!( + hw.total_vram_bytes, + 32 * 1024 * 1024 * 1024, + "MAX of CUDA+Vulkan reports" + ); assert_eq!(hw.cpu_cores, 32); assert_eq!(hw.system_ram_bytes, 128 * 1024 * 1024 * 1024); } @@ -394,14 +401,7 @@ mod tests { /// doesn't itself silently fix bad inputs. #[test] fn zero_cpu_cores_propagates_to_profile() { - let hw = build_hardware_profile( - None, - None, - None, - 0, - 8 * 1024 * 1024 * 1024, - "test".into(), - ); + let hw = build_hardware_profile(None, None, None, 0, 8 * 1024 * 1024 * 1024, "test".into()); assert_eq!(hw.cpu_cores, 0); } @@ -485,7 +485,11 @@ mod tests { fn live_probe_does_not_panic() { let hw = probe_hardware_profile(); // Sanity: cpu_cores must be at least 1 (clamped) - assert!(hw.cpu_cores >= 1, "cpu_cores={} should be clamped >=1", hw.cpu_cores); + assert!( + hw.cpu_cores >= 1, + "cpu_cores={} should be clamped >=1", + hw.cpu_cores + ); // Sanity: platform string is non-empty assert!(!hw.platform.is_empty()); // Sanity: on a no-GPU-features build, all flags must be false diff --git a/src/workers/continuum-core/src/inference_capability/probe.rs b/src/workers/continuum-core/src/inference_capability/probe.rs index 19691090e..e54a049ea 100644 --- a/src/workers/continuum-core/src/inference_capability/probe.rs +++ b/src/workers/continuum-core/src/inference_capability/probe.rs @@ -49,9 +49,7 @@ const MIN_GPU_INFERENCE_VRAM_BYTES: u64 = 2 * 1024 * 1024 * 1024; /// CUDA specifically. As llama.cpp/candle gain Vulkan backends, lift /// the kind gate (no code change needed elsewhere — registry of kinds /// is dynamic). -pub fn probe_inference_capabilities( - hw: &HardwareProfile, -) -> Vec { +pub fn probe_inference_capabilities(hw: &HardwareProfile) -> Vec { let mut caps: Vec = Vec::new(); let has_native_gpu = hw.has_metal || hw.has_cuda; @@ -195,11 +193,11 @@ mod tests { "ort-vision".into(), ], ); + assert!(caps.iter().all(|c| c.latency_class == LatencyClass::Local)); + assert!(caps.iter().all(|c| c.current_lease_count == 0)); assert!(caps .iter() - .all(|c| c.latency_class == LatencyClass::Local)); - assert!(caps.iter().all(|c| c.current_lease_count == 0)); - assert!(caps.iter().all(|c| c.free_vram_bytes == 5 * 1024 * 1024 * 1024)); + .all(|c| c.free_vram_bytes == 5 * 1024 * 1024 * 1024)); } /// What this catches: M5 Pro with 32GB free VRAM advertises every kind @@ -410,7 +408,14 @@ mod tests { let kinds: Vec<&str> = caps.iter().map(|c| c.kind.as_str()).collect(); assert_eq!( kinds, - vec!["llamacpp", "candle", "ort-vision", "ort-tts", "ort-stt", "ort-embedding"], + vec![ + "llamacpp", + "candle", + "ort-vision", + "ort-tts", + "ort-stt", + "ort-embedding" + ], "ordering shifted — PR-2/PR-3 may have implicit assumptions; pin it explicitly", ); } diff --git a/src/workers/continuum-core/src/inference_capability/registry.rs b/src/workers/continuum-core/src/inference_capability/registry.rs index 9108f0657..289104779 100644 --- a/src/workers/continuum-core/src/inference_capability/registry.rs +++ b/src/workers/continuum-core/src/inference_capability/registry.rs @@ -69,9 +69,9 @@ impl NodeCapabilityRegistry { min_free_vram_bytes: u64, ) -> impl Iterator + 'a { self.nodes.values().filter(move |node| { - node.capabilities.iter().any(|cap| { - cap.kind == *kind && cap.free_vram_bytes >= min_free_vram_bytes - }) + node.capabilities + .iter() + .any(|cap| cap.kind == *kind && cap.free_vram_bytes >= min_free_vram_bytes) }) } @@ -92,7 +92,12 @@ mod tests { kinds, HardwareProfile, InferenceCapability, LatencyClass, }; - fn mk_node(node_id: &str, kind: &str, free_vram_bytes: u64, last_updated_ms: u64) -> NodeCapability { + fn mk_node( + node_id: &str, + kind: &str, + free_vram_bytes: u64, + last_updated_ms: u64, + ) -> NodeCapability { NodeCapability { node_id: node_id.into(), hardware: HardwareProfile { @@ -164,8 +169,18 @@ mod tests { #[test] fn find_capable_filters_on_kind_and_vram() { let mut r = NodeCapabilityRegistry::new(); - r.upsert(mk_node("big-llamacpp", kinds::LLAMACPP, 24_000_000_000, 100)); - r.upsert(mk_node("small-llamacpp", kinds::LLAMACPP, 2_000_000_000, 100)); + r.upsert(mk_node( + "big-llamacpp", + kinds::LLAMACPP, + 24_000_000_000, + 100, + )); + r.upsert(mk_node( + "small-llamacpp", + kinds::LLAMACPP, + 2_000_000_000, + 100, + )); r.upsert(mk_node("big-candle", kinds::CANDLE, 24_000_000_000, 100)); let llamacpp = InferenceKind::from(kinds::LLAMACPP); @@ -192,7 +207,12 @@ mod tests { #[test] fn find_capable_returns_empty_when_kind_not_advertised() { let mut r = NodeCapabilityRegistry::new(); - r.upsert(mk_node("llamacpp-only", kinds::LLAMACPP, 8_000_000_000, 100)); + r.upsert(mk_node( + "llamacpp-only", + kinds::LLAMACPP, + 8_000_000_000, + 100, + )); let ort_vision = InferenceKind::from(kinds::ORT_VISION); let got: Vec<_> = r.find_capable(&ort_vision, 0).collect(); assert!(got.is_empty()); @@ -205,7 +225,12 @@ mod tests { fn list_iterates_all_nodes() { let mut r = NodeCapabilityRegistry::new(); for i in 0..5 { - r.upsert(mk_node(&format!("node-{i}"), kinds::LLAMACPP, 4_000_000_000, 100)); + r.upsert(mk_node( + &format!("node-{i}"), + kinds::LLAMACPP, + 4_000_000_000, + 100, + )); } let mut ids: Vec<&str> = r.list().map(|n| n.node_id.as_str()).collect(); ids.sort(); @@ -304,7 +329,12 @@ mod tests { fn remove_all_nodes_returns_to_empty() { let mut r = NodeCapabilityRegistry::new(); for i in 0..3 { - r.upsert(mk_node(&format!("n-{i}"), kinds::LLAMACPP, 4_000_000_000, 100)); + r.upsert(mk_node( + &format!("n-{i}"), + kinds::LLAMACPP, + 4_000_000_000, + 100, + )); } assert_eq!(r.node_count(), 3); for i in 0..3 { diff --git a/src/workers/continuum-core/src/ipc/mod.rs b/src/workers/continuum-core/src/ipc/mod.rs index 1c49ed775..cc82ecde0 100644 --- a/src/workers/continuum-core/src/ipc/mod.rs +++ b/src/workers/continuum-core/src/ipc/mod.rs @@ -108,8 +108,8 @@ impl IpcStream for TcpStream { pub mod diagnostics; pub mod protocol; -pub use protocol::InboxMessageRequest; use diagnostics::{current_rss_mb, dump_memory_report, log_command_rss_delta}; +pub use protocol::InboxMessageRequest; use protocol::Response; // See modules/health.rs, cognition.rs, channel.rs, voice.rs, code.rs, memory.rs, diff --git a/src/workers/continuum-core/src/lib.rs b/src/workers/continuum-core/src/lib.rs index 31b5b8eba..fbdc4dc28 100644 --- a/src/workers/continuum-core/src/lib.rs +++ b/src/workers/continuum-core/src/lib.rs @@ -20,15 +20,15 @@ pub mod ai; pub mod airc; pub mod audio_constants; pub mod code; -pub mod comms; pub mod cognition; +pub mod comms; pub mod concurrency; pub mod contracts; pub mod events; pub mod ffi; pub mod forge; -pub mod governor; pub mod genome; +pub mod governor; pub mod gpu; pub mod http; pub mod inference; diff --git a/src/workers/continuum-core/src/live/audio/stt/moonshine.rs b/src/workers/continuum-core/src/live/audio/stt/moonshine.rs index 8b7b04c91..b9be184bb 100644 --- a/src/workers/continuum-core/src/live/audio/stt/moonshine.rs +++ b/src/workers/continuum-core/src/live/audio/stt/moonshine.rs @@ -229,7 +229,9 @@ impl MoonshineStt { // The helper uses the correct `feature = "metal"` gate that // matches Cargo.toml. let providers = crate::inference::ort_providers::build_ort_gpu_execution_providers() - .map_err(|e| STTError::ModelNotLoaded(format!("ORT GPU EP setup failed (Moonshine STT): {e}")))?; + .map_err(|e| { + STTError::ModelNotLoaded(format!("ORT GPU EP setup failed (Moonshine STT): {e}")) + })?; builder = builder .with_execution_providers(providers) .map_err(|e| STTError::ModelNotLoaded(format!("EP register failed: {e}")))?; diff --git a/src/workers/continuum-core/src/live/audio/tts/kokoro.rs b/src/workers/continuum-core/src/live/audio/tts/kokoro.rs index c13b463df..88f95aed8 100644 --- a/src/workers/continuum-core/src/live/audio/tts/kokoro.rs +++ b/src/workers/continuum-core/src/live/audio/tts/kokoro.rs @@ -15,8 +15,8 @@ use crate::live::audio::reloadable::ReloadableModel; use crate::{clog_info, clog_warn}; use async_trait::async_trait; use ndarray; -use ort::session::Session; use ort::session::builder::GraphOptimizationLevel; +use ort::session::Session; use parking_lot::Mutex; use std::collections::HashMap; use std::path::PathBuf; diff --git a/src/workers/continuum-core/src/live/audio/tts/orpheus.rs b/src/workers/continuum-core/src/live/audio/tts/orpheus.rs index 7f9d95f93..6b722744e 100644 --- a/src/workers/continuum-core/src/live/audio/tts/orpheus.rs +++ b/src/workers/continuum-core/src/live/audio/tts/orpheus.rs @@ -206,7 +206,9 @@ impl OrpheusTts { // run on GPU. Pre-this-PR Orpheus never configured an EP at all, // so ORT's implicit CPU EP took every op silently. let providers = crate::inference::ort_providers::build_ort_gpu_execution_providers() - .map_err(|e| TTSError::ModelNotLoaded(format!("ORT GPU EP setup failed (Orpheus SNAC): {e}")))?; + .map_err(|e| { + TTSError::ModelNotLoaded(format!("ORT GPU EP setup failed (Orpheus SNAC): {e}")) + })?; Session::builder() .map_err(|e| TTSError::ModelNotLoaded(format!("SNAC session builder: {e}")))? .with_execution_providers(providers) diff --git a/src/workers/continuum-core/src/live/audio/tts/piper.rs b/src/workers/continuum-core/src/live/audio/tts/piper.rs index f2300dc0f..a1802e6a4 100644 --- a/src/workers/continuum-core/src/live/audio/tts/piper.rs +++ b/src/workers/continuum-core/src/live/audio/tts/piper.rs @@ -191,7 +191,9 @@ impl TextToSpeech for PiperTTS { // (#964 family). The helper uses the correct `feature = // "metal"` gate that matches Cargo.toml. let providers = crate::inference::ort_providers::build_ort_gpu_execution_providers() - .map_err(|e| TTSError::ModelNotLoaded(format!("ORT GPU EP setup failed (Piper TTS): {e}")))?; + .map_err(|e| { + TTSError::ModelNotLoaded(format!("ORT GPU EP setup failed (Piper TTS): {e}")) + })?; builder = builder.with_execution_providers(providers)?; builder .with_optimization_level(GraphOptimizationLevel::Level3)? diff --git a/src/workers/continuum-core/src/live/audio/vad/silero.rs b/src/workers/continuum-core/src/live/audio/vad/silero.rs index 5c5d93977..3c632c0bc 100644 --- a/src/workers/continuum-core/src/live/audio/vad/silero.rs +++ b/src/workers/continuum-core/src/live/audio/vad/silero.rs @@ -229,7 +229,9 @@ impl VoiceActivityDetection for SileroVAD { // overhead per frame) is ORT's call to make once it sees the model // graph + the GPU device profile. let providers = crate::inference::ort_providers::build_ort_gpu_execution_providers() - .map_err(|e| VADError::ModelNotLoaded(format!("ORT GPU EP setup failed (Silero VAD): {e}")))?; + .map_err(|e| { + VADError::ModelNotLoaded(format!("ORT GPU EP setup failed (Silero VAD): {e}")) + })?; // Load model with ONNX Runtime let session = Session::builder() .map_err(|e| VADError::ModelNotLoaded(e.to_string()))? diff --git a/src/workers/continuum-core/src/live/audio/vad/silero_raw.rs b/src/workers/continuum-core/src/live/audio/vad/silero_raw.rs index 21ca0235f..61be3e809 100644 --- a/src/workers/continuum-core/src/live/audio/vad/silero_raw.rs +++ b/src/workers/continuum-core/src/live/audio/vad/silero_raw.rs @@ -162,7 +162,9 @@ impl VoiceActivityDetection for SileroRawVAD { // must run on GPU. Pre-this-PR Silero never configured an EP at all, // so ORT's implicit CPU EP took every op silently. let providers = crate::inference::ort_providers::build_ort_gpu_execution_providers() - .map_err(|e| VADError::ModelNotLoaded(format!("ORT GPU EP setup failed (Silero VAD raw): {e}")))?; + .map_err(|e| { + VADError::ModelNotLoaded(format!("ORT GPU EP setup failed (Silero VAD raw): {e}")) + })?; // Load ONNX model let session = Session::builder() .map_err(|e| VADError::ModelNotLoaded(e.to_string()))? diff --git a/src/workers/continuum-core/src/model_registry/mod.rs b/src/workers/continuum-core/src/model_registry/mod.rs index 780d499b0..e0c022744 100644 --- a/src/workers/continuum-core/src/model_registry/mod.rs +++ b/src/workers/continuum-core/src/model_registry/mod.rs @@ -24,6 +24,6 @@ pub use artifacts::{ resolve_local_model_dir_for_model_id, }; pub use catalog::{models as catalog_models, providers as catalog_providers}; -pub use loader::{Registry, RegistryError, load_models, load_providers, load_registry}; +pub use loader::{load_models, load_providers, load_registry, Registry, RegistryError}; pub use singleton::{global, init_global, try_global}; pub use types::{Arch, AuthKind, Capability, Model, Provider}; diff --git a/src/workers/continuum-core/src/model_registry/singleton.rs b/src/workers/continuum-core/src/model_registry/singleton.rs index cb87326f6..30c9b5842 100644 --- a/src/workers/continuum-core/src/model_registry/singleton.rs +++ b/src/workers/continuum-core/src/model_registry/singleton.rs @@ -16,7 +16,7 @@ //! A deferred `init_global` keeps that control. use super::catalog; -use super::loader::{Registry, RegistryError, load_registry}; +use super::loader::{load_registry, Registry, RegistryError}; use std::path::Path; use std::sync::OnceLock; diff --git a/src/workers/continuum-core/src/modules/airc.rs b/src/workers/continuum-core/src/modules/airc.rs index 83af60a16..dff339a0e 100644 --- a/src/workers/continuum-core/src/modules/airc.rs +++ b/src/workers/continuum-core/src/modules/airc.rs @@ -1,10 +1,10 @@ //! ServiceModule adapter for Rust-native AIRC commands. use crate::airc::{ - AircEventTransport, AircQueueClient, AircQueueListRequest, AircQueueScanParams, - AircRealtimePublishParams, AircRealtimeReplayParams, AircRealtimeStore, CliAircQueueClient, - DaemonAircEventTransport, InMemoryAircRealtimeStore, StoreAircEventTransport, - TokioAircCommandRunner, default_socket_path_in, spawn_daemon_attach, + default_socket_path_in, spawn_daemon_attach, AircEventTransport, AircQueueClient, + AircQueueListRequest, AircQueueScanParams, AircRealtimePublishParams, AircRealtimeReplayParams, + AircRealtimeStore, CliAircQueueClient, DaemonAircEventTransport, InMemoryAircRealtimeStore, + StoreAircEventTransport, TokioAircCommandRunner, }; use crate::runtime::{ CommandResult, CommandSchema, ModuleConfig, ModuleContext, ModulePriority, ParamSchema, diff --git a/src/workers/continuum-core/src/modules/cognition.rs b/src/workers/continuum-core/src/modules/cognition.rs index 6f097a256..f46cb6304 100644 --- a/src/workers/continuum-core/src/modules/cognition.rs +++ b/src/workers/continuum-core/src/modules/cognition.rs @@ -717,13 +717,11 @@ impl ServiceModule for CognitionModule { crate::cognition::tool_embedding::SemanticSearchToolsRequest, >(params.clone()) .map_err(|e| format!("Invalid semantic-search-tools request: {e}"))?; - let results = - crate::cognition::tool_embedding::semantic_search_tools(request) - .await - .map_err(|e| format!("semantic-search-tools error: {e}"))?; + let results = crate::cognition::tool_embedding::semantic_search_tools(request) + .await + .map_err(|e| format!("semantic-search-tools error: {e}"))?; Ok(CommandResult::Json( - serde_json::to_value(&results) - .map_err(|e| format!("Serialize error: {e}"))?, + serde_json::to_value(&results).map_err(|e| format!("Serialize error: {e}"))?, )) } @@ -743,8 +741,7 @@ impl ServiceModule for CognitionModule { .await .map_err(|e| format!("validate-response-decision error: {e}"))?; Ok(CommandResult::Json( - serde_json::to_value(&decision) - .map_err(|e| format!("Serialize error: {e}"))?, + serde_json::to_value(&decision).map_err(|e| format!("Serialize error: {e}"))?, )) } diff --git a/src/workers/continuum-core/src/modules/docker_tier.rs b/src/workers/continuum-core/src/modules/docker_tier.rs index 0d80ef6d3..772cfd879 100644 --- a/src/workers/continuum-core/src/modules/docker_tier.rs +++ b/src/workers/continuum-core/src/modules/docker_tier.rs @@ -41,7 +41,11 @@ use ts_rs::TS; /// Result of probing the Docker storage tier on the current host. #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[serde(rename_all = "camelCase", rename_all_fields = "camelCase", tag = "kind")] +#[serde( + rename_all = "camelCase", + rename_all_fields = "camelCase", + tag = "kind" +)] #[ts( export, export_to = "../../../shared/generated/system/DockerTierProbe.ts" @@ -77,10 +81,7 @@ pub enum DockerTierProbe { /// Returning the variant rather than panicking lets callers carry /// on (the resource manager treats unprobeable tiers as `unknown /// capacity` and refuses to bound on them). - Unsupported { - os: String, - reason: String, - }, + Unsupported { os: String, reason: String }, } impl DockerTierProbe { diff --git a/src/workers/continuum-core/src/modules/docker_tier_pool.rs b/src/workers/continuum-core/src/modules/docker_tier_pool.rs index 0884ad926..e63097502 100644 --- a/src/workers/continuum-core/src/modules/docker_tier_pool.rs +++ b/src/workers/continuum-core/src/modules/docker_tier_pool.rs @@ -271,10 +271,7 @@ fn now_ms() -> u64 { /// line that `docker system prune` always emits on success. Format is /// stable across Docker Desktop versions (verified Docker 24.x + 25.x). fn run_docker_prune(args: &[&str]) -> Option { - let output = Command::new("docker") - .args(args) - .output() - .ok()?; // None if `docker` binary not in PATH. + let output = Command::new("docker").args(args).output().ok()?; // None if `docker` binary not in PATH. if !output.status.success() { return None; // Daemon down / permission denied / etc. } @@ -375,7 +372,10 @@ mod tests { fn parse_reclaimed_bytes_handles_all_units() { // Real Docker outputs (Docker 24.x verified): let cases = [ - ("Deleted Containers:\nfoo\nTotal reclaimed space: 0B\n", 0u64), + ( + "Deleted Containers:\nfoo\nTotal reclaimed space: 0B\n", + 0u64, + ), ("...\nTotal reclaimed space: 512B\n", 512), ("...\nTotal reclaimed space: 1.5kB\n", 1_500), ("...\nTotal reclaimed space: 250MB\n", 250_000_000), @@ -404,8 +404,8 @@ mod tests { let cases = [ "", "some unrelated docker output", - "Total reclaimed space:", // header but no value - "Total reclaimed space: 5XYZ", // unknown unit + "Total reclaimed space:", // header but no value + "Total reclaimed space: 5XYZ", // unknown unit "Total reclaimed space: not-a-number GB", ]; for input in cases { @@ -428,7 +428,8 @@ mod tests { /// "Total reclaimed space:" is the canonical total. #[test] fn parse_reclaimed_bytes_picks_last_summary_line() { - let input = "Total reclaimed space: 100MB\nDeleted Volumes:\nTotal reclaimed space: 250MB\n"; + let input = + "Total reclaimed space: 100MB\nDeleted Volumes:\nTotal reclaimed space: 250MB\n"; // Last line wins → 250MB assert_eq!(parse_reclaimed_bytes(input), Some(250_000_000)); } @@ -457,7 +458,10 @@ mod tests { ); } _ => { - assert!(snap.is_empty(), "non-Detected tier should yield zero entries"); + assert!( + snap.is_empty(), + "non-Detected tier should yield zero entries" + ); } } } diff --git a/src/workers/continuum-core/src/modules/events.rs b/src/workers/continuum-core/src/modules/events.rs index bdc547923..077774388 100644 --- a/src/workers/continuum-core/src/modules/events.rs +++ b/src/workers/continuum-core/src/modules/events.rs @@ -172,7 +172,10 @@ mod tests { let name = "ipc-test:declare-then-get"; let result = module - .handle_command("events/declare-class", declare_params_broadcast_global(name)) + .handle_command( + "events/declare-class", + declare_params_broadcast_global(name), + ) .await .unwrap(); match result { @@ -185,10 +188,7 @@ mod tests { } let result = module - .handle_command( - "events/get-class", - serde_json::json!({ "name": name }), - ) + .handle_command("events/get-class", serde_json::json!({ "name": name })) .await .unwrap(); match result { @@ -239,7 +239,10 @@ mod tests { let module = EventsModule::new(); let name = "ipc-test:resolve-global"; module - .handle_command("events/declare-class", declare_params_broadcast_global(name)) + .handle_command( + "events/declare-class", + declare_params_broadcast_global(name), + ) .await .unwrap(); @@ -276,9 +279,9 @@ mod tests { match result { CommandResult::Json(v) => { let arr = v.as_array().expect("list returns array"); - let found = arr.iter().any(|c| { - c.get("name").and_then(|n| n.as_str()) == Some(name) - }); + let found = arr + .iter() + .any(|c| c.get("name").and_then(|n| n.as_str()) == Some(name)); assert!(found, "declared class should appear in list"); } _ => panic!("expected json array"), diff --git a/src/workers/continuum-core/src/modules/forge.rs b/src/workers/continuum-core/src/modules/forge.rs index 030e9bbaa..a9a0696d6 100644 --- a/src/workers/continuum-core/src/modules/forge.rs +++ b/src/workers/continuum-core/src/modules/forge.rs @@ -85,7 +85,8 @@ impl ServiceModule for ForgeModule { let parsed: ForgeRunParams = serde_json::from_value(params) .map_err(|e| format!("forge/run: invalid params: {e}"))?; - let artifact = synthesize_stub_artifact(&parsed.recipe, parsed.hardware_node.as_deref())?; + let artifact = + synthesize_stub_artifact(&parsed.recipe, parsed.hardware_node.as_deref())?; let json = serde_json::to_value(&artifact) .map_err(|e| format!("forge/run: serialize artifact: {e}"))?; Ok(CommandResult::Json(json)) @@ -102,7 +103,10 @@ impl ServiceModule for ForgeModule { /// Synthesize a stub `ForgeArtifact` from a recipe. Phase 4 placeholder /// — real foundry execution lands in Phase 5+. Caller persists the /// returned artifact via `data/upsert` against `forge_artifacts`. -fn synthesize_stub_artifact(recipe: &ForgeRecipe, hardware_node: Option<&str>) -> Result { +fn synthesize_stub_artifact( + recipe: &ForgeRecipe, + hardware_node: Option<&str>, +) -> Result { let now_ms = SystemTime::now() .duration_since(UNIX_EPOCH) .map_err(|e| format!("system time before epoch: {e}"))? @@ -111,7 +115,16 @@ fn synthesize_stub_artifact(recipe: &ForgeRecipe, hardware_node: Option<&str>) - // Derive an identifiable stub hash from the recipe id (first 16 hex // chars). Real Phase 5 hash will be sha256 of the populated alloy // content. Stub format prefix avoids collision with real hashes. - let stub_hash = format!("sha256:stub-{}", recipe.id.simple().to_string().chars().take(16).collect::()); + let stub_hash = format!( + "sha256:stub-{}", + recipe + .id + .simple() + .to_string() + .chars() + .take(16) + .collect::() + ); Ok(ForgeArtifact { id: Uuid::new_v4(), @@ -257,7 +270,10 @@ mod tests { assert_eq!(artifact.hardware_verified.len(), 1); assert_eq!(artifact.hardware_verified[0].device, "m5-pro@local"); assert_eq!(artifact.hardware_verified[0].format, "stub"); - assert!(!artifact.hardware_verified[0].verified, "stub is not verified"); + assert!( + !artifact.hardware_verified[0].verified, + "stub is not verified" + ); } /// What this catches: with no hardware_node, hardware_verified diff --git a/src/workers/continuum-core/src/modules/pressure_broker_module.rs b/src/workers/continuum-core/src/modules/pressure_broker_module.rs index f9ef7493b..ffbe3197d 100644 --- a/src/workers/continuum-core/src/modules/pressure_broker_module.rs +++ b/src/workers/continuum-core/src/modules/pressure_broker_module.rs @@ -27,7 +27,7 @@ //! pattern keeps the boot sequence in `ipc/mod.rs` uniform and gives the //! broker the same shutdown / metrics treatment as everything else. -use crate::governor::{SubstrateGovernor, governor_alert_sink}; +use crate::governor::{governor_alert_sink, SubstrateGovernor}; use crate::modules::docker_tier_pool::DockerTierPool; use crate::paging::{BrokerConfig, PressureBroker, ResourcePool}; use crate::runtime::{CommandResult, ModuleConfig, ModuleContext, ModulePriority, ServiceModule}; diff --git a/src/workers/continuum-core/src/modules/vdd.rs b/src/workers/continuum-core/src/modules/vdd.rs index f9317df6f..08125f6c5 100644 --- a/src/workers/continuum-core/src/modules/vdd.rs +++ b/src/workers/continuum-core/src/modules/vdd.rs @@ -116,7 +116,11 @@ impl ServiceModule for VddModule { let report = if latest_only { let collapsed = latest_per_scenario(entries); - build_report(collapsed.into_values().collect(), &self.artifact_root, &opts) + build_report( + collapsed.into_values().collect(), + &self.artifact_root, + &opts, + ) } else { build_report(entries, &self.artifact_root, &opts) }; @@ -347,10 +351,25 @@ mod tests { async fn report_aggregates_summary_across_record_statuses() { let tmp = tempfile::tempdir().unwrap(); // 2 pass on different shas. - write(tmp.path(), "sha-a", "chat-roundtrip-live-harness", HarnessStatus::Pass); - write(tmp.path(), "sha-b", "chat-roundtrip-live-harness", HarnessStatus::Pass); + write( + tmp.path(), + "sha-a", + "chat-roundtrip-live-harness", + HarnessStatus::Pass, + ); + write( + tmp.path(), + "sha-b", + "chat-roundtrip-live-harness", + HarnessStatus::Pass, + ); // 1 fail. - write(tmp.path(), "sha-c", "chat-roundtrip-live-harness", HarnessStatus::Fail); + write( + tmp.path(), + "sha-c", + "chat-roundtrip-live-harness", + HarnessStatus::Fail, + ); // 1 prerequisite_missing. write( tmp.path(), @@ -383,7 +402,12 @@ mod tests { async fn report_git_sha_filter_narrows_results_and_echoes_back() { let tmp = tempfile::tempdir().unwrap(); for sha in ["sha-a", "sha-b", "sha-c"] { - write(tmp.path(), sha, "chat-roundtrip-live-harness", HarnessStatus::Pass); + write( + tmp.path(), + sha, + "chat-roundtrip-live-harness", + HarnessStatus::Pass, + ); } let module = VddModule::with_root(tmp.path()); @@ -490,7 +514,9 @@ mod tests { "source path points at the on-disk record file" ); assert!( - report.artifact_root.contains(tmp.path().file_name().unwrap().to_str().unwrap()), + report + .artifact_root + .contains(tmp.path().file_name().unwrap().to_str().unwrap()), "artifact_root surfaces the resolved root path" ); } diff --git a/src/workers/continuum-core/src/orm/connection_manager.rs b/src/workers/continuum-core/src/orm/connection_manager.rs index da92d5f41..fa03c2cdc 100644 --- a/src/workers/continuum-core/src/orm/connection_manager.rs +++ b/src/workers/continuum-core/src/orm/connection_manager.rs @@ -87,8 +87,7 @@ impl ManagedPool { } fn touch(&self) { - self.last_access - .store(Self::now_nanos(), Ordering::Relaxed); + self.last_access.store(Self::now_nanos(), Ordering::Relaxed); } fn last_access_nanos(&self) -> u64 { diff --git a/src/workers/continuum-core/src/paging/broker.rs b/src/workers/continuum-core/src/paging/broker.rs index 125f92ba8..888f5c273 100644 --- a/src/workers/continuum-core/src/paging/broker.rs +++ b/src/workers/continuum-core/src/paging/broker.rs @@ -74,10 +74,7 @@ fn evict_amount_for(pool: &dyn ResourcePool) -> u64 { /// — operators can pattern-match without stringly-typed comparisons. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "lowercase")] -#[ts( - export, - export_to = "../../../shared/generated/paging/PressureTier.ts" -)] +#[ts(export, export_to = "../../../shared/generated/paging/PressureTier.ts")] pub enum PressureTier { /// All pools comfortably under their budgets. Normal, @@ -131,10 +128,7 @@ impl Default for BrokerConfig { /// Per-pool snapshot exposed to monitoring / IPC. #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/paging/PoolView.ts" -)] +#[ts(export, export_to = "../../../shared/generated/paging/PoolView.ts")] pub struct PoolView { pub name: String, pub pressure: f64, @@ -614,10 +608,7 @@ mod tests { report.triggered, "broker should fire on real pool over budget" ); - assert!( - report.bytes_freed > 0, - "evict_at_least should free bytes" - ); + assert!(report.bytes_freed > 0, "evict_at_least should free bytes"); assert_eq!(report.pools_acted, vec!["real-embeddings".to_string()]); // Pressure should drop after eviction. assert!( diff --git a/src/workers/continuum-core/src/paging/pool.rs b/src/workers/continuum-core/src/paging/pool.rs index 0317f11be..9eb4db826 100644 --- a/src/workers/continuum-core/src/paging/pool.rs +++ b/src/workers/continuum-core/src/paging/pool.rs @@ -40,8 +40,8 @@ use std::collections::HashMap; use std::future::Future; use std::hash::Hash; use std::pin::Pin; -use std::sync::Arc; use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; use tokio::sync::Mutex; use ts_rs::TS; diff --git a/src/workers/continuum-core/src/paths/docker.rs b/src/workers/continuum-core/src/paths/docker.rs index 543e62036..e9aab56c0 100644 --- a/src/workers/continuum-core/src/paths/docker.rs +++ b/src/workers/continuum-core/src/paths/docker.rs @@ -78,7 +78,10 @@ mod tests { #[test] #[cfg(target_os = "macos")] fn macos_with_home_resolves_to_docker_raw() { - if std::env::var("HOME").map(|h| !h.is_empty()).unwrap_or(false) { + if std::env::var("HOME") + .map(|h| !h.is_empty()) + .unwrap_or(false) + { match raw_image_path() { DockerRawPath::Resolved(p) => { assert!( diff --git a/src/workers/continuum-core/src/persona/admission/mod.rs b/src/workers/continuum-core/src/persona/admission/mod.rs index 838046160..c56694e05 100644 --- a/src/workers/continuum-core/src/persona/admission/mod.rs +++ b/src/workers/continuum-core/src/persona/admission/mod.rs @@ -68,11 +68,11 @@ use uuid::Uuid; // Re-exported pub so submodules (`recipes`) can import via `super::` // without reaching across to `crate::persona::engram` for every type. +use super::engram::Engram; pub use super::engram::{ AdmissionDecision, AdmissionDropReason, AdmissionError, AircMessageRef, EngramKind, EngramOrigin, TrustState, }; -use super::engram::Engram; use super::trace::{now_ms, CognitionTrace, SEAM_ADMISSION}; //============================================================================= @@ -307,7 +307,13 @@ impl AdmissionGate { // Step 1: Envelope structure if let Err(err) = verify_envelope(&candidate.origin) { - record_seam(trace.as_deref_mut(), recipe.id(), started, "EnvelopeVerificationFailed", None); + record_seam( + trace.as_deref_mut(), + recipe.id(), + started, + "EnvelopeVerificationFailed", + None, + ); return Err(err); } @@ -317,7 +323,13 @@ impl AdmissionGate { source_trust: candidate.trust_state, threshold: ctx.config.trust_threshold, }; - record_seam(trace.as_deref_mut(), recipe.id(), started, "TrustBoundaryRejected", None); + record_seam( + trace.as_deref_mut(), + recipe.id(), + started, + "TrustBoundaryRejected", + None, + ); return Err(err); } @@ -328,7 +340,13 @@ impl AdmissionGate { event_id, previously_seen_at_ms: prev_ms, }; - record_seam(trace.as_deref_mut(), recipe.id(), started, "ReplayDetected", None); + record_seam( + trace.as_deref_mut(), + recipe.id(), + started, + "ReplayDetected", + None, + ); return Err(err); } } @@ -386,9 +404,9 @@ fn verify_envelope(origin: &EngramOrigin) -> Result<(), AdmissionError> { EngramOrigin::Airc(r) => verify_airc_envelope(r), // Local-trust origins (chat/tool/self-reflection) don't carry // signed envelopes; structural verification is trivially OK. - EngramOrigin::Chat(_) - | EngramOrigin::Tool(_) - | EngramOrigin::SelfReflection { .. } => Ok(()), + EngramOrigin::Chat(_) | EngramOrigin::Tool(_) | EngramOrigin::SelfReflection { .. } => { + Ok(()) + } } } @@ -572,7 +590,12 @@ mod tests { EngramOrigin::Airc(airc_ref("msg-1", "", "hash", "v1")), ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); match result { Err(AdmissionError::EnvelopeVerificationFailed { detail }) => { assert!(detail.contains("signature"), "detail: {detail}"); @@ -604,7 +627,12 @@ mod tests { EngramOrigin::Airc(airc_ref("msg-x", "sig", "", "v1")), ); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)) { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) { Err(AdmissionError::EnvelopeVerificationFailed { detail }) => { assert!(detail.contains("content_hash"), "detail: {detail}"); } @@ -632,7 +660,12 @@ mod tests { EngramOrigin::Airc(airc_ref("msg-x", "sig", "hash", "")), ); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)) { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) { Err(AdmissionError::EnvelopeVerificationFailed { detail }) => { assert!(detail.contains("schema_version"), "detail: {detail}"); } @@ -659,7 +692,12 @@ mod tests { EngramOrigin::Airc(airc_ref("msg-x", "sig", "hash", "v2")), ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); match result { Err(AdmissionError::UnsupportedSchemaVersion { schema_version }) => { assert_eq!(schema_version, "v2"); @@ -695,8 +733,13 @@ mod tests { }, ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)) - .expect("self-reflection should pass structural checks"); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .expect("self-reflection should pass structural checks"); match result { AdmissionDecision::Admit { engram, .. } => { assert_eq!(engram.trust_state_at_admission, TrustState::SelfTrust); @@ -726,9 +769,18 @@ mod tests { let mut trace = CognitionTrace::new(); // ApprovedPeer is below IntragridMember (strict_v1's threshold). - let cand = airc_candidate("totally legitimate content here", TrustState::ApprovedPeer, "msg-2"); + let cand = airc_candidate( + "totally legitimate content here", + TrustState::ApprovedPeer, + "msg-2", + ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); match result { Err(AdmissionError::TrustBoundaryRejected { source_trust, @@ -758,8 +810,13 @@ mod tests { "msg-3", ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)) - .expect("equal-tier source should pass threshold"); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .expect("equal-tier source should pass threshold"); assert!(matches!(result, AdmissionDecision::Admit { .. })); } @@ -774,13 +831,26 @@ mod tests { let cfg = AdmissionConfig::permissive_v1(); let content = InMemoryContent::default(); let events = InMemoryEvents::default(); - events.0.lock().unwrap().insert("msg-replay".to_string(), 1_000_000); + events + .0 + .lock() + .unwrap() + .insert("msg-replay".to_string(), 1_000_000); let ctx = permissive_ctx(&cfg, &content, &events); let mut trace = CognitionTrace::new(); - let cand = airc_candidate("perfectly novel content here", TrustState::ApprovedPeer, "msg-replay"); + let cand = airc_candidate( + "perfectly novel content here", + TrustState::ApprovedPeer, + "msg-replay", + ); - let result = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let result = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); match result { Err(AdmissionError::ReplayDetected { event_id, @@ -817,8 +887,13 @@ mod tests { }, ); - AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)) - .expect("non-airc origin should bypass replay check"); + AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .expect("non-airc origin should bypass replay check"); } // (HeuristicIsMemorable policy tests moved to admission/recipes.rs @@ -845,7 +920,12 @@ mod tests { TrustState::ApprovedPeer, EngramOrigin::Airc(airc_ref("e1", "", "h", "v1")), ); - let _ = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let _ = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); } assert_eq!(trace.seam_count(), 1); @@ -859,7 +939,12 @@ mod tests { TrustState::ApprovedPeer, "e2", ); - let _ = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let _ = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); } assert_eq!(trace.seam_count(), 2); @@ -869,7 +954,12 @@ mod tests { let events = InMemoryEvents::default(); let ctx = permissive_ctx(&cfg, &content, &events); let cand = airc_candidate("short", TrustState::ApprovedPeer, "e3"); - let _ = AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)); + let _ = AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ); } assert_eq!(trace.seam_count(), 3); @@ -897,7 +987,13 @@ mod tests { "msg-trace-1", ); - AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)).unwrap(); + AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .unwrap(); let seam = &trace.seams[0]; assert_eq!(seam.metadata["recipe"], serde_json::json!("heuristic.v1")); assert_eq!(seam.metadata["structural"], serde_json::json!("accepted")); @@ -997,7 +1093,10 @@ mod tests { other => panic!("expected Quarantine, got {other:?}"), } // Trace metadata should carry the Quarantine decision label. - assert_eq!(trace.seams[0].metadata["decision"], serde_json::json!("Quarantine")); + assert_eq!( + trace.seams[0].metadata["decision"], + serde_json::json!("Quarantine") + ); } // ── AdmissionConfig presets ───────────────────────────────────────── diff --git a/src/workers/continuum-core/src/persona/admission/recipes.rs b/src/workers/continuum-core/src/persona/admission/recipes.rs index 73730a4ef..12bb1aec9 100644 --- a/src/workers/continuum-core/src/persona/admission/recipes.rs +++ b/src/workers/continuum-core/src/persona/admission/recipes.rs @@ -49,9 +49,7 @@ impl HeuristicIsMemorable { pub fn default_v1() -> Self { Self::with_noise_phrases( 16, - [ - "ack", "ok", "okay", "thanks", "thx", "got it", "+1", "👍", - ], + ["ack", "ok", "okay", "thanks", "thx", "got it", "+1", "👍"], ) } @@ -87,7 +85,10 @@ impl IsMemorable for HeuristicIsMemorable { ctx: &AdmissionContext<'_>, ) -> Result { // Dedup first — cheapest check, eliminates the most common drop case. - if let Some(existing) = ctx.seen_content.find_by_content_hash(&candidate.content_hash) { + if let Some(existing) = ctx + .seen_content + .find_by_content_hash(&candidate.content_hash) + { return Ok(AdmissionDecision::Drop { reason: AdmissionDropReason::Duplicate { existing_engram_id: existing, @@ -137,8 +138,8 @@ impl IsMemorable for HeuristicIsMemorable { #[cfg(test)] mod tests { use super::super::{ - AdmissionConfig, AdmissionContext, AdmissionGate, AircMessageRef, EngramKind, - EngramOrigin, SeenContentLookup, SeenEventLookup, TrustState, + AdmissionConfig, AdmissionContext, AdmissionGate, AircMessageRef, EngramKind, EngramOrigin, + SeenContentLookup, SeenEventLookup, TrustState, }; use super::*; use crate::persona::trace::CognitionTrace; @@ -186,11 +187,7 @@ mod tests { } } - fn airc_candidate( - content: &str, - trust: TrustState, - message_id: &str, - ) -> AdmissionCandidate { + fn airc_candidate(content: &str, trust: TrustState, message_id: &str) -> AdmissionCandidate { AdmissionCandidate { content: content.to_string(), kind: EngramKind::Episodic, @@ -228,12 +225,25 @@ mod tests { let cand = airc_candidate("short", TrustState::ApprovedPeer, "msg-short"); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)).unwrap() { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .unwrap() + { AdmissionDecision::Drop { reason: AdmissionDropReason::NotMemorable { explanation }, } => { - assert!(explanation.contains("too short"), "explanation: {explanation}"); - assert!(explanation.contains("16"), "must mention threshold: {explanation}"); + assert!( + explanation.contains("too short"), + "explanation: {explanation}" + ); + assert!( + explanation.contains("16"), + "must mention threshold: {explanation}" + ); } other => panic!("expected Drop NotMemorable, got {other:?}"), } @@ -253,11 +263,21 @@ mod tests { let padded = " ACK "; let cand = airc_candidate(padded, TrustState::ApprovedPeer, "msg-noise"); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)).unwrap() { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .unwrap() + { AdmissionDecision::Drop { reason: AdmissionDropReason::NotMemorable { explanation }, } => { - assert!(explanation.contains("noise phrase"), "explanation: {explanation}"); + assert!( + explanation.contains("noise phrase"), + "explanation: {explanation}" + ); } other => panic!("expected Drop NotMemorable for noise phrase, got {other:?}"), } @@ -281,10 +301,21 @@ mod tests { let ctx = permissive_ctx(&cfg, &content, &events); let mut trace = CognitionTrace::new(); - let cand = airc_candidate("twenty-nine character content", TrustState::ApprovedPeer, "msg-d"); + let cand = airc_candidate( + "twenty-nine character content", + TrustState::ApprovedPeer, + "msg-d", + ); assert_eq!(cand.content_hash, "sha256:fake-29"); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)).unwrap() { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .unwrap() + { AdmissionDecision::Drop { reason: AdmissionDropReason::Duplicate { existing_engram_id }, } => { @@ -312,7 +343,14 @@ mod tests { "msg-admit-1", ); - match AdmissionGate::admit(&cand, &HeuristicIsMemorable::default_v1(), &ctx, Some(&mut trace)).unwrap() { + match AdmissionGate::admit( + &cand, + &HeuristicIsMemorable::default_v1(), + &ctx, + Some(&mut trace), + ) + .unwrap() + { AdmissionDecision::Admit { engram, why } => { assert_eq!(engram.kind, EngramKind::Episodic); assert_eq!(engram.trust_state_at_admission, TrustState::IntragridMember); diff --git a/src/workers/continuum-core/src/persona/admission_state.rs b/src/workers/continuum-core/src/persona/admission_state.rs index d99a044b9..247e2dd27 100644 --- a/src/workers/continuum-core/src/persona/admission_state.rs +++ b/src/workers/continuum-core/src/persona/admission_state.rs @@ -179,12 +179,10 @@ impl AdmissionState { fn record_admitted(&self, engram: &Engram) { match &engram.origin { EngramOrigin::Chat(r) => { - self.seen_content - .record(r.content_hash.clone(), engram.id); + self.seen_content.record(r.content_hash.clone(), engram.id); } EngramOrigin::Airc(r) => { - self.seen_content - .record(r.content_hash.clone(), engram.id); + self.seen_content.record(r.content_hash.clone(), engram.id); self.seen_events .record(r.message_id.clone(), engram.admitted_at_ms); } @@ -230,7 +228,9 @@ impl AdmissionState { /// True iff `content_hash` is recorded as seen in the dedup store. pub fn is_content_seen(&self, content_hash: &str) -> bool { - self.seen_content.find_by_content_hash(content_hash).is_some() + self.seen_content + .find_by_content_hash(content_hash) + .is_some() } /// True iff the AIRC event_id is recorded in the replay-protection store. @@ -301,11 +301,7 @@ impl AdmissionState { /// SelfReflection). Newest first, capped at `limit`. Useful for /// callers that want "what did I learn from chat" vs "what did I /// learn from tool invocations". - pub fn recall_by_origin_kind( - &self, - kind: EngramOriginKind, - limit: usize, - ) -> Vec { + pub fn recall_by_origin_kind(&self, kind: EngramOriginKind, limit: usize) -> Vec { if limit == 0 { return Vec::new(); } @@ -574,7 +570,11 @@ mod tests { !state.is_content_seen(&content_hash), "chat-origin quarantine MUST NOT record content_hash (would dangle)" ); - assert_eq!(state.engram_count(), 0, "quarantine MUST NOT add to engram store"); + assert_eq!( + state.engram_count(), + 0, + "quarantine MUST NOT add to engram store" + ); } /// What this catches: Quarantine of an AIRC-origin engram records @@ -585,10 +585,8 @@ mod tests { fn quarantine_airc_origin_records_event_id_only_not_content_hash() { let state = AdmissionState::new(); let event_id = "airc-msg-quarantine-1"; - let engram = synthetic_engram_with_airc_origin( - "borderline observation worth holding", - event_id, - ); + let engram = + synthetic_engram_with_airc_origin("borderline observation worth holding", event_id); let content_hash = match &engram.origin { EngramOrigin::Airc(r) => r.content_hash.clone(), _ => unreachable!(), @@ -609,7 +607,11 @@ mod tests { !state.is_content_seen(&content_hash), "airc-origin quarantine MUST NOT record content_hash (would dangle)" ); - assert_eq!(state.engram_count(), 0, "quarantine MUST NOT add to engram store"); + assert_eq!( + state.engram_count(), + 0, + "quarantine MUST NOT add to engram store" + ); } // ── Recall surface (#1121 PR-5) ────────────────────────────────────── @@ -620,7 +622,10 @@ mod tests { let mut trace = CognitionTrace::new(); let mut ids = Vec::new(); for c in contents { - match state.admit(&synthetic_human_message(c), Some(&mut trace)).unwrap() { + match state + .admit(&synthetic_human_message(c), Some(&mut trace)) + .unwrap() + { AdmissionDecision::Admit { engram, .. } => ids.push(engram.id), other => panic!("expected Admit for content {c:?}, got {other:?}"), } @@ -664,7 +669,11 @@ mod tests { ); assert_eq!(state.recall_recent(0).len(), 0, "limit=0 returns empty"); assert_eq!(state.recall_recent(1).len(), 1, "limit=1 returns one"); - assert_eq!(state.recall_recent(99).len(), 2, "limit > count caps at count"); + assert_eq!( + state.recall_recent(99).len(), + 2, + "limit > count caps at count" + ); } /// What this catches: recall_by_id returns the exact engram for a @@ -675,12 +684,18 @@ mod tests { let state = AdmissionState::new(); let ids = admit_n_distinct( &state, - &["first observation worth storing", "second observation worth storing"], + &[ + "first observation worth storing", + "second observation worth storing", + ], ); let found = state.recall_by_id(ids[0]).expect("known id must resolve"); assert_eq!(found.id, ids[0]); assert_eq!(found.content, "first observation worth storing"); - assert!(state.recall_by_id(Uuid::new_v4()).is_none(), "unknown id is None"); + assert!( + state.recall_by_id(Uuid::new_v4()).is_none(), + "unknown id is None" + ); } /// What this catches: keyword search is case-insensitive substring, @@ -698,7 +713,11 @@ mod tests { ], ); let hits = state.recall_by_keyword("recall", 10); - assert_eq!(hits.len(), 2, "two engrams contain 'recall' (case-insensitive)"); + assert_eq!( + hits.len(), + 2, + "two engrams contain 'recall' (case-insensitive)" + ); // Newest first: "another RECALL..." was admitted last. assert!( hits[0].content.contains("another RECALL"), @@ -772,10 +791,8 @@ mod tests { fn admit_airc_origin_still_records_both_content_hash_and_event_id() { let state = AdmissionState::new(); let event_id = "airc-msg-admit-1"; - let engram = synthetic_engram_with_airc_origin( - "valuable observation worth recalling", - event_id, - ); + let engram = + synthetic_engram_with_airc_origin("valuable observation worth recalling", event_id); let content_hash = match &engram.origin { EngramOrigin::Airc(r) => r.content_hash.clone(), _ => unreachable!(), diff --git a/src/workers/continuum-core/src/persona/allocator.rs b/src/workers/continuum-core/src/persona/allocator.rs index edcbde67b..2e92816cf 100644 --- a/src/workers/continuum-core/src/persona/allocator.rs +++ b/src/workers/continuum-core/src/persona/allocator.rs @@ -448,10 +448,22 @@ mod tests { #[test] fn test_select_local_model() { - assert_eq!(select_local_model(32.0), "continuum-ai/qwen3.5-4b-code-forged-GGUF"); - assert_eq!(select_local_model(48.0), "continuum-ai/qwen3.5-4b-code-forged-GGUF"); - assert_eq!(select_local_model(16.0), "continuum-ai/qwen3.5-4b-code-forged-GGUF"); - assert_eq!(select_local_model(4.0), "continuum-ai/qwen3.5-4b-code-forged-GGUF"); + assert_eq!( + select_local_model(32.0), + "continuum-ai/qwen3.5-4b-code-forged-GGUF" + ); + assert_eq!( + select_local_model(48.0), + "continuum-ai/qwen3.5-4b-code-forged-GGUF" + ); + assert_eq!( + select_local_model(16.0), + "continuum-ai/qwen3.5-4b-code-forged-GGUF" + ); + assert_eq!( + select_local_model(4.0), + "continuum-ai/qwen3.5-4b-code-forged-GGUF" + ); } #[test] @@ -493,10 +505,7 @@ mod tests { .iter() .filter(|a| a.provider == "local") .count(); - assert!( - local_count >= 1, - "Should create at least one local persona" - ); + assert!(local_count >= 1, "Should create at least one local persona"); // No cloud personas without API keys let cloud_count = result @@ -617,8 +626,7 @@ mod tests { "Runtime persona provider must be local, not training backend" ); assert_eq!( - first.model, - "continuum-ai/qwen3.5-4b-code-forged-GGUF", + first.model, "continuum-ai/qwen3.5-4b-code-forged-GGUF", "CodeReview should use the Qwen3.5 local registry default" ); @@ -628,8 +636,7 @@ mod tests { .expect("Vision AI should be in the Rust persona catalog"); assert_eq!(vision.provider, "local"); assert_eq!( - vision.model_preferences[0].model, - "qwen2-vl-7b-instruct", + vision.model_preferences[0].model, "qwen2-vl-7b-instruct", "Vision AI should use the Qwen2-VL local registry default" ); } diff --git a/src/workers/continuum-core/src/persona/channel_items.rs b/src/workers/continuum-core/src/persona/channel_items.rs index 7853515ca..77900cf5b 100644 --- a/src/workers/continuum-core/src/persona/channel_items.rs +++ b/src/workers/continuum-core/src/persona/channel_items.rs @@ -276,8 +276,14 @@ impl ChatQueueItem { // VideoFrameQueueItem / GameMoveQueueItem can choose different // trigger rules appropriate to their domain. let latest_with_media = all_messages.iter().rev().find(|m| !m.media.is_empty()); - let trigger = latest_with_media.copied().unwrap_or(*all_messages.last().unwrap()); - let prior: Vec<&ChatQueueItem> = all_messages.iter().copied().filter(|m| m.id != trigger.id).collect(); + let trigger = latest_with_media + .copied() + .unwrap_or(*all_messages.last().unwrap()); + let prior: Vec<&ChatQueueItem> = all_messages + .iter() + .copied() + .filter(|m| m.id != trigger.id) + .collect(); // Build consolidated context let mut context: Vec = self.consolidated_context.clone(); diff --git a/src/workers/continuum-core/src/persona/cognition_io.rs b/src/workers/continuum-core/src/persona/cognition_io.rs index b39414c68..6bad67e21 100644 --- a/src/workers/continuum-core/src/persona/cognition_io.rs +++ b/src/workers/continuum-core/src/persona/cognition_io.rs @@ -206,14 +206,9 @@ impl PersonaContext { /// shaped projection (a `FrameUpdate` or `CodeContext` routed to a /// chat-cognition step is a host bug — surface it loudly here, not /// as silently-wrong cognition output downstream). -pub fn build_respond_input( - signal: &Signal, - ctx: &PersonaContext, -) -> Result { +pub fn build_respond_input(signal: &Signal, ctx: &PersonaContext) -> Result { match &signal.kind { - SignalKind::ChatMessage - | SignalKind::AutonomousTick - | SignalKind::Custom { .. } => {} + SignalKind::ChatMessage | SignalKind::AutonomousTick | SignalKind::Custom { .. } => {} other => { return Err(format!( "build_respond_input: SignalKind::{:?} not supported by the \ @@ -306,24 +301,18 @@ pub fn build_respond_input( /// for variants that don't carry an id). pub fn signal_to_inbox_message(signal: &Signal, ctx: &PersonaContext) -> InboxMessage { let (sender_id, sender_name, sender_type) = match &signal.originator { - SignalOriginator::User { user_id } => { - (*user_id, String::new(), SenderType::Human) - } + SignalOriginator::User { user_id } => (*user_id, String::new(), SenderType::Human), SignalOriginator::Persona { persona_id } => { // Best-effort name — the originator's display name isn't on // Signal. Empty string is acceptable; admission scoring uses // sender_type, not the name. (*persona_id, String::new(), SenderType::Persona) } - SignalOriginator::Tool { tool_name } => { - (Uuid::nil(), tool_name.clone(), SenderType::Agent) - } + SignalOriginator::Tool { tool_name } => (Uuid::nil(), tool_name.clone(), SenderType::Agent), SignalOriginator::GameEngine => { (Uuid::nil(), "game-engine".to_string(), SenderType::System) } - SignalOriginator::System => { - (Uuid::nil(), "system".to_string(), SenderType::System) - } + SignalOriginator::System => (Uuid::nil(), "system".to_string(), SenderType::System), }; InboxMessage { @@ -335,7 +324,11 @@ pub fn signal_to_inbox_message(signal: &Signal, ctx: &PersonaContext) -> InboxMe content: signal.text.clone(), timestamp: signal.timestamp_ms, priority: 0.5, - source_modality: Some(if ctx.is_voice { Modality::Voice } else { Modality::Chat }), + source_modality: Some(if ctx.is_voice { + Modality::Voice + } else { + Modality::Chat + }), voice_session_id: None, } } @@ -368,7 +361,9 @@ mod tests { kind: SignalKind::ChatMessage, text: text.to_string(), media: vec![], - originator: SignalOriginator::User { user_id: Uuid::nil() }, + originator: SignalOriginator::User { + user_id: Uuid::nil(), + }, timestamp_ms: 0, message_id: Some(Uuid::nil()), } @@ -384,7 +379,9 @@ mod tests { kind: SignalKind::ChatMessage, text: "hello".to_string(), media: vec![], - originator: SignalOriginator::User { user_id: Uuid::nil() }, + originator: SignalOriginator::User { + user_id: Uuid::nil(), + }, timestamp_ms: 1234, message_id: Some(Uuid::nil()), }; @@ -451,8 +448,7 @@ mod tests { fn projection_accepts_autonomous_tick() { let mut signal = chat_signal(""); signal.kind = SignalKind::AutonomousTick; - let input = build_respond_input(&signal, &empty_ctx()) - .expect("autonomous tick accepted"); + let input = build_respond_input(&signal, &empty_ctx()).expect("autonomous tick accepted"); assert!(input.message_text.is_empty()); } @@ -471,8 +467,8 @@ mod tests { mime_type: Some("image/png".to_string()), description: None, }]; - let input = build_respond_input(&signal, &empty_ctx()) - .expect("media-bearing chat accepted"); + let input = + build_respond_input(&signal, &empty_ctx()).expect("media-bearing chat accepted"); assert_eq!(input.message_media.len(), 1); assert_eq!(input.message_media[0].item_type, "image"); assert_eq!(input.message_media[0].base64.as_deref(), Some("AAAA")); @@ -561,7 +557,12 @@ mod tests { #[test] fn signal_to_inbox_handles_all_originator_variants() { let cases = [ - (SignalOriginator::Tool { tool_name: "search".to_string() }, SenderType::Agent), + ( + SignalOriginator::Tool { + tool_name: "search".to_string(), + }, + SenderType::Agent, + ), (SignalOriginator::GameEngine, SenderType::System), (SignalOriginator::System, SenderType::System), ]; diff --git a/src/workers/continuum-core/src/persona/engram.rs b/src/workers/continuum-core/src/persona/engram.rs index b329b3c8a..866200e2b 100644 --- a/src/workers/continuum-core/src/persona/engram.rs +++ b/src/workers/continuum-core/src/persona/engram.rs @@ -63,10 +63,7 @@ use uuid::Uuid; /// and contribute to recall via the same mechanisms a biological memory /// store does. #[derive(Debug, Clone, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../../shared/generated/persona/Engram.ts" -)] +#[ts(export, export_to = "../../../shared/generated/persona/Engram.ts")] pub struct Engram { /// Stable engram id. Used for recall keys, deduplication, and as the /// referent target for `EngramOrigin::SelfReflection { parent_engram_id }`. @@ -128,10 +125,7 @@ pub struct Engram { /// across kinds, and the discriminator is cheap. Per the airc design /// discussion 2026-05-13. #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] -#[ts( - export, - export_to = "../../../shared/generated/persona/EngramKind.ts" -)] +#[ts(export, export_to = "../../../shared/generated/persona/EngramKind.ts")] pub enum EngramKind { Episodic, Semantic, @@ -410,7 +404,9 @@ pub enum AdmissionError { /// The source's trust tier is below the configured threshold for any /// admission. Not a `Drop` (which is a policy decision); this is a /// hard structural reject before policy runs. - #[error("trust boundary rejected: source trust {source_trust:?} below threshold {threshold:?}")] + #[error( + "trust boundary rejected: source trust {source_trust:?} below threshold {threshold:?}" + )] TrustBoundaryRejected { source_trust: TrustState, threshold: TrustState, @@ -452,13 +448,8 @@ pub enum AdmissionError { /// /// Ordered roughly from least to most trusted; `PartialOrd` derives so /// admission gates can compare `source_trust >= threshold` directly. -#[derive( - Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS, -)] -#[ts( - export, - export_to = "../../../shared/generated/persona/TrustState.ts" -)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, TS)] +#[ts(export, export_to = "../../../shared/generated/persona/TrustState.ts")] pub enum TrustState { /// Anonymous / unauthenticated — signature missing or fails. Untrusted, @@ -561,7 +552,9 @@ mod tests { #[test] fn engram_origin_self_reflection_carries_parent() { let parent = Uuid::new_v4(); - let origin = EngramOrigin::SelfReflection { parent_engram_id: parent }; + let origin = EngramOrigin::SelfReflection { + parent_engram_id: parent, + }; let json = serde_json::to_string(&origin).expect("serialize"); let back: EngramOrigin = serde_json::from_str(&json).expect("deserialize"); match back { @@ -620,7 +613,10 @@ mod tests { let json = serde_json::to_string(&err).expect("serialize"); let back: AdmissionError = serde_json::from_str(&json).expect("deserialize"); match back { - AdmissionError::TrustBoundaryRejected { source_trust, threshold } => { + AdmissionError::TrustBoundaryRejected { + source_trust, + threshold, + } => { assert_eq!(source_trust, TrustState::Untrusted); assert_eq!(threshold, TrustState::ApprovedPeer); } diff --git a/src/workers/continuum-core/src/persona/inbox_admission.rs b/src/workers/continuum-core/src/persona/inbox_admission.rs index fd6829187..7271684b0 100644 --- a/src/workers/continuum-core/src/persona/inbox_admission.rs +++ b/src/workers/continuum-core/src/persona/inbox_admission.rs @@ -67,7 +67,9 @@ use super::admission::{ AdmissionCandidate, AdmissionConfig, AdmissionContext, AdmissionGate, IsMemorable, SeenContentLookup, SeenEventLookup, }; -use super::engram::{AdmissionDecision, AdmissionError, ChatMessageRef, EngramKind, EngramOrigin, TrustState}; +use super::engram::{ + AdmissionDecision, AdmissionError, ChatMessageRef, EngramKind, EngramOrigin, TrustState, +}; use super::trace::CognitionTrace; use super::types::{InboxMessage, SenderType}; @@ -352,9 +354,16 @@ mod tests { let hash = content_hash_sha256("hello, world"); assert!(hash.starts_with("sha256:"), "got: {hash}"); let hex = &hash["sha256:".len()..]; - assert_eq!(hex.len(), 64, "hex must be 64 chars (32-byte SHA-256): {hex}"); - assert!(hex.chars().all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()), - "hex must be lowercase: {hex}"); + assert_eq!( + hex.len(), + 64, + "hex must be 64 chars (32-byte SHA-256): {hex}" + ); + assert!( + hex.chars() + .all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase()), + "hex must be lowercase: {hex}" + ); } /// What this catches: the same input always produces the same hash. @@ -442,8 +451,10 @@ mod tests { assert_eq!(cand.recall_keys, vec!["test-sender".to_string()]); // Content hash on candidate matches the origin's if let EngramOrigin::Chat(ref r) = cand.origin { - assert_eq!(r.content_hash, cand.content_hash, - "candidate.content_hash must equal origin.content_hash"); + assert_eq!( + r.content_hash, cand.content_hash, + "candidate.content_hash must equal origin.content_hash" + ); } else { panic!("expected Chat origin"); } @@ -511,8 +522,13 @@ mod tests { let mut trace = CognitionTrace::new(); let msg = synthetic_message("short", SenderType::Human); - match runner.admit(&msg, &content, &events, Some(&mut trace)).unwrap() { - AdmissionDecision::Drop { reason: AdmissionDropReason::NotMemorable { .. } } => {} + match runner + .admit(&msg, &content, &events, Some(&mut trace)) + .unwrap() + { + AdmissionDecision::Drop { + reason: AdmissionDropReason::NotMemorable { .. }, + } => {} other => panic!("expected Drop NotMemorable, got {other:?}"), } } @@ -532,8 +548,13 @@ mod tests { let mut trace = CognitionTrace::new(); let msg = synthetic_message(content_text, SenderType::Human); - match runner.admit(&msg, &content, &events, Some(&mut trace)).unwrap() { - AdmissionDecision::Drop { reason: AdmissionDropReason::Duplicate { existing_engram_id } } => { + match runner + .admit(&msg, &content, &events, Some(&mut trace)) + .unwrap() + { + AdmissionDecision::Drop { + reason: AdmissionDropReason::Duplicate { existing_engram_id }, + } => { assert_eq!(existing_engram_id, existing); } other => panic!("expected Drop Duplicate, got {other:?}"), @@ -576,7 +597,10 @@ mod tests { ); match runner.admit(&msg, &content, &events, Some(&mut trace)) { - Err(AdmissionError::TrustBoundaryRejected { source_trust, threshold }) => { + Err(AdmissionError::TrustBoundaryRejected { + source_trust, + threshold, + }) => { assert_eq!(source_trust, TrustState::Authenticated); assert_eq!(threshold, TrustState::IntragridMember); } @@ -630,7 +654,9 @@ mod tests { // via the custom recipe — proves the custom recipe is the one being // consulted. let msg = synthetic_message("short", SenderType::Human); - let decision = runner.admit(&msg, &content, &events, Some(&mut trace)).unwrap(); + let decision = runner + .admit(&msg, &content, &events, Some(&mut trace)) + .unwrap(); assert!(matches!(decision, AdmissionDecision::Admit { .. })); } diff --git a/src/workers/continuum-core/src/persona/mod.rs b/src/workers/continuum-core/src/persona/mod.rs index 2022f86ac..67c46e468 100644 --- a/src/workers/continuum-core/src/persona/mod.rs +++ b/src/workers/continuum-core/src/persona/mod.rs @@ -20,6 +20,7 @@ pub mod channel_queue; pub mod channel_registry; pub mod channel_types; pub mod cognition; +pub mod cognition_io; pub mod domain_classifier; pub mod engram; pub mod evaluator; @@ -30,14 +31,13 @@ pub mod media_policy; pub mod message_cache; pub mod model_selection; pub mod prompt_assembly; -pub mod cognition_io; pub mod recorder; -pub mod trace; pub mod resource_forecast; pub mod response; pub mod self_task_generator; pub mod service_module; pub mod text_analysis; +pub mod trace; pub mod turn_context; pub mod turn_frame; pub mod types; @@ -62,8 +62,8 @@ pub use channel_types::{ActivityDomain, ChannelRegistryStatus, ChannelStatus, Se pub use cognition::{CognitionDecision, PersonaCognitionEngine, PriorityFactors, PriorityScore}; pub use domain_classifier::{DomainClassification, DomainClassifier, QualityFactors, QualityScore}; pub use engram::{ - AdmissionDecision, AdmissionDropReason, AdmissionError, AircMessageRef, ChatMessageRef, - Engram, EngramKind, EngramOrigin, ToolInvocationRef, TrustState, + AdmissionDecision, AdmissionDropReason, AdmissionError, AircMessageRef, ChatMessageRef, Engram, + EngramKind, EngramOrigin, ToolInvocationRef, TrustState, }; pub use evaluator::{ AdequacyResult, FullEvaluateRequest, FullEvaluateResult, GateDetails, RateLimiterState, @@ -75,8 +75,8 @@ pub use genome_paging::{ }; pub use inbox::{PersonaInbox, PersonaInboxFrame, PersonaInboxFrameMetrics}; pub use inbox_admission::{ - content_hash_sha256, inbox_message_to_candidate, inbox_message_to_origin, - InboxAdmissionRunner, TrustMapping, + content_hash_sha256, inbox_message_to_candidate, inbox_message_to_origin, InboxAdmissionRunner, + TrustMapping, }; pub use message_cache::{ CachedMessage, ContentDedupResult, ContentDeduplicator, EchoChamberResult, RecentMessageCache, diff --git a/src/workers/continuum-core/src/persona/prompt_assembly.rs b/src/workers/continuum-core/src/persona/prompt_assembly.rs index aa36da2ba..ecae0a703 100644 --- a/src/workers/continuum-core/src/persona/prompt_assembly.rs +++ b/src/workers/continuum-core/src/persona/prompt_assembly.rs @@ -103,8 +103,7 @@ pub fn assemble(input: &PromptAssemblyInput) -> AssembledPrompt { // input + a generous overhead estimate for the optional blocks. // Avoids the realloc that would otherwise fire on the first // `push_str` of an angle/social/voice block (#1209). - let mut system_prompt = - String::with_capacity(input.system_prompt.len() + 512); + let mut system_prompt = String::with_capacity(input.system_prompt.len() + 512); system_prompt.push_str(&input.system_prompt); // Inject shared analysis angle if present — grounds the persona's @@ -180,12 +179,14 @@ pub fn assemble(input: &PromptAssemblyInput) -> AssembledPrompt { &input.current_message, &input.persona_name, ), - MultiPartyChatStrategy::ProperChatMlSingleParty => build_messages_proper_chatml_single_party( - &input.history, - &input.current_message, - &input.persona_name, - &input.other_persona_names, - ), + MultiPartyChatStrategy::ProperChatMlSingleParty => { + build_messages_proper_chatml_single_party( + &input.history, + &input.current_message, + &input.persona_name, + &input.other_persona_names, + ) + } }; // Estimate tokens (~4 chars per token) @@ -285,8 +286,8 @@ fn build_messages_single_user_turn( .iter() .map(|m| m.name.as_ref().map_or(0, |n| n.len() + 2) + m.content.len() + 1) .sum(); - let current_capacity = current.name.as_ref().map_or(20, |n| n.len() + 22) - + current.content.len(); + let current_capacity = + current.name.as_ref().map_or(20, |n| n.len() + 22) + current.content.len(); let closing_cue_capacity = persona_name.len() + 128; let mut transcript = String::with_capacity( header_overhead + history_capacity + current_capacity + closing_cue_capacity, @@ -470,10 +471,18 @@ fn append_social_block(buf: &mut String, signals: &SocialSignals) { buf.push_str("\n- This message is directed at another persona (not you)"); } if let Some(secs) = signals.seconds_since_last_response { - let _ = write!(buf, "\n- You last responded {}s ago in this room", secs.round() as i64); + let _ = write!( + buf, + "\n- You last responded {}s ago in this room", + secs.round() as i64 + ); } if let (Some(count), Some(cap)) = (signals.response_count_this_session, signals.response_cap) { - let _ = write!(buf, "\n- You have responded {}/{} times this session", count, cap); + let _ = write!( + buf, + "\n- You have responded {}/{} times this session", + count, cap + ); } } @@ -550,12 +559,16 @@ mod tests { result.system_message ); assert!( - result.system_message.contains("- Joel's favorite color is teal."), + result + .system_message + .contains("- Joel's favorite color is teal."), "expected bullet-prefixed engram in: {}", result.system_message ); assert!( - result.system_message.contains("- Joel works in San Francisco."), + result + .system_message + .contains("- Joel works in San Francisco."), "expected second bullet in: {}", result.system_message ); @@ -821,10 +834,7 @@ mod tests { timestamp_ms: None, }; - let other_personas = vec![ - "Helper AI".to_string(), - "CodeReview AI".to_string(), - ]; + let other_personas = vec!["Helper AI".to_string(), "CodeReview AI".to_string()]; let messages = build_messages_proper_chatml_single_party( &history, ¤t, @@ -899,12 +909,8 @@ mod tests { timestamp_ms: None, }; - let messages = build_messages_proper_chatml_single_party( - &history, - ¤t, - "Local Assistant", - &[], - ); + let messages = + build_messages_proper_chatml_single_party(&history, ¤t, "Local Assistant", &[]); assert_eq!(messages.len(), 2); assert_eq!(messages[0].role, "user"); @@ -924,12 +930,8 @@ mod tests { timestamp_ms: None, }; - let messages = build_messages_proper_chatml_single_party( - &[], - ¤t, - "Local Assistant", - &[], - ); + let messages = + build_messages_proper_chatml_single_party(&[], ¤t, "Local Assistant", &[]); assert_eq!(messages.len(), 1); assert_eq!(messages[0].role, "user"); diff --git a/src/workers/continuum-core/src/persona/service_module.rs b/src/workers/continuum-core/src/persona/service_module.rs index 458be20ec..d86256967 100644 --- a/src/workers/continuum-core/src/persona/service_module.rs +++ b/src/workers/continuum-core/src/persona/service_module.rs @@ -151,7 +151,9 @@ impl ResponderConfig { /// not inside the inference layer. pub fn validate(&self) -> Result<(), String> { if self.model.trim().is_empty() { - return Err("ResponderConfig.model is empty (persona must declare its model)".to_string()); + return Err( + "ResponderConfig.model is empty (persona must declare its model)".to_string(), + ); } if self.specialty.trim().is_empty() { return Err( @@ -404,9 +406,8 @@ impl PersonaServiceModule { if item_type != "chat" { return Ok(ServicePopDecision::UnsupportedItem { item_type }); } - let wire: ChatItemWire = serde_json::from_value(item_value).map_err(|e| { - format!("service_once_for: failed to deserialize chat item: {e}") - })?; + let wire: ChatItemWire = serde_json::from_value(item_value) + .map_err(|e| format!("service_once_for: failed to deserialize chat item: {e}"))?; let sender_is_human = matches!(wire.sender_type, SenderType::Human); let request = FullEvaluateRequest { persona_id: persona.persona_id, @@ -559,9 +560,7 @@ impl PersonaServiceModule { })?; drained += 1; } - Ok(ServicePopDecision::NeedsResponse { - respond_input, .. - }) => { + Ok(ServicePopDecision::NeedsResponse { respond_input, .. }) => { // Lock is dropped here. respond() runs free. let respond_result = self.responder.respond(*respond_input).await; match respond_result { @@ -667,11 +666,7 @@ impl ServiceModule for PersonaServiceModule { Ok(()) } - async fn handle_command( - &self, - command: &str, - params: Value, - ) -> Result { + async fn handle_command(&self, command: &str, params: Value) -> Result { match command { "persona/status" => { let snapshot = self.enrolled_snapshot()?; @@ -838,8 +833,10 @@ mod tests { async fn enroll_is_idempotent_and_updates_display_name() { let m = fresh_module(); let persona_id = Uuid::new_v4(); - m.enroll(persona_id, "First", test_config()).expect("first enroll"); - m.enroll(persona_id, "Second", test_config()).expect("second enroll"); + m.enroll(persona_id, "First", test_config()) + .expect("first enroll"); + m.enroll(persona_id, "Second", test_config()) + .expect("second enroll"); assert_eq!(m.enrolled_count().unwrap(), 1); let snapshot = m.enrolled_snapshot().unwrap(); assert_eq!(snapshot.len(), 1); @@ -863,7 +860,10 @@ mod tests { .handle_command("persona/enroll", json!({"display_name": "Helper"})) .await .expect_err("enroll without persona_id must fail"); - assert!(err.contains("persona_id"), "error names the missing param: {err}"); + assert!( + err.contains("persona_id"), + "error names the missing param: {err}" + ); } #[tokio::test] @@ -918,7 +918,8 @@ mod tests { async fn tick_with_enrolled_persona_and_no_items_is_no_op() { let m = fresh_module(); let persona_id = Uuid::new_v4(); - m.enroll(persona_id, "Helper", test_config()).expect("enroll"); + m.enroll(persona_id, "Helper", test_config()) + .expect("enroll"); // No items in any channel — tick should drain nothing, errors zero. m.tick().await.expect("tick succeeds with idle persona"); assert_eq!(m.enrolled_count().unwrap(), 1); @@ -973,7 +974,8 @@ mod tests { async fn service_once_for_idle_returns_idle() { let m = fresh_module(); let persona_id = Uuid::new_v4(); - m.enroll(persona_id, "Helper", test_config()).expect("enroll"); + m.enroll(persona_id, "Helper", test_config()) + .expect("enroll"); let mut personas = m.personas.lock().unwrap(); let persona = personas.get_mut(&persona_id).unwrap(); ensure_chat_channel(persona); @@ -986,7 +988,8 @@ mod tests { async fn service_once_for_dispatches_chat_item_through_full_evaluate() { let m = fresh_module(); let persona_id = Uuid::new_v4(); - m.enroll(persona_id, "Helper", test_config()).expect("enroll"); + m.enroll(persona_id, "Helper", test_config()) + .expect("enroll"); let room_id = Uuid::new_v4(); let mut personas = m.personas.lock().unwrap(); let persona = personas.get_mut(&persona_id).unwrap(); @@ -997,8 +1000,8 @@ mod tests { .channels .route(Box::new(item)) .expect("route chat item to Chat channel"); - let outcome = - PersonaServiceModule::service_once_for(persona, 1_700_000_000_000).expect("dispatch ok"); + let outcome = PersonaServiceModule::service_once_for(persona, 1_700_000_000_000) + .expect("dispatch ok"); // Sender is human + persona is not in DND + no rate limit → gate // says respond → NeedsResponse with a fully-formed RespondInput. match outcome { @@ -1068,7 +1071,10 @@ mod tests { ) .await .expect_err("enroll command must require model"); - assert!(err.contains("model"), "error names the missing param: {err}"); + assert!( + err.contains("model"), + "error names the missing param: {err}" + ); } #[tokio::test] @@ -1089,7 +1095,9 @@ mod tests { .expect("route"); } } - m.drain_all_personas(1_700_000_000_000).await.expect("drain ok"); + m.drain_all_personas(1_700_000_000_000) + .await + .expect("drain ok"); // Both personas should be healthy: zero consecutive failures, // closed circuit. let personas = m.personas.lock().unwrap(); @@ -1106,7 +1114,8 @@ mod tests { // processed; the remainder stays queued. let m = fresh_module(); let persona_id = Uuid::new_v4(); - m.enroll(persona_id, "Helper", test_config()).expect("enroll"); + m.enroll(persona_id, "Helper", test_config()) + .expect("enroll"); let room_id = Uuid::new_v4(); let staged = MAX_DRAIN_PER_TICK as usize + 5; { @@ -1119,13 +1128,12 @@ mod tests { let mut item = test_chat_item(&format!("msg {i}"), true, room_id); // Vary timestamps so consolidation orders deterministically. item.timestamp = 1_700_000_000_000 + i as u64; - persona - .channels - .route(Box::new(item)) - .expect("route item"); + persona.channels.route(Box::new(item)).expect("route item"); } } - m.drain_all_personas(1_700_000_000_000).await.expect("drain ok"); + m.drain_all_personas(1_700_000_000_000) + .await + .expect("drain ok"); // After one drain pass, the queue should NOT be empty (we // staged more than the per-tick cap and ChatQueueItem // consolidates same-room items, so the actual count drained @@ -1180,7 +1188,9 @@ mod tests { } } - fn module_with_responder(script: ResponderScript) -> (PersonaServiceModule, Arc) { + fn module_with_responder( + script: ResponderScript, + ) -> (PersonaServiceModule, Arc) { let mock = Arc::new(MockResponder { call_count: AtomicU32::new(0), scripted: script, @@ -1194,8 +1204,7 @@ mod tests { #[tokio::test] async fn drain_calls_responder_when_gate_says_yes() { - let (m, mock) = - module_with_responder(ResponderScript::AlwaysSpoke("howdy".to_string())); + let (m, mock) = module_with_responder(ResponderScript::AlwaysSpoke("howdy".to_string())); let persona_id = Uuid::new_v4(); m.enroll(persona_id, "Helper", test_config()) .expect("enroll"); @@ -1230,8 +1239,7 @@ mod tests { // ai-sender + no @mention → response_cap / sender filter typically // gates it silent. Either way, if SilentByDecision fires, the // responder must NOT be invoked. - let (m, mock) = - module_with_responder(ResponderScript::AlwaysSpoke("never".to_string())); + let (m, mock) = module_with_responder(ResponderScript::AlwaysSpoke("never".to_string())); let persona_id = Uuid::new_v4(); m.enroll(persona_id, "Helper", test_config()) .expect("enroll"); @@ -1267,9 +1275,8 @@ mod tests { // at MAX_DRAIN_PER_TICK (20) per tick AND breaks on inference // error. So each tick we hit exactly ONE inference error before // breaking. We drive 15 ticks. - let (m, mock) = module_with_responder(ResponderScript::AlwaysErr( - "model not loaded".to_string(), - )); + let (m, mock) = + module_with_responder(ResponderScript::AlwaysErr("model not loaded".to_string())); let persona_id = Uuid::new_v4(); m.enroll(persona_id, "Helper", test_config()) .expect("enroll"); @@ -1296,8 +1303,7 @@ mod tests { let personas = m.personas.lock().unwrap(); let p = personas.get(&persona_id).unwrap(); assert_eq!( - p.consecutive_inference_failures, - CIRCUIT_BREAKER_MAX_CONSECUTIVE_INFERENCE_FAILURES, + p.consecutive_inference_failures, CIRCUIT_BREAKER_MAX_CONSECUTIVE_INFERENCE_FAILURES, "inference failure counter should equal the threshold" ); assert_ne!( @@ -1309,9 +1315,8 @@ mod tests { #[tokio::test] async fn inference_failure_below_threshold_does_not_trip_circuit() { // 1 inference error → counter at 1, circuit still closed. - let (m, _mock) = module_with_responder(ResponderScript::AlwaysErr( - "transient hiccup".to_string(), - )); + let (m, _mock) = + module_with_responder(ResponderScript::AlwaysErr("transient hiccup".to_string())); let persona_id = Uuid::new_v4(); m.enroll(persona_id, "Helper", test_config()) .expect("enroll"); diff --git a/src/workers/continuum-core/src/runtime/artifact_handle.rs b/src/workers/continuum-core/src/runtime/artifact_handle.rs index 71a1c411f..adc5c4459 100644 --- a/src/workers/continuum-core/src/runtime/artifact_handle.rs +++ b/src/workers/continuum-core/src/runtime/artifact_handle.rs @@ -62,10 +62,7 @@ use ts_rs::TS; /// humans reading subscription lists, not the dispatcher. #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(transparent)] -#[ts( - export, - export_to = "../../../shared/generated/runtime/ArtifactKey.ts" -)] +#[ts(export, export_to = "../../../shared/generated/runtime/ArtifactKey.ts")] pub struct ArtifactKey(pub String); impl ArtifactKey { @@ -153,10 +150,7 @@ impl ArtifactSelector { /// truly never wakes shouldn't exist as a registered module. #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase", tag = "kind")] -#[ts( - export, - export_to = "../../../shared/generated/runtime/Cadence.ts" -)] +#[ts(export, export_to = "../../../shared/generated/runtime/Cadence.ts")] pub enum Cadence { Periodic { /// Requested floor on tick interval. ms over the wire so the diff --git a/src/workers/continuum-core/src/runtime/brain_region.rs b/src/workers/continuum-core/src/runtime/brain_region.rs new file mode 100644 index 000000000..ddcf7586d --- /dev/null +++ b/src/workers/continuum-core/src/runtime/brain_region.rs @@ -0,0 +1,476 @@ +//! BrainRegion — the cognitive-cycle trait every brain region implements. +//! +//! Companion to ServiceModule. Where ServiceModule handles command/event +//! routing (the existing dispatch surface), BrainRegion handles the +//! cognitive tick: continuous parallel computation, yield telemetry, +//! pressure registration, ready-buffer publishing. +//! +//! A real region (hippocampus, motor cortex, attention, sensory, sleep +//! policy) implements BOTH ServiceModule (for cmd/event surface) and +//! BrainRegion (for cognitive cycle). The runtime continues to dispatch +//! via ServiceModule. The substrate governor (lands L0-4c) dispatches +//! the cognitive tick via BrainRegion. +//! +//! Doctrine (from docs/architecture/BRAIN-REGIONS-SUBSTRATE.md): +//! +//! > No region of cognition runs on the hot path. Each region is its +//! > own RTOS task with its own tick. The handler dispatches and reads +//! > pre-staged results. The handler never blocks on recall, embedding, +//! > planning, or admission — those are continuously produced by their +//! > owning regions, in parallel, governed by SubstrateGovernor. +//! +//! ## L0-3a.0 scope (this slice) +//! +//! Pure typed surface. No region implementations. No governor +//! integration. No derive macro, no scaffold generator (those land +//! when ≥3 regions exist to motivate the abstraction — per the +//! outlier-validation strategy in CLAUDE.md). +//! +//! Later slices ship: L0-3a.1 HippocampusModule skeleton, L0-3a.2+ +//! per-algorithm bodies, L0-4a motor cortex, L0-4b attention, L0-4c +//! governor yield-learning integration. + +use crate::governor::types::PressureSignal; +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use std::borrow::Cow; +use ts_rs::TS; +use uuid::Uuid; + +// ─── Region identity ──────────────────────────────────────────────── + +/// Stable identifier for a brain region. Used by SubstrateGovernor for +/// policy lookup and by telemetry/log streams for tagging events. +/// +/// Carries `Cow<'static, str>` so static IDs ("hippocampus") cost +/// nothing and dynamic IDs (custom regions registered at runtime) are +/// still supported. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] +#[ts(export, export_to = "../../../shared/generated/runtime/RegionId.ts")] +pub struct RegionId(pub Cow<'static, str>); + +impl RegionId { + pub const fn from_static(id: &'static str) -> Self { + Self(Cow::Borrowed(id)) + } + + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl From<&'static str> for RegionId { + fn from(s: &'static str) -> Self { + Self::from_static(s) + } +} + +impl From for RegionId { + fn from(s: String) -> Self { + Self(Cow::Owned(s)) + } +} + +impl std::fmt::Display for RegionId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +// ─── Pressure profile ─────────────────────────────────────────────── + +/// Memory footprint class. Drives governor decisions about which +/// regions to throttle first under memory pressure. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts(export, export_to = "../../../shared/generated/runtime/MemoryClass.ts")] +pub enum MemoryClass { + /// Lightweight — small in-memory structures, no large caches. + Light, + /// Moderate — recall caches, salience maps, telemetry windows. + Moderate, + /// Heavy — engram graph, working memory ring, multiple ready-buffers. + Heavy, + /// VRAM-sensitive — touches GPU residency (genome region, inference-adjacent). + VramSensitive, +} + +/// Compute footprint class. Drives governor decisions about which +/// regions to throttle first under compute/thermal pressure. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts( + export, + export_to = "../../../shared/generated/runtime/ComputeClass.ts" +)] +pub enum ComputeClass { + /// Tick body is bookkeeping only — cheap. + Bookkeeping, + /// Tick body does scoring / graph traversal — CPU-bound but bounded. + Cpu, + /// Tick body invokes embedding / similarity / vectorized work. + CpuVectorized, + /// Tick body invokes inference (sub-token generation or scoring). + InferenceLight, + /// Tick body could invoke full inference. The governor MUST budget this carefully. + InferenceHeavy, +} + +/// Which kinds of pressure signals a region wants to receive via +/// `on_signal`. The governor filters and routes signals based on this. +/// +/// Mirrors the variants of [`PressureSignal`] but is a kind-only enum +/// (no payload) so it can be declared statically by a region at +/// registration time. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts( + export, + export_to = "../../../shared/generated/runtime/PressureSignalKind.ts" +)] +pub enum PressureSignalKind { + Thermal, + BatteryLow, + SystemMemHigh, + VramHigh, + UserActive, + InferenceQueueDepth, + SpeculationMissRate, +} + +/// What a region declares about its resource footprint at registration +/// time. The governor reads this once at register, then re-queries it +/// when pressure shifts (regions may report different profiles after +/// adapting under load — e.g., hippocampus drops from `Heavy` to +/// `Moderate` when working memory is pruned). +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts( + export, + export_to = "../../../shared/generated/runtime/PressureProfile.ts" +)] +pub struct PressureProfile { + pub memory_class: MemoryClass, + pub compute_class: ComputeClass, + /// Pressure kinds this region wants `on_signal` calls for. Other + /// kinds are filtered out by the governor. + pub responds_to: Vec, +} + +// ─── Tick outcome (yield telemetry) ───────────────────────────────── + +/// A hint a region can pass back to the governor about preferred next +/// tick cadence. The governor may honor or override; it owns the +/// final policy. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts(export, export_to = "../../../shared/generated/runtime/CadenceHint.ts")] +pub enum CadenceHint { + /// Tick faster than current cadence (region has urgent work). + Faster, + /// Hold current cadence. + Hold, + /// Tick slower than current cadence (region is idle / over-tasked relative to consumed yield). + Slower, + /// Sleep — region has nothing useful to do until a signal fires. + Sleep, +} + +/// Yield telemetry returned by every region tick. Feeds the substrate +/// governor's yield-learning loop (algorithm 7 in +/// COGNITION-ALGORITHMS.md, lands in L0-4c). +/// +/// Regions emit this from every tick. The governor reads aggregate +/// (`consumed_since_last` vs `published`) to upweight regions whose +/// output is being consumed by handlers and downweight regions whose +/// output is ignored. +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts(export, export_to = "../../../shared/generated/runtime/TickOutcome.ts")] +pub struct TickOutcome { + /// Items the region pre-staged this tick (publishes to ready-buffers). + #[ts(type = "number")] + pub published: usize, + + /// Items in the region's ready-buffer that have been consumed by + /// handlers since the last tick. The denominator for yield. + #[ts(type = "number")] + pub consumed_since_last: usize, + + /// Pressure observation. If the region detected backpressure (DB + /// slow, embedding queue full, etc.), reports it here for the + /// governor. + #[ts(optional)] + pub pressure_observed: Option, + + /// Optional next-tick hint (region requests faster/slower cadence). + #[ts(optional)] + pub cadence_hint: Option, +} + +impl TickOutcome { + /// Idle outcome — region had no work this tick. Convenience for + /// no-op ticks and tests. + pub fn idle() -> Self { + Self { + published: 0, + consumed_since_last: 0, + pressure_observed: None, + cadence_hint: None, + } + } +} + +// ─── Region signals ───────────────────────────────────────────────── + +/// Persona lifecycle events relevant to regions (allow regions to +/// allocate / deallocate per-persona state). +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case", tag = "kind")] +#[ts( + export, + export_to = "../../../shared/generated/runtime/PersonaLifecycle.ts" +)] +pub enum PersonaLifecycle { + Created { + #[ts(type = "string")] + persona_id: Uuid, + }, + Destroyed { + #[ts(type = "string")] + persona_id: Uuid, + }, +} + +/// Sleep/wake phases for the persona-level cognitive cycle. The sleep +/// policy region (L0-4d) emits these; other regions react by changing +/// their tick body (active vs idle vs sleep consolidation). +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts(export, export_to = "../../../shared/generated/runtime/SleepPhase.ts")] +pub enum SleepPhase { + /// Persona is actively servicing — tick at high cadence, shallow consolidation. + Active, + /// Persona is idle but recently active — tick at moderate cadence, normal consolidation. + Idle, + /// Persona is in deep idle — tick at low cadence, deep consolidation + pruning. + Sleep, +} + +/// Coarse system pressure level surfaced to regions so they can adjust +/// internally without parsing every PressureSignal variant. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case")] +#[ts( + export, + export_to = "../../../shared/generated/runtime/PressureLevel.ts" +)] +pub enum PressureLevel { + Nominal, + Moderate, + High, + Critical, +} + +/// Signals the substrate sends to regions out-of-band (not on the +/// regular tick). Regions that don't care about a signal default to a +/// no-op. +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[serde(rename_all = "kebab-case", tag = "kind")] +#[ts( + export, + export_to = "../../../shared/generated/runtime/RegionSignal.ts" +)] +pub enum RegionSignal { + PersonaLifecycle(PersonaLifecycle), + SleepTransition { + #[ts(type = "string")] + persona_id: Uuid, + phase: SleepPhase, + }, + SystemPressureChanged { + level: PressureLevel, + }, +} + +// ─── Region context ───────────────────────────────────────────────── + +/// What the substrate passes to a region's `tick` body. Carries the +/// substrate handles a region needs to do its work without reaching +/// for globals. +/// +/// L0-3a.0 ships the type; L0-3a.1+ adds real handles (ModuleContext +/// reference, governor handle, persona state map, etc.). For now it's +/// a placeholder so the trait signature compiles. +#[derive(Debug, Clone)] +pub struct RegionContext { + /// Tick number since region started. Useful for cadence-modulated + /// logic ("every 10th tick, do deeper work"). + pub tick_number: u64, + /// Optional persona scope — if the substrate is ticking the region + /// for one specific persona's slot, this is set. If `None`, the + /// region is ticking globally (background work). + pub persona_scope: Option, +} + +impl RegionContext { + pub fn global(tick_number: u64) -> Self { + Self { + tick_number, + persona_scope: None, + } + } + + pub fn for_persona(tick_number: u64, persona_id: Uuid) -> Self { + Self { + tick_number, + persona_scope: Some(persona_id), + } + } +} + +// ─── Region errors ────────────────────────────────────────────────── + +/// Errors a region can surface from `on_signal`. Tick failures use +/// `TickOutcome.pressure_observed` to signal degradation; signal +/// failures are explicit because the substrate may need to retry. +#[derive(Debug, thiserror::Error)] +pub enum RegionError { + #[error("region {0} rejected signal: {1}")] + SignalRejected(RegionId, String), + #[error("region {0} not ready: {1}")] + NotReady(RegionId, String), + #[error("region {0} internal error: {1}")] + Internal(RegionId, String), +} + +// ─── The trait ────────────────────────────────────────────────────── + +/// A cognitive subsystem (hippocampus, motor cortex, attention, +/// sensory, sleep policy). Each region runs its own tick on its own +/// tokio task, governed by SubstrateGovernor. +/// +/// A region typically also implements [`ServiceModule`](super::ServiceModule) +/// for command/event routing, but doesn't have to — pure cognitive +/// regions with no external command surface are valid. +/// +/// See `docs/architecture/BRAIN-REGIONS-SUBSTRATE.md` for the full +/// contract and `docs/architecture/COGNITION-ALGORITHMS.md` for what +/// runs inside the tick. +#[async_trait] +pub trait BrainRegion: Send + Sync + 'static { + /// Stable identifier. Used by SubstrateGovernor for policy lookup + /// and by telemetry/log streams for event tagging. + fn id(&self) -> RegionId; + + /// Pressure footprint declaration. Returned at registration time + /// and re-queried by the governor when pressure shifts. + fn pressure_profile(&self) -> PressureProfile; + + /// Run one tick. The substrate calls this on the region's own task + /// at the cadence governed by SubstrateGovernor. + /// + /// The body is responsible for: reading inputs (from shared state, + /// channels, or its own DB), producing pre-staged results, and + /// publishing them to the ready-buffer. + /// + /// Implementations MUST be idempotent on early return and MUST NOT + /// block indefinitely — the governor cancels long-running ticks + /// under pressure. + async fn tick(&self, ctx: &RegionContext) -> TickOutcome; + + /// React to a substrate-level signal. Defaults to a no-op so + /// regions that don't care about any signals can ignore the + /// surface entirely. + async fn on_signal(&self, _signal: RegionSignal) -> Result<(), RegionError> { + Ok(()) + } +} + +// ─── Tests ────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + /// A minimal region for trait validation. Verifies the trait is + /// object-safe, the default `on_signal` works, and an idle tick + /// outcome round-trips through the type system. + struct TestRegion { + id: RegionId, + } + + #[async_trait] + impl BrainRegion for TestRegion { + fn id(&self) -> RegionId { + self.id.clone() + } + + fn pressure_profile(&self) -> PressureProfile { + PressureProfile { + memory_class: MemoryClass::Light, + compute_class: ComputeClass::Bookkeeping, + responds_to: vec![], + } + } + + async fn tick(&self, _ctx: &RegionContext) -> TickOutcome { + TickOutcome::idle() + } + } + + #[tokio::test] + async fn test_region_implements_trait() { + let region: Box = Box::new(TestRegion { + id: RegionId::from_static("test"), + }); + let ctx = RegionContext::global(0); + let outcome = region.tick(&ctx).await; + assert_eq!(outcome.published, 0); + assert_eq!(outcome.consumed_since_last, 0); + assert!(outcome.pressure_observed.is_none()); + assert!(outcome.cadence_hint.is_none()); + } + + #[tokio::test] + async fn test_default_on_signal_is_noop() { + let region = TestRegion { + id: RegionId::from_static("test"), + }; + let signal = RegionSignal::SystemPressureChanged { + level: PressureLevel::Nominal, + }; + assert!(region.on_signal(signal).await.is_ok()); + } + + #[test] + fn test_region_id_static_construction() { + const ID: RegionId = RegionId::from_static("hippocampus"); + assert_eq!(ID.as_str(), "hippocampus"); + } + + #[test] + fn test_region_id_display() { + let id = RegionId::from_static("motor_cortex"); + assert_eq!(format!("{id}"), "motor_cortex"); + } + + #[test] + fn test_region_context_global_and_per_persona() { + let global = RegionContext::global(5); + assert_eq!(global.tick_number, 5); + assert!(global.persona_scope.is_none()); + + let persona_id = Uuid::new_v4(); + let scoped = RegionContext::for_persona(7, persona_id); + assert_eq!(scoped.tick_number, 7); + assert_eq!(scoped.persona_scope, Some(persona_id)); + } + + #[test] + fn test_tick_outcome_idle_constructor() { + let outcome = TickOutcome::idle(); + assert_eq!(outcome.published, 0); + assert_eq!(outcome.consumed_since_last, 0); + assert!(outcome.pressure_observed.is_none()); + assert!(outcome.cadence_hint.is_none()); + } +} diff --git a/src/workers/continuum-core/src/runtime/mod.rs b/src/workers/continuum-core/src/runtime/mod.rs index b3c07e4d3..a188226c6 100644 --- a/src/workers/continuum-core/src/runtime/mod.rs +++ b/src/workers/continuum-core/src/runtime/mod.rs @@ -25,12 +25,15 @@ use std::sync::Arc; use std::sync::OnceLock; pub mod artifact_handle; +pub mod brain_region; pub mod command_executor; pub mod control; pub mod message_bus; pub mod module_context; pub mod module_logger; pub mod module_metrics; +pub mod ready_buffer; +pub mod region_telemetry; pub mod registry; #[allow(clippy::module_inception)] pub mod runtime; @@ -38,6 +41,11 @@ pub mod service_module; pub mod shared_compute; pub use artifact_handle::{ArtifactKey, ArtifactSelector, Cadence}; +pub use brain_region::{ + BrainRegion, CadenceHint, ComputeClass, MemoryClass, PersonaLifecycle, PressureLevel, + PressureProfile, PressureSignalKind, RegionContext, RegionError, RegionId, RegionSignal, + SleepPhase, TickOutcome, +}; pub use command_executor::{ execute as execute_command, execute_json as execute_command_json, executor, init_executor, CommandExecutor, @@ -47,6 +55,8 @@ pub use message_bus::MessageBus; pub use module_context::ModuleContext; pub use module_logger::ModuleLogger; pub use module_metrics::{CommandTiming, ModuleMetrics, ModuleStats}; +pub use ready_buffer::{DashMapReadyBuffer, ReadyBuffer}; +pub use region_telemetry::RegionTelemetry; pub use registry::ModuleRegistry; pub use runtime::Runtime; pub use service_module::{ diff --git a/src/workers/continuum-core/src/runtime/ready_buffer.rs b/src/workers/continuum-core/src/runtime/ready_buffer.rs new file mode 100644 index 000000000..270a8fb6e --- /dev/null +++ b/src/workers/continuum-core/src/runtime/ready_buffer.rs @@ -0,0 +1,278 @@ +//! ReadyBuffer — the publish/peek surface that every brain region +//! uses to hand off pre-staged results to handlers without blocking. +//! +//! Doctrine (from docs/architecture/BRAIN-REGIONS-SUBSTRATE.md): +//! +//! > Empty buffer is a signal, not a block. If a handler reads and +//! > gets None, it proceeds with whatever degraded path the algorithm +//! > specifies. Slightly-stale context > stalled persona. +//! +//! ## Semantic rules +//! +//! - **Reads MUST NOT block** — handlers call `peek` on the hot path; +//! it MUST complete in microseconds and MUST NOT `await`. The +//! [`DashMapReadyBuffer`] default impl honors this via DashMap's +//! sharded locks. +//! - **Staleness is acceptable** — a ready value might be 100ms old; +//! that's better than blocking the handler 500ms to recompute. +//! - **Per-region buffers, not a global one** — hippocampus owns its +//! engram-prefetch buffer; motor cortex owns its candidate-utterance +//! buffer. They share the same trait shape but live in their own +//! region structs. +//! - **TTL eviction** is region-owned — regions decide what "stale" +//! means for their value type. +//! +//! ## L0-3a.0 scope (this slice) +//! +//! Trait definition + a single default `DashMap`-backed implementation. +//! No region-specific buffers yet (those land with their owning regions +//! in L0-3a.1+, L0-4a, L0-4b, etc.). + +use dashmap::DashMap; +use std::hash::Hash; +use std::sync::Arc; +use std::time::{Duration, Instant}; + +// ─── The trait ────────────────────────────────────────────────────── + +/// Pre-staged result publishing for brain regions. Regions write +/// (`publish`), handlers read (`peek`). The buffer holds the freshest +/// value per key; older values are dropped on overwrite. +pub trait ReadyBuffer: Send + Sync { + /// The key type. Typically `(persona_id, channel_id)` or similar + /// composite identifying what the staged value is for. + type Key: Hash + Eq + Clone; + + /// The value type. Region-specific (engram set, candidate-utterance + /// list, salience snapshot, ...). + type Value: Clone; + + /// Synchronous read. Returns the freshest staged value for the + /// key, or `None`. + /// + /// Handlers call this on the hot path — it MUST NOT block, MUST + /// NOT await, and MUST complete in microseconds. + fn peek(&self, key: &Self::Key) -> Option; + + /// Region-side write. Atomically replaces the value for the key. + /// Older value (if any) is dropped. + fn publish(&self, key: Self::Key, value: Self::Value); + + /// TTL-style eviction sweep. Removes entries whose published-at + /// timestamp is older than `max_age`. Called by the substrate + /// under memory pressure or by the region itself on a sweep tick. + /// + /// Returns the number of entries evicted. + fn evict_stale(&self, max_age: Duration) -> usize; + + /// Current entry count. Used for telemetry and pressure reporting. + fn len(&self) -> usize; + + /// Convenience — most call sites care whether the buffer is empty + /// before deciding to sweep / report pressure. + fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +// ─── Default implementation ───────────────────────────────────────── + +/// Each entry stores its value plus the instant it was published, so +/// `evict_stale` can compute age without walking external state. +#[derive(Clone)] +struct TimestampedEntry { + value: V, + published_at: Instant, +} + +/// DashMap-backed [`ReadyBuffer`]. The default implementation for +/// regions that need a key→value mapping with sharded concurrent +/// access. +/// +/// Reads are sharded by key hash, so peek is wait-free in the common +/// case. Writes acquire the per-shard lock briefly to replace the +/// entry — well within the "microseconds" budget the peek contract +/// asks for. +pub struct DashMapReadyBuffer +where + K: Hash + Eq + Clone + Send + Sync + 'static, + V: Clone + Send + Sync + 'static, +{ + inner: Arc>>, +} + +impl DashMapReadyBuffer +where + K: Hash + Eq + Clone + Send + Sync + 'static, + V: Clone + Send + Sync + 'static, +{ + pub fn new() -> Self { + Self { + inner: Arc::new(DashMap::new()), + } + } + + /// Create with an initial shard capacity hint. Useful when the + /// region knows the working set size up front (e.g., one entry per + /// active persona). + pub fn with_capacity(capacity: usize) -> Self { + Self { + inner: Arc::new(DashMap::with_capacity(capacity)), + } + } +} + +impl Default for DashMapReadyBuffer +where + K: Hash + Eq + Clone + Send + Sync + 'static, + V: Clone + Send + Sync + 'static, +{ + fn default() -> Self { + Self::new() + } +} + +impl Clone for DashMapReadyBuffer +where + K: Hash + Eq + Clone + Send + Sync + 'static, + V: Clone + Send + Sync + 'static, +{ + /// Cheap clone — shares the underlying DashMap via `Arc`. Multiple + /// handles to the same buffer is the expected pattern (region + /// publishes, handlers read). + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } +} + +impl ReadyBuffer for DashMapReadyBuffer +where + K: Hash + Eq + Clone + Send + Sync + 'static, + V: Clone + Send + Sync + 'static, +{ + type Key = K; + type Value = V; + + fn peek(&self, key: &Self::Key) -> Option { + self.inner.get(key).map(|entry| entry.value.clone()) + } + + fn publish(&self, key: Self::Key, value: Self::Value) { + self.inner.insert( + key, + TimestampedEntry { + value, + published_at: Instant::now(), + }, + ); + } + + fn evict_stale(&self, max_age: Duration) -> usize { + let now = Instant::now(); + let stale_keys: Vec = self + .inner + .iter() + .filter(|entry| now.duration_since(entry.value().published_at) > max_age) + .map(|entry| entry.key().clone()) + .collect(); + let evicted = stale_keys.len(); + for key in stale_keys { + self.inner.remove(&key); + } + evicted + } + + fn len(&self) -> usize { + self.inner.len() + } +} + +// ─── Tests ────────────────────────────────────────────────────────── + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_publish_then_peek_returns_value() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + buf.publish(1, "engram-set-1".to_string()); + assert_eq!(buf.peek(&1), Some("engram-set-1".to_string())); + } + + #[test] + fn test_peek_missing_key_returns_none() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + assert_eq!(buf.peek(&42), None); + } + + #[test] + fn test_publish_overwrites_previous_value() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + buf.publish(1, "old".to_string()); + buf.publish(1, "new".to_string()); + assert_eq!(buf.peek(&1), Some("new".to_string())); + } + + #[test] + fn test_evict_stale_removes_old_entries_keeps_fresh() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + buf.publish(1, "old".to_string()); + std::thread::sleep(Duration::from_millis(20)); + buf.publish(2, "fresh".to_string()); + + // Anything older than 10ms is evicted — key 1 goes, key 2 stays. + let evicted = buf.evict_stale(Duration::from_millis(10)); + assert_eq!(evicted, 1); + assert_eq!(buf.peek(&1), None); + assert_eq!(buf.peek(&2), Some("fresh".to_string())); + } + + #[test] + fn test_evict_stale_zero_max_age_clears_everything() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + buf.publish(1, "a".to_string()); + buf.publish(2, "b".to_string()); + let evicted = buf.evict_stale(Duration::ZERO); + assert_eq!(evicted, 2); + assert!(buf.is_empty()); + } + + #[test] + fn test_len_and_is_empty_reflect_state() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::new(); + assert!(buf.is_empty()); + assert_eq!(buf.len(), 0); + buf.publish(1, "x".to_string()); + assert!(!buf.is_empty()); + assert_eq!(buf.len(), 1); + } + + #[test] + fn test_clone_shares_underlying_storage() { + let buf_a: DashMapReadyBuffer = DashMapReadyBuffer::new(); + let buf_b = buf_a.clone(); + buf_a.publish(1, "from-a".to_string()); + // Both handles see the same value — Arc-shared inner DashMap. + assert_eq!(buf_b.peek(&1), Some("from-a".to_string())); + } + + #[test] + fn test_trait_object_usage() { + // Trait is dyn-compatible for handlers that don't care about + // the concrete type. + let buf: Box> = + Box::new(DashMapReadyBuffer::::new()); + buf.publish(1, "via-trait".to_string()); + assert_eq!(buf.peek(&1), Some("via-trait".to_string())); + } + + #[test] + fn test_with_capacity_constructor() { + let buf: DashMapReadyBuffer = DashMapReadyBuffer::with_capacity(64); + buf.publish(1, 100); + assert_eq!(buf.peek(&1), Some(100)); + } +} diff --git a/src/workers/continuum-core/src/runtime/region_telemetry.rs b/src/workers/continuum-core/src/runtime/region_telemetry.rs new file mode 100644 index 000000000..7b36de9a7 --- /dev/null +++ b/src/workers/continuum-core/src/runtime/region_telemetry.rs @@ -0,0 +1,145 @@ +//! RegionTelemetry — the structured event shape every brain region +//! emits per tick. +//! +//! Mandatory for every region. It's the only path the substrate +//! governor's yield-learning loop (algorithm 7) has into the regions +//! and the only operator surface for debugging cognitive cycles. +//! +//! Doctrine (from docs/architecture/BRAIN-REGIONS-SUBSTRATE.md): +//! +//! > Telemetry is mandatory for every region; it's the only way the +//! > yield-learning loop and the operator debugging path work. The +//! > derive macro generates the telemetry emission automatically. +//! +//! The derive macro lands later (once ≥3 regions exist to motivate +//! it); this slice ships the typed struct so regions can emit +//! manually. + +use super::brain_region::RegionId; +use crate::governor::types::PressureSignal; +use serde::{Deserialize, Serialize}; +use std::time::{Duration, SystemTime}; +use ts_rs::TS; +use uuid::Uuid; + +/// Per-tick telemetry shape every brain region emits. +/// +/// Emitted on every tick. The substrate routes it to: +/// +/// - **The governor** — reads `consumed_since_last` / `published` to +/// tune region budget (yield-learning loop, algorithm 7). +/// - **The operator surface** — `./jtag region/stats` / `region/yield` +/// read aggregate telemetry across personas. +/// - **The substrate event stream** — `RegionTickCompleted` and +/// `ReadyBufferUpdated` events for cross-region awareness. +#[derive(Debug, Clone, Serialize, Deserialize, TS)] +#[ts( + export, + export_to = "../../../shared/generated/runtime/RegionTelemetry.ts" +)] +pub struct RegionTelemetry { + /// Which region this came from. Stable string id. + pub region_id: RegionId, + + /// Persona scope. `None` means the tick was global (background + /// work not tied to a specific persona). + #[ts(type = "string | null")] + pub persona_id: Option, + + /// When this tick started (wall clock). + #[ts(type = "string")] + pub tick_started_at: SystemTime, + + /// How long the tick body ran. + #[ts(type = "string")] + pub tick_duration: Duration, + + /// Items the region published to ready-buffers this tick. + #[ts(type = "number")] + pub published: usize, + + /// Items in the region's ready-buffers consumed by handlers since + /// the last tick. + #[ts(type = "number")] + pub consumed_since_last: usize, + + /// Handler `peek` calls that returned `None` since the last tick. + /// Signals to the governor that the region should be upweighted + /// (handlers are asking for stuff that's not staged yet). + #[ts(type = "number")] + pub buffer_misses_since_last: usize, + + /// Pressure the region observed (DB slow, embedding queue full, + /// etc.). Surfaced to the governor for cascade evaluation. + #[ts(optional)] + pub pressure_observed: Option, +} + +impl RegionTelemetry { + /// Compute the consumption fraction. Used by the governor to + /// upweight or downweight a region's budget. Returns `None` when + /// `published` is zero (no signal this tick — preserve prior + /// estimate rather than introducing a zero). + pub fn consumption_fraction(&self) -> Option { + if self.published == 0 { + None + } else { + Some(self.consumed_since_last as f32 / self.published as f32) + } + } + + /// Whether handlers were asking for data the region hadn't staged. + /// A positive value here is the governor's signal to give the + /// region more budget. + pub fn had_buffer_misses(&self) -> bool { + self.buffer_misses_since_last > 0 + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn sample(published: usize, consumed: usize, misses: usize) -> RegionTelemetry { + RegionTelemetry { + region_id: RegionId::from_static("test"), + persona_id: Some(Uuid::nil()), + tick_started_at: SystemTime::UNIX_EPOCH, + tick_duration: Duration::from_millis(1), + published, + consumed_since_last: consumed, + buffer_misses_since_last: misses, + pressure_observed: None, + } + } + + #[test] + fn test_consumption_fraction_with_publishes() { + let t = sample(10, 7, 0); + assert_eq!(t.consumption_fraction(), Some(0.7)); + } + + #[test] + fn test_consumption_fraction_zero_published_returns_none() { + let t = sample(0, 0, 3); + assert_eq!(t.consumption_fraction(), None); + } + + #[test] + fn test_consumption_fraction_full_consumption() { + let t = sample(5, 5, 0); + assert_eq!(t.consumption_fraction(), Some(1.0)); + } + + #[test] + fn test_had_buffer_misses_true_when_positive() { + let t = sample(10, 5, 1); + assert!(t.had_buffer_misses()); + } + + #[test] + fn test_had_buffer_misses_false_when_zero() { + let t = sample(10, 5, 0); + assert!(!t.had_buffer_misses()); + } +} diff --git a/src/workers/continuum-core/src/runtime/runtime.rs b/src/workers/continuum-core/src/runtime/runtime.rs index 775e302a5..3db31b279 100644 --- a/src/workers/continuum-core/src/runtime/runtime.rs +++ b/src/workers/continuum-core/src/runtime/runtime.rs @@ -648,11 +648,7 @@ mod piece_2_pr3_dispatch_tests { .await; runtime .bus() - .publish( - "anything/at/all", - serde_json::json!({}), - runtime.registry(), - ) + .publish("anything/at/all", serde_json::json!({}), runtime.registry()) .await; assert!( diff --git a/src/workers/continuum-core/src/runtime/service_module.rs b/src/workers/continuum-core/src/runtime/service_module.rs index b9be560a1..459697eb4 100644 --- a/src/workers/continuum-core/src/runtime/service_module.rs +++ b/src/workers/continuum-core/src/runtime/service_module.rs @@ -254,11 +254,7 @@ pub trait ServiceModule: Send + Sync + Any { /// this from the publisher's task; long work belongs in `tick` or /// in a spawned task. Errors are logged by the dispatcher; the /// publisher is not blocked by a slow subscriber. - async fn on_artifact_available( - &self, - _key: &ArtifactKey, - _value: Value, - ) -> Result<(), String> { + async fn on_artifact_available(&self, _key: &ArtifactKey, _value: Value) -> Result<(), String> { Ok(()) } @@ -296,11 +292,15 @@ mod tests { tick_interval: None, } } - async fn initialize(&self, _ctx: &super::super::ModuleContext) -> Result<(), String> { Ok(()) } + async fn initialize(&self, _ctx: &super::super::ModuleContext) -> Result<(), String> { + Ok(()) + } async fn handle_command(&self, _: &str, _: Value) -> Result { Err("not handled".to_string()) } - fn as_any(&self) -> &dyn Any { self } + fn as_any(&self) -> &dyn Any { + self + } } /// Module that opts in — represents what Lane D's persona modules @@ -320,7 +320,9 @@ mod tests { tick_interval: None, } } - async fn initialize(&self, _ctx: &super::super::ModuleContext) -> Result<(), String> { Ok(()) } + async fn initialize(&self, _ctx: &super::super::ModuleContext) -> Result<(), String> { + Ok(()) + } async fn handle_command(&self, _: &str, _: Value) -> Result { Err("not handled".to_string()) } @@ -350,7 +352,9 @@ mod tests { Ok(()) } - fn as_any(&self) -> &dyn Any { self } + fn as_any(&self) -> &dyn Any { + self + } } /// What this catches: default-impl methods return the "no @@ -364,10 +368,7 @@ mod tests { assert!(m.artifact_subscriptions().is_empty()); assert_eq!(m.cadence(), None); let result = m - .on_artifact_available( - &ArtifactKey::from("anything/at/all"), - Value::Null, - ) + .on_artifact_available(&ArtifactKey::from("anything/at/all"), Value::Null) .await; assert!( result.is_ok(), @@ -397,7 +398,8 @@ mod tests { "opted-in module should subscribe to broker snapshot" ); assert!( - !subs.iter() + !subs + .iter() .any(|s| s.matches(&ArtifactKey::from("cognition/rate_proposals.result"))), "subscription set is bounded — random unrelated keys don't match" ); diff --git a/src/workers/continuum-core/src/system_resources/memory_pressure.rs b/src/workers/continuum-core/src/system_resources/memory_pressure.rs index 106b26fb7..913b73964 100644 --- a/src/workers/continuum-core/src/system_resources/memory_pressure.rs +++ b/src/workers/continuum-core/src/system_resources/memory_pressure.rs @@ -64,8 +64,8 @@ use serde::Serialize; use std::panic::AssertUnwindSafe; -use std::sync::Arc; use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::Arc; use std::time::Duration; use tokio::sync::watch; use ts_rs::TS; diff --git a/src/workers/continuum-core/src/system_resources/mod.rs b/src/workers/continuum-core/src/system_resources/mod.rs index ed3589a6c..bec167cd4 100644 --- a/src/workers/continuum-core/src/system_resources/mod.rs +++ b/src/workers/continuum-core/src/system_resources/mod.rs @@ -18,9 +18,9 @@ pub mod monitor; pub use concurrency::local_inference_capacity; pub use memory_pressure::{ - MemoryBudgetAllocation, MemoryBudgetSnapshot, MemoryBudgetSpec, MemoryPressureMonitor, - MemoryPriority, MemoryReporter, ModuleMemoryReport, PressureLevel, PressureSnapshot, - is_memory_gate_closed, + is_memory_gate_closed, MemoryBudgetAllocation, MemoryBudgetSnapshot, MemoryBudgetSpec, + MemoryPressureMonitor, MemoryPriority, MemoryReporter, ModuleMemoryReport, PressureLevel, + PressureSnapshot, }; pub use monitor::{ CpuStats, MemoryStats, ProcessStats, SystemResourceMonitor, SystemResourceSnapshot, TopProcess, diff --git a/src/workers/continuum-core/src/tool_parsing/correction.rs b/src/workers/continuum-core/src/tool_parsing/correction.rs index cac62877b..31e886b16 100644 --- a/src/workers/continuum-core/src/tool_parsing/correction.rs +++ b/src/workers/continuum-core/src/tool_parsing/correction.rs @@ -243,16 +243,12 @@ mod tests { let result = correct_tool_call("code/write", ¶ms); assert_eq!(result.parameters.get("filePath").unwrap(), "/test.ts"); assert_eq!(result.parameters.get("content").unwrap(), "hello world"); - assert!( - result - .param_corrections - .contains(&"path -> filePath".to_string()) - ); - assert!( - result - .param_corrections - .contains(&"text -> content".to_string()) - ); + assert!(result + .param_corrections + .contains(&"path -> filePath".to_string())); + assert!(result + .param_corrections + .contains(&"text -> content".to_string())); } #[test] diff --git a/src/workers/continuum-core/src/vdd/chat_roundtrip.rs b/src/workers/continuum-core/src/vdd/chat_roundtrip.rs index 8911ac027..72b1f9214 100644 --- a/src/workers/continuum-core/src/vdd/chat_roundtrip.rs +++ b/src/workers/continuum-core/src/vdd/chat_roundtrip.rs @@ -260,10 +260,8 @@ mod tests { assert_eq!(record.status, HarnessStatus::Pass); assert_eq!(record.first_response_ms, Some(40)); assert!(bundle.manifest_toml.exists()); - assert!( - std::fs::read_to_string(&bundle.summary_md) - .unwrap() - .contains("chat-roundtrip-live-harness") - ); + assert!(std::fs::read_to_string(&bundle.summary_md) + .unwrap() + .contains("chat-roundtrip-live-harness")); } } diff --git a/src/workers/continuum-core/src/vdd/mod.rs b/src/workers/continuum-core/src/vdd/mod.rs index a3184469a..17228d999 100644 --- a/src/workers/continuum-core/src/vdd/mod.rs +++ b/src/workers/continuum-core/src/vdd/mod.rs @@ -17,7 +17,7 @@ pub use chat_roundtrip::{ }; pub use reader::{latest_per_scenario, read_records, VddReadOptions, VddRecordEntry}; pub use record::{HarnessStatus, StandardVddRecord, VddError}; -pub use registry::{HARNESS_SPECS, HarnessCadence, HarnessId, HarnessSpec, harness_spec}; +pub use registry::{harness_spec, HarnessCadence, HarnessId, HarnessSpec, HARNESS_SPECS}; pub use turn_replay::{ read_fixture, LiveTurnReplayFixture, LiveTurnReplayWriter, LIVE_TURN_REPLAY_FIXTURE_SCHEMA_VERSION, diff --git a/src/workers/continuum-core/src/vdd/reader.rs b/src/workers/continuum-core/src/vdd/reader.rs index f0aadc5a5..5e6543a75 100644 --- a/src/workers/continuum-core/src/vdd/reader.rs +++ b/src/workers/continuum-core/src/vdd/reader.rs @@ -246,8 +246,8 @@ mod tests { #[test] fn empty_root_returns_empty_vec() { let tmp = tempfile::tempdir().unwrap(); - let entries = read_records(tmp.path(), &VddReadOptions::default()) - .expect("empty root reads cleanly"); + let entries = + read_records(tmp.path(), &VddReadOptions::default()).expect("empty root reads cleanly"); assert!(entries.is_empty()); } @@ -262,8 +262,7 @@ mod tests { let manifest = ReproducibilityManifest::from_record(&original, &[]); writer.write(&original, &manifest).expect("write succeeds"); - let entries = read_records(tmp.path(), &VddReadOptions::default()) - .expect("read succeeds"); + let entries = read_records(tmp.path(), &VddReadOptions::default()).expect("read succeeds"); assert_eq!(entries.len(), 1); let entry = &entries[0]; assert_eq!(entry.record.git_sha, "abc1234"); @@ -292,8 +291,7 @@ mod tests { writer.write(&r, &m).unwrap(); } - let entries = read_records(tmp.path(), &VddReadOptions::default()) - .expect("read succeeds"); + let entries = read_records(tmp.path(), &VddReadOptions::default()).expect("read succeeds"); let pairs: Vec<(&str, &str)> = entries .iter() .map(|e| (e.record.git_sha.as_str(), e.record.scenario.as_str())) @@ -384,7 +382,10 @@ mod tests { let latest = latest_per_scenario(entries); assert_eq!(latest.len(), 1); let entry = latest - .get(&("sha-x".to_string(), "chat-roundtrip-live-harness".to_string())) + .get(&( + "sha-x".to_string(), + "chat-roundtrip-live-harness".to_string(), + )) .expect("scenario present"); assert_eq!(entry.record.status, HarnessStatus::Fail); assert_eq!(entry.record.silence_reasons, vec!["model_load_timeout"]); diff --git a/src/workers/continuum-core/src/vdd/turn_replay.rs b/src/workers/continuum-core/src/vdd/turn_replay.rs index d297682f9..f8a6ba024 100644 --- a/src/workers/continuum-core/src/vdd/turn_replay.rs +++ b/src/workers/continuum-core/src/vdd/turn_replay.rs @@ -129,8 +129,8 @@ impl LiveTurnReplayWriter { /// Matches `ArtifactWriter::continuum_default()` so both /// writers share the same artifact root. pub fn continuum_default() -> Self { - let home = dirs::home_dir() - .expect("home directory must exist for VDD turn-replay artifacts"); + let home = + dirs::home_dir().expect("home directory must exist for VDD turn-replay artifacts"); Self::new(home.join(".continuum").join("vdd")) } @@ -169,11 +169,10 @@ impl LiveTurnReplayWriter { source, })?; // Trailing newline — convention for cat / grep ergonomics. - file.write_all(b"\n") - .map_err(|source| VddError::Io { - path: path.clone(), - source, - })?; + file.write_all(b"\n").map_err(|source| VddError::Io { + path: path.clone(), + source, + })?; Ok(path) } } @@ -363,7 +362,9 @@ mod tests { let writer = LiveTurnReplayWriter::new(tmp.path()); let original = sample_fixture(); - let path = writer.write(&original, "request-100").expect("write succeeds"); + let path = writer + .write(&original, "request-100") + .expect("write succeeds"); // Path layout: //turn-replays/.json let expected = tmp diff --git a/src/workers/continuum-core/tests/fixture_assembly_replay.rs b/src/workers/continuum-core/tests/fixture_assembly_replay.rs index c4edc7eda..04dc4490f 100644 --- a/src/workers/continuum-core/tests/fixture_assembly_replay.rs +++ b/src/workers/continuum-core/tests/fixture_assembly_replay.rs @@ -65,10 +65,10 @@ use continuum_core::ai::types::{ContentPart, MessageContent}; use continuum_core::cognition::tool_executor::types::MediaItemLite; use continuum_core::model_registry::Capability; -use continuum_core::persona::prompt_assembly::PromptMessage; use continuum_core::persona::cognition_io::{ build_respond_input, PersonaContext, Signal, SignalKind, SignalOriginator, }; +use continuum_core::persona::prompt_assembly::PromptMessage; use continuum_core::persona::response::build_messages_with_media; use serde_json::Value; use std::collections::HashSet; @@ -215,9 +215,10 @@ fn signal_and_ctx_from_legacy_fixture( // New shape (post-IPC-reshape commit 983d30102): rust_request already // has `signal` + `personaContext` as nested objects matching the wire // shape exactly. Deserialize directly. No reconstruction needed. - if let (Some(signal_json), Some(ctx_json)) = - (rust_request.get("signal"), rust_request.get("personaContext")) - { + if let (Some(signal_json), Some(ctx_json)) = ( + rust_request.get("signal"), + rust_request.get("personaContext"), + ) { let signal: Signal = serde_json::from_value(signal_json.clone()) .map_err(|e| format!("new-shape signal deserialize failed: {e}"))?; let ctx: PersonaContext = serde_json::from_value(ctx_json.clone()) @@ -286,7 +287,9 @@ fn signal_and_ctx_from_legacy_fixture( kind: SignalKind::ChatMessage, text: message_text, media, - originator: SignalOriginator::User { user_id: Uuid::nil() }, + originator: SignalOriginator::User { + user_id: Uuid::nil(), + }, timestamp_ms: 0, message_id: Some(message_id), }; @@ -334,7 +337,9 @@ fn fixtures_replay_through_message_builder() { let prompt = synth_prompt_messages(rust_request); let out = build_messages_with_media(prompt, &media, &caps); - let last = out.last().expect("builder always returns at least one message"); + let last = out + .last() + .expect("builder always returns at least one message"); let image_parts: Vec<&ContentPart> = match &last.content { MessageContent::Text(_) => Vec::new(), MessageContent::Parts(parts) => parts @@ -493,8 +498,10 @@ async fn ensure_llamacpp_qwen2vl_registered() -> Option<()> { if !gguf_path.exists() { continue; } - let mut adapter: Box = - Box::new(LlamaCppAdapter::with_model_id(gguf_path.clone(), m.id.clone())); + let mut adapter: Box = Box::new(LlamaCppAdapter::with_model_id( + gguf_path.clone(), + m.id.clone(), + )); adapter .initialize() .await @@ -537,10 +544,7 @@ async fn vision_fixture_describes_image_via_real_model() { let caps = extract_capabilities(rust_request); let has_real_image = media.iter().any(|m| { m.item_type == "image" - && m.base64 - .as_deref() - .map(|b| !b.is_empty()) - .unwrap_or(false) + && m.base64.as_deref().map(|b| !b.is_empty()).unwrap_or(false) }); has_real_image && caps.contains(&Capability::Vision) }) @@ -602,7 +606,9 @@ async fn vision_fixture_describes_image_via_real_model() { let (signal, ctx) = match signal_and_ctx_from_legacy_fixture(rust_request) { Ok(pair) => pair, Err(e) => { - failures.push(format!("[{fname}] could not build Signal+PersonaContext: {e}")); + failures.push(format!( + "[{fname}] could not build Signal+PersonaContext: {e}" + )); continue; } }; @@ -647,7 +653,9 @@ async fn vision_fixture_describes_image_via_real_model() { a response. reason: {reason}" )); } - PersonaResponse::Spoke { text, model_used, .. } => { + PersonaResponse::Spoke { + text, model_used, .. + } => { let trimmed = text.trim(); if trimmed.len() < 30 { failures.push(format!( diff --git a/src/workers/continuum-core/tests/generated_barrel_sync.rs b/src/workers/continuum-core/tests/generated_barrel_sync.rs index 93d33ac58..fe515115a 100644 --- a/src/workers/continuum-core/tests/generated_barrel_sync.rs +++ b/src/workers/continuum-core/tests/generated_barrel_sync.rs @@ -174,8 +174,7 @@ fn scan_all_modules(root: &Path) -> Vec { let referenced = parse_barrel_from_paths(&barrel); let missing_from_barrel: BTreeSet = on_disk.difference(&referenced).cloned().collect(); - let dangling_exports: BTreeSet = - referenced.difference(&on_disk).cloned().collect(); + let dangling_exports: BTreeSet = referenced.difference(&on_disk).cloned().collect(); reports.push(ModuleDrift { module: module_name, missing_from_barrel, @@ -257,7 +256,10 @@ fn parser_extracts_from_path_not_type_name_on_rename() { let input = "export type { ToolCall } from './AgentToolCall';"; let got = parse_barrel_from_paths_str(input); assert!(got.contains("AgentToolCall"), "got: {got:?}"); - assert!(!got.contains("ToolCall"), "must not extract type name: {got:?}"); + assert!( + !got.contains("ToolCall"), + "must not extract type name: {got:?}" + ); } /// What this catches: double-quoted variants are tolerated. The @@ -325,8 +327,14 @@ fn drift_detection_reports_both_regression_modes() { .collect(); let missing: BTreeSet = on_disk.difference(&referenced).cloned().collect(); let dangling: BTreeSet = referenced.difference(&on_disk).cloned().collect(); - assert_eq!(missing.iter().cloned().collect::>(), vec!["B".to_string()]); - assert_eq!(dangling.iter().cloned().collect::>(), vec!["C".to_string()]); + assert_eq!( + missing.iter().cloned().collect::>(), + vec!["B".to_string()] + ); + assert_eq!( + dangling.iter().cloned().collect::>(), + vec!["C".to_string()] + ); } /// Smoke check: every module dir we expect to exist actually does. @@ -341,10 +349,29 @@ fn drift_detection_reports_both_regression_modes() { fn known_modules_still_present() { let root = shared_generated_dir(); let known = [ - "agent", "ai", "cognition", "code", "dataset", "gpu", "grid", - "inference", "ipc", "live", "logger", "mcp", "model_registry", - "orm", "persona", "plasticity", "rag", "recipe", "runtime", - "search", "sentinel", "system", "voice", + "agent", + "ai", + "cognition", + "code", + "dataset", + "gpu", + "grid", + "inference", + "ipc", + "live", + "logger", + "mcp", + "model_registry", + "orm", + "persona", + "plasticity", + "rag", + "recipe", + "runtime", + "search", + "sentinel", + "system", + "voice", ]; let on_disk: BTreeSet = fs::read_dir(&root) .expect("read shared/generated") @@ -358,7 +385,11 @@ fn known_modules_still_present() { } }) .collect(); - let missing: Vec<&str> = known.iter().copied().filter(|m| !on_disk.contains(*m)).collect(); + let missing: Vec<&str> = known + .iter() + .copied() + .filter(|m| !on_disk.contains(*m)) + .collect(); assert!( missing.is_empty(), "known module dir(s) disappeared from shared/generated/: {missing:?}. \ diff --git a/src/workers/continuum-core/tests/llamacpp_audio_integration.rs b/src/workers/continuum-core/tests/llamacpp_audio_integration.rs index 9cbbfa403..7bc091988 100644 --- a/src/workers/continuum-core/tests/llamacpp_audio_integration.rs +++ b/src/workers/continuum-core/tests/llamacpp_audio_integration.rs @@ -36,14 +36,18 @@ fn qwen2_audio_paths() -> (PathBuf, PathBuf) { let model = env::var("QWEN2_AUDIO_7B_GGUF") .map(PathBuf::from) .unwrap_or_else(|_| { - PathBuf::from(env::var("HOME").expect("HOME env var must be set for this integration test")) - .join("models/qwen2-audio-7b/Qwen2-Audio-7B-Instruct-Q4_K_M.gguf") + PathBuf::from( + env::var("HOME").expect("HOME env var must be set for this integration test"), + ) + .join("models/qwen2-audio-7b/Qwen2-Audio-7B-Instruct-Q4_K_M.gguf") }); let mmproj = env::var("QWEN2_AUDIO_7B_MMPROJ") .map(PathBuf::from) .unwrap_or_else(|_| { - PathBuf::from(env::var("HOME").expect("HOME env var must be set for this integration test")) - .join("models/qwen2-audio-7b/mmproj-Qwen2-Audio-7B-Instruct-f16.gguf") + PathBuf::from( + env::var("HOME").expect("HOME env var must be set for this integration test"), + ) + .join("models/qwen2-audio-7b/mmproj-Qwen2-Audio-7B-Instruct-f16.gguf") }); (model, mmproj) } @@ -92,9 +96,12 @@ fn load_or_generate_test_wav() -> Option> { } let convert_ok = Command::new("afconvert") .args([ - "-f", "WAVE", - "-d", "LEI16@16000", - "-c", "1", + "-f", + "WAVE", + "-d", + "LEI16@16000", + "-c", + "1", aiff.to_str()?, path.to_str()?, ]) @@ -232,7 +239,14 @@ fn qwen2_audio_describes_clip_via_rust_pipeline() { // would mean the audio bytes never made it to the encoder. let lower = text.to_lowercase(); let signal_words = [ - "hello", "test", "audio", "model", "describe", "hear", "clip", "understanding", + "hello", + "test", + "audio", + "model", + "describe", + "hear", + "clip", + "understanding", ]; let hits: Vec<&str> = signal_words .iter() diff --git a/src/workers/continuum-core/tests/llamacpp_vision_integration.rs b/src/workers/continuum-core/tests/llamacpp_vision_integration.rs index af0de33cd..b0b104ca8 100644 --- a/src/workers/continuum-core/tests/llamacpp_vision_integration.rs +++ b/src/workers/continuum-core/tests/llamacpp_vision_integration.rs @@ -39,14 +39,18 @@ fn qwen2_vl_paths() -> (PathBuf, PathBuf) { let model = env::var("QWEN2_VL_7B_GGUF") .map(PathBuf::from) .unwrap_or_else(|_| { - PathBuf::from(env::var("HOME").expect("HOME env var must be set for this integration test")) - .join("models/qwen2-vl-7b/Qwen2-VL-7B-Instruct-Q4_K_M.gguf") + PathBuf::from( + env::var("HOME").expect("HOME env var must be set for this integration test"), + ) + .join("models/qwen2-vl-7b/Qwen2-VL-7B-Instruct-Q4_K_M.gguf") }); let mmproj = env::var("QWEN2_VL_7B_MMPROJ") .map(PathBuf::from) .unwrap_or_else(|_| { - PathBuf::from(env::var("HOME").expect("HOME env var must be set for this integration test")) - .join("models/qwen2-vl-7b/mmproj-Qwen2-VL-7B-Instruct-f16.gguf") + PathBuf::from( + env::var("HOME").expect("HOME env var must be set for this integration test"), + ) + .join("models/qwen2-vl-7b/mmproj-Qwen2-VL-7B-Instruct-f16.gguf") }); (model, mmproj) } diff --git a/src/workers/continuum-core/tests/multi_adapter_boot_integration.rs b/src/workers/continuum-core/tests/multi_adapter_boot_integration.rs index eadaf2e29..e05e7ecf1 100644 --- a/src/workers/continuum-core/tests/multi_adapter_boot_integration.rs +++ b/src/workers/continuum-core/tests/multi_adapter_boot_integration.rs @@ -93,7 +93,12 @@ async fn llamacpp_local_models_coexist_without_metal_oom() { local_rows.len() ); for m in &local_rows { - let mtmd = if m.mmproj_local_path.as_ref().map(|p| p.exists()).unwrap_or(false) { + let mtmd = if m + .mmproj_local_path + .as_ref() + .map(|p| p.exists()) + .unwrap_or(false) + { "mtmd-capable" } else { "text-only" @@ -109,8 +114,8 @@ async fn llamacpp_local_models_coexist_without_metal_oom() { let mut adapters: Vec> = Vec::with_capacity(local_rows.len()); for model_meta in &local_rows { let gguf = model_meta.gguf_local_path.as_ref().unwrap().clone(); - let adapter = LlamaCppAdapter::with_model_id(gguf, model_meta.id.clone()) - .with_context_length(32768); + let adapter = + LlamaCppAdapter::with_model_id(gguf, model_meta.id.clone()).with_context_length(32768); let mut boxed: Box = Box::new(adapter); let init_start = std::time::Instant::now(); boxed diff --git a/src/workers/continuum-core/tests/no_cpu_fallback_contract.rs b/src/workers/continuum-core/tests/no_cpu_fallback_contract.rs index ea5325513..674918fe8 100644 --- a/src/workers/continuum-core/tests/no_cpu_fallback_contract.rs +++ b/src/workers/continuum-core/tests/no_cpu_fallback_contract.rs @@ -25,14 +25,11 @@ //! https://github.com/CambrianTech/continuum/issues/1262#issuecomment-4461757997 //! https://github.com/CambrianTech/continuum/issues/1280#issuecomment-4462181316 -const LLAMACPP_BACKEND_SOURCE: &str = - include_str!("../src/inference/backends/llamacpp.rs"); +const LLAMACPP_BACKEND_SOURCE: &str = include_str!("../src/inference/backends/llamacpp.rs"); -const ORT_PROVIDERS_SOURCE: &str = - include_str!("../src/inference/ort_providers.rs"); +const ORT_PROVIDERS_SOURCE: &str = include_str!("../src/inference/ort_providers.rs"); -const LLAMACPP_ADAPTER_SOURCE: &str = - include_str!("../src/inference/llamacpp_adapter.rs"); +const LLAMACPP_ADAPTER_SOURCE: &str = include_str!("../src/inference/llamacpp_adapter.rs"); // Candle-side sources surfaced by #1316 ALPHA-GAP finding #5: the // no_cpu_fallback contract test originally covered only llama.cpp + @@ -43,20 +40,15 @@ const LLAMACPP_ADAPTER_SOURCE: &str = // of those paths without breaking this gate. The constants below close // that hole. -const INFERENCE_GRPC_MODEL_SOURCE: &str = - include_str!("../../inference-grpc/src/model.rs"); +const INFERENCE_GRPC_MODEL_SOURCE: &str = include_str!("../../inference-grpc/src/model.rs"); -const ORPHEUS_TTS_SOURCE: &str = - include_str!("../src/live/audio/tts/orpheus.rs"); +const ORPHEUS_TTS_SOURCE: &str = include_str!("../src/live/audio/tts/orpheus.rs"); -const RESIDENCY_GATE_SOURCE: &str = - include_str!("../src/inference_capability/residency.rs"); +const RESIDENCY_GATE_SOURCE: &str = include_str!("../src/inference_capability/residency.rs"); -const ENFORCEMENT_SOURCE: &str = - include_str!("../src/inference_capability/enforcement.rs"); +const ENFORCEMENT_SOURCE: &str = include_str!("../src/inference_capability/enforcement.rs"); -const HW_PROBE_SOURCE: &str = - include_str!("../src/inference_capability/hw_probe.rs"); +const HW_PROBE_SOURCE: &str = include_str!("../src/inference_capability/hw_probe.rs"); #[test] fn llamacpp_default_config_requires_full_gpu_offload() { @@ -138,8 +130,7 @@ fn inference_grpc_select_best_device_hard_fails_on_no_gpu() { // someone silently re-add Device::Cpu as the "Ok" fallback. assert!( INFERENCE_GRPC_MODEL_SOURCE.contains("fn select_best_device") - && (INFERENCE_GRPC_MODEL_SOURCE - .contains("fn select_best_device() -> Result Result Result . If you changed the signature \ @@ -156,8 +147,7 @@ fn orpheus_tts_select_device_hard_fails_on_no_metal() { // caller sees the broken state instead of getting choppy CPU TTS. assert!( - ORPHEUS_TTS_SOURCE.contains("fn select_device") && - ORPHEUS_TTS_SOURCE.contains("TTSError"), + ORPHEUS_TTS_SOURCE.contains("fn select_device") && ORPHEUS_TTS_SOURCE.contains("TTSError"), "orpheus.rs select_device must return Result and refuse to fall \ back to CPU. If you removed the Result return type or the TTSError variant, \ the TTS path silently CPU-degrades — the exact bug #1312 fixed." @@ -244,9 +234,9 @@ fn hw_probe_does_not_introduce_cpu_fallback() { // what's available). assert!( - HW_PROBE_SOURCE.contains("Probe NEVER panics") || - HW_PROBE_SOURCE.contains("never panics") || - HW_PROBE_SOURCE.contains("probe NEVER panics"), + HW_PROBE_SOURCE.contains("Probe NEVER panics") + || HW_PROBE_SOURCE.contains("never panics") + || HW_PROBE_SOURCE.contains("probe NEVER panics"), "hw_probe.rs must document its never-panic contract — the probe is called from \ supervisor + adapter init code, panicking there crashes the process. Comment \ is also the contract for reviewers: don't add a panic path here." diff --git a/src/workers/continuum-core/tests/persona_respond_replay.rs b/src/workers/continuum-core/tests/persona_respond_replay.rs index 19c2894ad..28a849d59 100644 --- a/src/workers/continuum-core/tests/persona_respond_replay.rs +++ b/src/workers/continuum-core/tests/persona_respond_replay.rs @@ -171,11 +171,7 @@ fn build_input(fix: &Fixture, known_specialties: Vec) -> RespondInput { // the room-level fields from the captured fixture, then bundles // them into Arc so the constructed RespondInput // matches the live IPC path's shape. - turn_context: TurnContext::arc( - fix.rust_request.room_id, - recent_history, - known_specialties, - ), + turn_context: TurnContext::arc(fix.rust_request.room_id, recent_history, known_specialties), message_id: fix.rust_request.message_id, message_text: fix.rust_request.message_text.clone(), other_persona_names: Vec::new(), diff --git a/src/workers/continuum-core/tests/qwen35_chat_pipeline_full.rs b/src/workers/continuum-core/tests/qwen35_chat_pipeline_full.rs index 897b109fc..b9359009a 100644 --- a/src/workers/continuum-core/tests/qwen35_chat_pipeline_full.rs +++ b/src/workers/continuum-core/tests/qwen35_chat_pipeline_full.rs @@ -141,7 +141,13 @@ fn qwen35_scheduler_json_grammar_returns_object() { }; let (text, n_tokens) = backend - .generate(&prompt, 128, sampling, &["<|im_end|>", "<|endoftext|>"], &[]) + .generate( + &prompt, + 128, + sampling, + &["<|im_end|>", "<|endoftext|>"], + &[], + ) .expect("generate"); eprintln!("[json-grammar] tokens={n_tokens} text={text:?}");