diff --git a/src/shared/generated/genome/CapabilityQuery.ts b/src/shared/generated/genome/CapabilityQuery.ts index e81faf875..551153f53 100644 --- a/src/shared/generated/genome/CapabilityQuery.ts +++ b/src/shared/generated/genome/CapabilityQuery.ts @@ -2,8 +2,8 @@ import type { ArtifactRef } from "./ArtifactRef"; import type { DomainHint } from "./DomainHint"; import type { FreshnessTarget } from "./FreshnessTarget"; +import type { RecallBudget } from "./RecallBudget"; import type { RecallScope } from "./RecallScope"; -import type { ResourceBudget } from "./ResourceBudget"; import type { TaskKind } from "./TaskKind"; /** @@ -15,7 +15,7 @@ export type CapabilityQuery = { taskKind: TaskKind, /** * Free-form tags from the persona's plan. May be empty. */ -domainHints: Array, budget: ResourceBudget, +domainHints: Array, budget: RecallBudget, /** * Hard pins — recall MUST include these in the RankedPool even * if their score is low. Used for persona-private LoRA layers diff --git a/src/shared/generated/genome/CompositionRef.ts b/src/shared/generated/genome/CompositionRef.ts index fac5de7b7..9c5528561 100644 --- a/src/shared/generated/genome/CompositionRef.ts +++ b/src/shared/generated/genome/CompositionRef.ts @@ -3,6 +3,6 @@ /** * Stub placeholder for "what composition is currently hot for this * persona." Full shape from the composer module (not built yet); - * PR-2 ships a thin opaque struct so PersonaContext compiles. + * PR-2 ships a thin opaque struct so RecallContext compiles. */ export type CompositionRef = string; diff --git a/src/shared/generated/genome/TrustClass.ts b/src/shared/generated/genome/TrustClass.ts index 5bf95c4ac..f0b3518d9 100644 --- a/src/shared/generated/genome/TrustClass.ts +++ b/src/shared/generated/genome/TrustClass.ts @@ -3,7 +3,7 @@ /** * How much the persona trusts a peer's artifacts. Adjusted at * scoring time via the persona's `trust_overrides` field - * (PersonaContext, PR-2). PR-1 names the variants the override list + * (RecallContext, PR-2). PR-1 names the variants the override list * can map a peer to. */ export type TrustClass = "local" | "trustedPeer" | "knownPeer" | "anonymous"; diff --git a/src/workers/continuum-core/src/genome/mod.rs b/src/workers/continuum-core/src/genome/mod.rs index 987edec17..a1f1f94aa 100644 --- a/src/workers/continuum-core/src/genome/mod.rs +++ b/src/workers/continuum-core/src/genome/mod.rs @@ -76,17 +76,16 @@ pub use bus::{ PAGE_FAULT_KEY, }; pub use local_manager::LocalWorkingSetManager; +pub use manager::WorkingSetManager; pub use recall::{ - AcquireSource, FreshnessTarget, PeerId, RecallError, RecallScope, RecallScore, - ResidencyHint, TaskKind, TrustClass, + AcquireSource, FreshnessTarget, PeerId, RecallError, RecallScope, RecallScore, ResidencyHint, + TaskKind, TrustClass, }; pub use recall_trait::{ - ArtifactRef, CapabilityQuery, CompositionHint, CompositionRef, DemandAlignedRecall, - DomainHint, EngramRef, LoRALayerRef, MoEExpertRef, OutcomeWindow, PersonaContext, - RankedPool, RecallScoreWeights, RecallTrace, ResourceBudget, TrajectoryHint, - WeightSumOutOfBounds, + ArtifactRef, CapabilityQuery, CompositionHint, CompositionRef, DemandAlignedRecall, DomainHint, + EngramRef, LoRALayerRef, MoEExpertRef, OutcomeWindow, RankedPool, RecallBudget, RecallContext, + RecallScoreWeights, RecallTrace, TrajectoryHint, WeightSumOutOfBounds, }; -pub use manager::WorkingSetManager; pub use store::TierStore; pub use tier::{EvictionPolicy, EvictionRecord, TierCapacity, TierError, TierRole}; pub use working_set::{ diff --git a/src/workers/continuum-core/src/genome/recall.rs b/src/workers/continuum-core/src/genome/recall.rs index 5baa97e1c..550a719eb 100644 --- a/src/workers/continuum-core/src/genome/recall.rs +++ b/src/workers/continuum-core/src/genome/recall.rs @@ -39,9 +39,9 @@ //! ## What PR-1 does NOT ship (PR-2 / PR-3) //! //! - `DemandAlignedRecall` trait — PR-2 -//! - `CapabilityQuery`, `PersonaContext`, `RankedPool`, +//! - `CapabilityQuery`, `RecallContext`, `RankedPool`, //! `RecallScoreWeights` full shapes — PR-2 (they reference PR-1's -//! types but depend on PersonaContext + composition types that +//! types but depend on RecallContext + composition types that //! benefit from being grouped with the trait) //! - Scoring function + grid_penalty + recency_decay — PR-3 //! - `LocalDemandAlignedRecall` impl + working-set integration — PR-3 @@ -267,7 +267,7 @@ pub enum TaskKind { /// How much the persona trusts a peer's artifacts. Adjusted at /// scoring time via the persona's `trust_overrides` field -/// (PersonaContext, PR-2). PR-1 names the variants the override list +/// (RecallContext, PR-2). PR-1 names the variants the override list /// can map a peer to. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] diff --git a/src/workers/continuum-core/src/genome/recall_trait.rs b/src/workers/continuum-core/src/genome/recall_trait.rs index ca9bbaf07..a0ad98853 100644 --- a/src/workers/continuum-core/src/genome/recall_trait.rs +++ b/src/workers/continuum-core/src/genome/recall_trait.rs @@ -9,14 +9,14 @@ //! - The trait itself — `recall` + `replay` method signatures //! - `CapabilityQuery` — the input to `recall`: what kind of task, //! resource budget, scope, freshness target, hard pins -//! - `PersonaContext` — who's asking and what they already have hot +//! - `RecallContext` — who's asking and what they already have hot //! - `RankedPool` — the output: ranked layers + experts + engrams //! with per-artifact `ResidencyHint` (from PR-1) //! - `RecallScoreWeights` — governor-tunable weights with a sum-to-1 //! invariant + a constructor that enforces it //! - `ArtifactRef` + `LoRALayerRef` / `MoEExpertRef` / `EngramRef` //! typed wrappers around `ArtifactId` -//! - `ResourceBudget` — the memory + time budget the persona allocates +//! - `RecallBudget` — the memory + time budget the persona allocates //! - Stub placeholders for `OutcomeWindow` / `TrajectoryHint` / //! `CompositionRef` / `CompositionHint` / `RecallTrace` — //! GENOME-FOUNDRY-SENTINEL names these but their full shapes @@ -39,7 +39,7 @@ use serde::{Deserialize, Serialize}; use ts_rs::TS; use super::recall::{ - FreshnessTarget, PeerId, RecallError, RecallScore, RecallScope, ResidencyHint, TaskKind, + FreshnessTarget, PeerId, RecallError, RecallScope, RecallScore, ResidencyHint, TaskKind, TrustClass, }; use super::working_set::{ArtifactId, PersonaId}; @@ -92,10 +92,7 @@ pub struct EngramRef(pub ArtifactId); /// consumers narrow by `kind` and read `ref` for the artifact id. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(tag = "kind", content = "ref", rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/ArtifactRef.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/ArtifactRef.ts")] pub enum ArtifactRef { LoRALayer(LoRALayerRef), MoEExpert(MoEExpertRef), @@ -127,11 +124,8 @@ impl DomainHint { /// (e.g. don't include a 4GB layer if budget is 1GB). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/ResourceBudget.ts" -)] -pub struct ResourceBudget { +#[ts(export, export_to = "../../../shared/generated/genome/RecallBudget.ts")] +pub struct RecallBudget { /// Maximum bytes the composition is allowed to consume. #[ts(type = "number")] pub max_bytes: u64, @@ -184,7 +178,7 @@ pub struct TrajectoryHint { /// Stub placeholder for "what composition is currently hot for this /// persona." Full shape from the composer module (not built yet); -/// PR-2 ships a thin opaque struct so PersonaContext compiles. +/// PR-2 ships a thin opaque struct so RecallContext compiles. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, TS)] #[serde(transparent)] #[ts( @@ -203,9 +197,9 @@ pub struct CompositionRef(pub ArtifactId); #[serde(rename_all = "camelCase")] #[ts( export, - export_to = "../../../shared/generated/genome/PersonaContext.ts" + export_to = "../../../shared/generated/genome/RecallContext.ts" )] -pub struct PersonaContext { +pub struct RecallContext { pub persona: PersonaId, /// What composition is already hot for this persona. `None` /// means the persona is starting fresh (cold composition). @@ -219,8 +213,8 @@ pub struct PersonaContext { pub trust_overrides: Vec<(PeerId, TrustClass)>, } -impl PersonaContext { - /// Cold-start PersonaContext: no current composition, no +impl RecallContext { + /// Cold-start RecallContext: no current composition, no /// outcome window, no trajectory, no trust overrides. Used by /// tests + first-turn recall calls. pub fn cold_start(persona: PersonaId) -> Self { @@ -249,7 +243,7 @@ pub struct CapabilityQuery { pub task_kind: TaskKind, /// Free-form tags from the persona's plan. May be empty. pub domain_hints: Vec, - pub budget: ResourceBudget, + pub budget: RecallBudget, /// Hard pins — recall MUST include these in the RankedPool even /// if their score is low. Used for persona-private LoRA layers /// and sticky engrams. @@ -299,10 +293,7 @@ pub struct RecallTrace(pub ArtifactId); /// so the persona can make the cost trade-off explicit. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)] #[serde(rename_all = "camelCase")] -#[ts( - export, - export_to = "../../../shared/generated/genome/RankedPool.ts" -)] +#[ts(export, export_to = "../../../shared/generated/genome/RankedPool.ts")] pub struct RankedPool { pub layers: Vec<(LoRALayerRef, RecallScore, ResidencyHint)>, pub experts: Vec<(MoEExpertRef, RecallScore, ResidencyHint)>, @@ -373,8 +364,7 @@ impl RecallScoreWeights { tier_proximity: f32, provenance_trust: f32, ) -> Result { - let sum = - semantic + outcome_history + recency + tier_proximity + provenance_trust; + let sum = semantic + outcome_history + recency + tier_proximity + provenance_trust; if (sum - 1.0).abs() > Self::SUM_EPSILON { return Err(WeightSumOutOfBounds { actual_sum: sum }); } @@ -432,7 +422,7 @@ pub trait DemandAlignedRecall: Send + Sync { async fn recall( &self, query: &CapabilityQuery, - context: &PersonaContext, + context: &RecallContext, ) -> Result; /// Replay a previous recall deterministically from its trace. @@ -440,10 +430,7 @@ pub trait DemandAlignedRecall: Send + Sync { /// regression testing. Replay produces the same RankedPool the /// live recall did, using snapshotted scoring weights + artifact /// set at that time. - async fn replay( - &self, - trace: &RecallTrace, - ) -> Result; + async fn replay(&self, trace: &RecallTrace) -> Result; } #[cfg(test)] @@ -476,7 +463,7 @@ mod tests { async fn recall( &self, _query: &CapabilityQuery, - _context: &PersonaContext, + _context: &RecallContext, ) -> Result { Ok(RankedPool { layers: Vec::new(), @@ -487,10 +474,7 @@ mod tests { }) } - async fn replay( - &self, - _trace: &RecallTrace, - ) -> Result { + async fn replay(&self, _trace: &RecallTrace) -> Result { Err(RecallError::ScopeUnreachable { reason: "stub does not implement replay".to_string(), }) @@ -501,7 +485,7 @@ mod tests { CapabilityQuery { task_kind: TaskKind::Chat, domain_hints: vec![DomainHint::new("math")], - budget: ResourceBudget { + budget: RecallBudget { max_bytes: 1_000_000, max_duration_ms: 100, }, @@ -520,7 +504,7 @@ mod tests { #[tokio::test] async fn trait_is_object_safe() { let recall: Arc = Arc::new(StubRecall); - let ctx = PersonaContext::cold_start(sample_persona()); + let ctx = RecallContext::cold_start(sample_persona()); let pool = recall.recall(&sample_query(), &ctx).await.unwrap(); assert!(pool.layers.is_empty()); assert!(pool.experts.is_empty()); @@ -543,12 +527,12 @@ mod tests { } } - /// What this catches: cold_start PersonaContext produces a + /// What this catches: cold_start RecallContext produces a /// valid context with sensible defaults. Used by first-turn /// recall calls + tests; needs to be cheap and deterministic. #[test] - fn persona_context_cold_start_has_sensible_defaults() { - let ctx = PersonaContext::cold_start(sample_persona()); + fn recall_context_cold_start_has_sensible_defaults() { + let ctx = RecallContext::cold_start(sample_persona()); assert_eq!(ctx.persona, sample_persona()); assert!(ctx.current_composition.is_none()); assert_eq!(ctx.recent_outcomes.turn_count, 0); @@ -590,7 +574,10 @@ mod tests { fn artifact_ref_serializes_with_adjacent_kind_tag() { let layer = ArtifactRef::LoRALayer(LoRALayerRef(sample_artifact())); let j = serde_json::to_string(&layer).unwrap(); - assert!(j.contains("\"kind\":\"loRALayer\"") || j.contains("\"kind\":\"loraLayer\""), "got {j}"); + assert!( + j.contains("\"kind\":\"loRALayer\"") || j.contains("\"kind\":\"loraLayer\""), + "got {j}" + ); assert!(j.contains("\"ref\":\""), "got {j}"); let expert = ArtifactRef::MoEExpert(MoEExpertRef(sample_artifact())); @@ -604,7 +591,8 @@ mod tests { assert!(j.contains("\"ref\":\""), "got {j}"); // Round-trip - let back: ArtifactRef = serde_json::from_str(&serde_json::to_string(&layer).unwrap()).unwrap(); + let back: ArtifactRef = + serde_json::from_str(&serde_json::to_string(&layer).unwrap()).unwrap(); assert_eq!(layer, back); } @@ -625,11 +613,11 @@ mod tests { assert_eq!(expert.0.as_uuid(), engram.0.as_uuid()); } - /// What this catches: ResourceBudget serializes with camelCase + /// What this catches: RecallBudget serializes with camelCase /// fields. Wire stability. #[test] - fn resource_budget_serializes_camel_case() { - let b = ResourceBudget { + fn recall_budget_serializes_camel_case() { + let b = RecallBudget { max_bytes: 1_000_000, max_duration_ms: 250, }; @@ -645,7 +633,8 @@ mod tests { #[test] fn default_recall_score_weights_sum_to_one() { let w = RecallScoreWeights::default(); - let sum = w.semantic + w.outcome_history + w.recency + w.tier_proximity + w.provenance_trust; + let sum = + w.semantic + w.outcome_history + w.recency + w.tier_proximity + w.provenance_trust; assert!( (sum - 1.0).abs() < RecallScoreWeights::SUM_EPSILON, "default weights must sum to 1.0; got {sum}" @@ -704,7 +693,9 @@ mod tests { layers: vec![( LoRALayerRef(sample_artifact()), score, - ResidencyHint::Hot { role: super::super::tier::TierRole::Fast }, + ResidencyHint::Hot { + role: super::super::tier::TierRole::Fast, + }, )], experts: vec![], engrams: vec![( @@ -722,13 +713,13 @@ mod tests { assert_eq!(pool, back); } - /// What this catches: PersonaContext serializes with camelCase + /// What this catches: RecallContext serializes with camelCase /// + current_composition is optional (None → null on wire OR /// omitted, depending on ts(optional) + skip_serializing_if). /// This pins the contract. #[test] - fn persona_context_serializes_camel_case() { - let ctx = PersonaContext::cold_start(sample_persona()); + fn recall_context_serializes_camel_case() { + let ctx = RecallContext::cold_start(sample_persona()); let j = serde_json::to_string(&ctx).unwrap(); assert!(j.contains("\"currentComposition\":") || !j.contains("currentComposition")); assert!(j.contains("\"recentOutcomes\":"), "got {j}");