open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-history/tests/r8_tiered.rs
Transaction Science 8f9a42e4d4 jouleclaw Phase 2: cascade + loader-gguf + pack + history + wire
Five crates ported / designed. All 235 tests pass; 0 errors;
0 warnings outside the loader-gguf NEON-intrinsics module (suppressed
with a documented #![allow(unsafe_op_in_unsafe_fn)] — proper fix is
wrapping each intrinsic call in unsafe { }, tracked as kernel-quality
maintenance, not standards work).

jouleclaw-cascade
  Ported from pattern-lang/joule-cascade (~7044 LOC, 17 files).
  L0 (Cache), tier dispatch, 7-axis Synthesis coordinate router, cost
  model, calibration ledger (in-memory + disk), history, verification,
  body + body_safety policy gates, active-entity tracking, l2_service
  trait surface. Two tests that depended on joule-l1's lawful-primitive
  impl (which requires pattern-lang's pattern-core + cortex-ir +
  synthesizer — OpenIE IP) were removed; the L1 trait surface lives
  here and consumers plug their own lawful lexicon in.

jouleclaw-loader-gguf
  Ported from pattern-lang/joule-loader-gguf (~11605 LOC, 17 files).
  GGUF v3 parser with mmap, Llama and Gemma / Gemma 4 decoders,
  Q4_0 / Q5_K / Q8_0 / Q8_0 grouped quants, in-place KV cache,
  matmul_q8_0 with NEON intrinsics on Apple Silicon, safetensors
  reader, tokenizer + Gemma tokenizer, sample, dequant.

jouleclaw-pack (NEW)
  The .jc.toml sidecar — JouleClaw's standard-defining contribution.
  A model on disk (GGUF / safetensors / MLX) tells you tensor shapes
  and quant schemes. It does NOT tell you what one forward pass
  actually costs in joules on real hardware. The .jc.toml is a
  declared-cost contract that travels with the model:

    - per-tensor precision (FP16/BF16/FP8/INT8/NVFP4/NF4/Q4_K/...)
    - page-alignment status (mmap-zero-copy possible? on which UMA?)
    - measured J/op on a reference hardware matrix with explicit
      Provenance (HwShunt / ModelBased / Estimator)
    - drift_factor tolerance per measurement; the runtime trips
      PackError::CostDrift when measured > declared * drift_factor
      and down-weights the backend in subsequent auction rounds

  Without a declared-cost contract, "energy-optimized inference" is
  marketing. With one, it is an engineering claim any third party
  can verify against the published reference-hardware corpus.

  5 lib tests pass. ModelFormat, Precision, Alignment, MeasurementOp,
  Pack, TensorContract, Measurement, hash_model(), verify_measurement().

jouleclaw-history
  Ported from pattern-lang/joule-history (~2026 LOC, 6 files).
  Persistent resolution history: disk + in-memory + tiered
  (hot/warm/cold) + semantic. One Rust-2024 edition fix in
  tiered.rs (binding-mode pattern: |(_, &seq)| seq -> |(_, seq)| *seq).

jouleclaw-wire
  Ported from pattern-lang/joule-wire (~1129 LOC, 2 files).
  L4 wire tier — the remote frontier escape hatch. RPC client +
  transport, invoked only when the local cascade returns Unresolvable.

Phase 3 next: HAL from efficient-genai, backend-{apple,reference,
cuda,metal,rocm,vulkan}, runtime (port joule-runtime — chat/embed/
generate/streaming/PLD/drafter — needs backends to compile).

Co-Authored-By: Claude Opus 4.7 <[email protected]>
2026-05-26 11:55:46 -04:00

342 lines
12 KiB
Rust

//! R8 tests — tiered memory transitions.
//!
//! Proves:
//! - Hot tier respects capacity; excess items demote to Warm
//! - Warm tier respects capacity; excess items demote to Cold
//! - Lookups walk Hot → Warm → Cold
//! - Cold/Warm hits promote back to Hot
//! - Transition joule costs are recorded and bounded
use jouleclaw_cascade::*;
use jouleclaw_history::*;
fn text(s: &str) -> Query {
Query {
input: QueryInput::Text(s.to_string()),
budget: JouleBudget::standard(),
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
}
}
fn answer(text: &str, tier: TierId, joules: f64) -> Answer {
Answer {
output: AnswerOutput::Text(text.to_string()),
tier_used: tier,
joules_spent: joules,
confidence: 0.9,
trace: ExecutionTrace::default(),
verification: jouleclaw_cascade::verification::VerificationStatus::Resolved,
}
}
struct TempPath { path: std::path::PathBuf }
impl TempPath {
fn new(label: &str) -> Self {
let nanos = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos();
Self {
path: std::env::temp_dir().join(format!(
"joule-r8-test-{}-{}-{}.log",
label, std::process::id(), nanos)),
}
}
fn p(&self) -> &std::path::Path { &self.path }
}
impl Drop for TempPath {
fn drop(&mut self) { let _ = std::fs::remove_file(&self.path); }
}
// ============================================================
// WarmCache unit tests
// ============================================================
#[test]
fn warm_cache_respects_capacity() {
let mut warm = WarmCache::new(3);
for i in 0..3 {
let q = text(&format!("query-{}", i));
let a = answer(&format!("answer-{}", i), TierId::L4(L4ModelId(0)), 0.5);
warm.record(&q, &a).unwrap();
}
assert_eq!(warm.len(), 3);
// Insert a 4th item — should evict the LRU (query-0).
let q = text("query-3");
let a = answer("answer-3", TierId::L4(L4ModelId(0)), 0.5);
warm.record(&q, &a).unwrap();
assert_eq!(warm.len(), 3, "capacity should hold at 3");
// query-0 should be gone.
let key_0 = key_for(&text("query-0"));
assert!(warm.lookup_exact(&key_0).unwrap().is_none());
// query-3 should be there.
let key_3 = key_for(&text("query-3"));
assert!(warm.lookup_exact(&key_3).unwrap().is_some());
}
#[test]
fn warm_cache_touches_on_lookup() {
let mut warm = WarmCache::new(3);
for i in 0..3 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
warm.record(&q, &a).unwrap();
}
// Touch q-0 — should move it to MRU.
let key_0 = key_for(&text("q-0"));
warm.lookup_exact(&key_0).unwrap();
// Insert q-3 — q-1 should be evicted now (not q-0).
let q = text("q-3");
let a = answer("a-3", TierId::L4(L4ModelId(0)), 0.5);
warm.record(&q, &a).unwrap();
assert!(warm.lookup_exact(&key_0).unwrap().is_some(), "q-0 should survive (touched)");
let key_1 = key_for(&text("q-1"));
assert!(warm.lookup_exact(&key_1).unwrap().is_none(), "q-1 should have been evicted");
}
// ============================================================
// TieredMemory integration
// ============================================================
#[test]
fn tiered_memory_fills_hot_first() {
let t = TempPath::new("hot_first");
let mut m = TieredMemory::open(2, 4, t.p()).unwrap();
// Insert two items — both should land in Hot.
for i in 0..2 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
}
let (hot, warm, _) = m.tier_sizes();
assert_eq!(hot, 2);
assert_eq!(warm, 0);
}
#[test]
fn tiered_memory_demotes_overflow_from_hot_to_warm() {
let t = TempPath::new("demote_hw");
let mut m = TieredMemory::open(2, 4, t.p()).unwrap();
// Insert 4 items into a 2-capacity Hot. Two should fall through
// to Warm.
for i in 0..4 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
// Sleep 1ms-ish to ensure distinct timestamps for LRU.
}
let (hot, warm, cold) = m.tier_sizes();
assert_eq!(hot, 2, "Hot should hold 2 items");
assert_eq!(warm, 2, "Warm should hold the 2 demoted items");
assert_eq!(cold, 4, "Cold (durable) should hold all 4");
}
#[test]
fn tiered_memory_lookup_walks_hot_warm_cold() {
let t = TempPath::new("walk");
let mut m = TieredMemory::open(2, 2, t.p()).unwrap();
// Insert 6 items into a 2/2/inf tiered memory. Final state:
// Hot: q-4, q-5 (latest 2)
// Warm: q-2, q-3 (next 2 most recent)
// Cold: all 6
for i in 0..6 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
}
// Lookup q-5 — should hit Hot.
m.lookup_exact(&key_for(&text("q-5"))).unwrap();
assert_eq!(m.last_hit_tier, Some(MemoryTier::Hot));
// Lookup q-0 — should hit Cold (it's old, fell out of Hot and Warm).
m.lookup_exact(&key_for(&text("q-0"))).unwrap();
assert_eq!(m.last_hit_tier, Some(MemoryTier::Cold),
"q-0 should be in Cold only; got {:?}", m.last_hit_tier);
}
#[test]
fn tiered_memory_promotes_on_cold_hit() {
let t = TempPath::new("promote");
let mut m = TieredMemory::open(2, 2, t.p()).unwrap();
// Fill hierarchy beyond Hot+Warm capacity.
for i in 0..6 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
}
// Lookup q-0 — Cold hit.
let k0 = key_for(&text("q-0"));
let r1 = m.lookup_exact(&k0).unwrap();
assert!(r1.is_some());
assert_eq!(m.last_hit_tier, Some(MemoryTier::Cold));
// Lookup q-0 again — should now be in Hot (promoted on first access).
let r2 = m.lookup_exact(&k0).unwrap();
assert!(r2.is_some());
assert_eq!(m.last_hit_tier, Some(MemoryTier::Hot),
"after promotion q-0 should be Hot; got {:?}", m.last_hit_tier);
}
#[test]
fn tiered_memory_records_transition_joules() {
let t = TempPath::new("joules");
let mut m = TieredMemory::open(1, 1, t.p()).unwrap();
// Fill enough to force transitions.
for i in 0..3 {
let q = text(&format!("q-{}", i));
let a = answer(&format!("a-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
}
// Lookup the oldest — Cold hit, promotion to Hot.
let _ = m.lookup_exact(&key_for(&text("q-0"))).unwrap();
// total_transition_joules should be nonzero and small.
assert!(m.total_transition_joules > 0.0);
assert!(m.total_transition_joules < 1e-3,
"transition cost should be small; got {:.3e}", m.total_transition_joules);
}
#[test]
fn tiered_memory_miss_returns_none() {
let t = TempPath::new("miss");
let mut m = TieredMemory::open(2, 2, t.p()).unwrap();
let result = m.lookup_exact(&key_for(&text("never recorded"))).unwrap();
assert!(result.is_none());
assert_eq!(m.last_hit_tier, None);
}
// ============================================================
// Runtime integration: tiered memory plugged into the cascade
// ============================================================
struct MockL4 { cost: f64, response: String }
impl Tier for MockL4 {
fn id(&self) -> TierId { TierId::L4(L4ModelId(0)) }
fn estimate_cost(&self, _q: &Query) -> Option<TierEstimate> {
Some(TierEstimate {
joules: self.cost,
latency: std::time::Duration::from_millis(100),
confidence_floor: 0.9,
})
}
fn try_answer(&mut self, _q: &Query, _b: f64) -> Result<Answer, AnswerError> {
Ok(Answer {
output: AnswerOutput::Text(self.response.clone()),
tier_used: self.id(),
joules_spent: self.cost,
confidence: 0.9,
trace: ExecutionTrace::default(),
verification: jouleclaw_cascade::verification::VerificationStatus::Resolved,
})
}
}
#[test]
fn runtime_with_tiered_memory_hits_l0_on_repeat_query() {
let t = TempPath::new("runtime");
let memory = Box::new(TieredMemory::open(2, 2, t.p()).unwrap());
let mut cascade = Cascade::new();
cascade.register(Box::new(MockL4 {
cost: 0.5, response: "Paris".into(),
}));
let mut rt = Runtime::new_with_history(cascade, memory);
let q = text("capital of France?");
let a1 = rt.answer(q.clone()).unwrap();
assert_eq!(a1.tier_used, TierId::L4(L4ModelId(0)));
let a2 = rt.answer(q).unwrap();
assert_eq!(a2.tier_used, TierId::L0,
"second query should hit L0 (via tiered memory)");
assert!(a2.joules_spent < 1e-5,
"L0 hit should be cheap; got {:.3e}", a2.joules_spent);
}
#[test]
fn runtime_query_falls_through_tiers_on_repeat() {
// Spam more queries than fit in Hot+Warm. Old ones must be
// recoverable from Cold via the L0 path.
let t = TempPath::new("falls_through");
let memory = Box::new(TieredMemory::open(2, 2, t.p()).unwrap());
let mut cascade = Cascade::new();
cascade.register(Box::new(MockL4 {
cost: 0.5, response: "answer".into(),
}));
let mut rt = Runtime::new_with_history(cascade, memory);
// Send 10 distinct queries; only 4 can fit in Hot+Warm.
for i in 0..10 {
rt.answer(text(&format!("query-{}", i))).unwrap();
}
// The first query should still be retrievable via Cold.
let a = rt.answer(text("query-0")).unwrap();
assert_eq!(a.tier_used, TierId::L0,
"old query should still hit L0 via Cold tier; got {:?}", a.tier_used);
assert_eq!(a.output, AnswerOutput::Text("answer".to_string()));
}
// ============================================================
// Demo
// ============================================================
#[test]
fn r8_tiered_memory_demo() {
println!("\n=== R8: tiered memory (Hot/Warm/Cold) with joule-priced transitions ===\n");
let t = TempPath::new("demo");
let mut m = TieredMemory::open(2, 2, t.p()).unwrap();
println!("Memory hierarchy: Hot=2, Warm=2, Cold=∞ (disk)\n");
// Insert 6 items, observing the hierarchy.
for i in 0..6 {
let q = text(&format!("query-{}", i));
let a = answer(&format!("answer-{}", i), TierId::L4(L4ModelId(0)), 0.5);
m.record(&q, &a).unwrap();
}
let (hot, warm, cold) = m.tier_sizes();
println!("After 6 records:");
println!(" Hot: {} items (most recent)", hot);
println!(" Warm: {} items (recently demoted)", warm);
println!(" Cold: {} items (durable on disk)", cold);
println!(" Total transition joules so far: {:.3e} J", m.total_transition_joules);
println!("\nLookups walk Hot → Warm → Cold and promote on hit:\n");
for i in [5, 3, 0] {
let key = key_for(&text(&format!("query-{}", i)));
let _ = m.lookup_exact(&key).unwrap();
println!(" query-{}: hit {:?}", i, m.last_hit_tier.unwrap());
}
println!();
println!("After looking up query-0 (Cold hit):");
let (hot, warm, cold) = m.tier_sizes();
println!(" Hot: {} items (query-0 promoted here)", hot);
println!(" Warm: {} items", warm);
println!(" Cold: {} items", cold);
println!(" Total transition joules: {:.3e} J", m.total_transition_joules);
println!("\nKey point: hot data costs ~6 nJ; cold data costs ~1 µJ;");
println!("but a single promotion lifts a hot-accessed item back to Hot,");
println!("so repeated access converges to Hot cost.");
}