open-standards/jouleclaw/jouleclaw-rs/crates/jouleclaw-cascade/tests/r7_budget.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

382 lines
13 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! R7 tests — budget enforcement and tier calibration.
//!
//! What R7 changes:
//! 1. Tiers that overrun their budget-cap produce `BudgetExhausted`
//! rather than silently consuming the overage.
//! 2. Every tier dispatch records `(estimate, actual)` for
//! calibration.
//! 3. `Runtime::calibration()` exposes the report.
use jouleclaw_cascade::*;
use std::time::Duration;
fn text(s: &str) -> Query {
Query {
input: QueryInput::Text(s.to_string()),
budget: JouleBudget::standard(),
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
}
}
// ============================================================
// Mock tiers with controllable cost behavior
// ============================================================
/// A tier that estimates one cost but consumes a configurable other
/// cost. Used to test budget-overrun handling and calibration recording.
struct OvershootingTier {
id: TierId,
estimated_joules: f64,
actual_joules: f64,
}
impl Tier for OvershootingTier {
fn id(&self) -> TierId { self.id }
fn estimate_cost(&self, _q: &Query) -> Option<TierEstimate> {
Some(TierEstimate {
joules: self.estimated_joules,
latency: Duration::from_micros(10),
confidence_floor: 0.9,
})
}
fn try_answer(&mut self, _q: &Query, _b: f64) -> Result<Answer, AnswerError> {
Ok(Answer {
output: AnswerOutput::Text("done".into()),
tier_used: self.id,
joules_spent: self.actual_joules,
confidence: 0.9,
trace: ExecutionTrace::default(),
verification: crate::verification::VerificationStatus::Resolved,
})
}
}
struct CheapHonestTier {
id: TierId,
cost: f64,
text: String,
}
impl Tier for CheapHonestTier {
fn id(&self) -> TierId { self.id }
fn estimate_cost(&self, _q: &Query) -> Option<TierEstimate> {
Some(TierEstimate {
joules: self.cost,
latency: Duration::from_micros(1),
confidence_floor: 1.0,
})
}
fn try_answer(&mut self, _q: &Query, _b: f64) -> Result<Answer, AnswerError> {
Ok(Answer {
output: AnswerOutput::Text(self.text.clone()),
tier_used: self.id,
joules_spent: self.cost,
confidence: 1.0,
trace: ExecutionTrace::default(),
verification: crate::verification::VerificationStatus::Resolved,
})
}
}
// ============================================================
// Calibration recording
// ============================================================
#[test]
fn calibration_records_estimate_and_actual() {
let mut cascade = Cascade::new();
cascade.register(Box::new(CheapHonestTier {
id: TierId::L1(L1Primitive::Execute),
cost: 1e-7, text: "ok".into(),
}));
let mut rt = Runtime::new(cascade);
let _ = rt.answer(text("test")).unwrap();
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L1(L1Primitive::Execute)).unwrap();
assert_eq!(entry.samples, 1);
assert_eq!(entry.total_estimated, 1e-7);
assert_eq!(entry.total_actual, 1e-7);
assert!((entry.mean_ratio() - 1.0).abs() < 1e-9,
"honest tier should have mean ratio 1.0; got {}", entry.mean_ratio());
}
#[test]
fn calibration_aggregates_over_many_calls() {
let mut cascade = Cascade::new();
cascade.register(Box::new(CheapHonestTier {
id: TierId::L1(L1Primitive::Execute),
cost: 1e-7, text: "ok".into(),
}));
let mut rt = Runtime::new(cascade);
for i in 0..20 {
rt.answer(text(&format!("q{}", i))).unwrap();
}
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L1(L1Primitive::Execute)).unwrap();
assert_eq!(entry.samples, 20);
assert!((entry.mean_ratio() - 1.0).abs() < 1e-9);
}
#[test]
fn calibration_detects_underestimating_tier() {
// A tier that says it'll cost 1 nJ but actually spends 1 mJ. Wildly
// dishonest — the budget should give 1 J standard, so the tier
// still fits, but calibration should flag it.
let mut cascade = Cascade::new();
cascade.register(Box::new(OvershootingTier {
id: TierId::L4(L4ModelId(0)),
estimated_joules: 1e-9,
actual_joules: 1e-3,
}));
let mut rt = Runtime::new(cascade);
for _ in 0..10 {
let q = Query {
input: QueryInput::Text(format!("q-{}", rand_str())),
budget: JouleBudget::standard(),
quality: QualityFloor::chat(),
context: ContextRef::fresh(),
deadline: None,
};
let _ = rt.answer(q);
}
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L4(L4ModelId(0))).unwrap();
assert!(entry.mean_ratio() > 100.0,
"underestimating tier should have mean_ratio >> 1; got {}",
entry.mean_ratio());
let dishonest = cal.dishonest_tiers(0.2);
assert!(!dishonest.is_empty());
}
fn rand_str() -> String {
// Deterministic but distinct strings to avoid L0 caching.
use std::sync::atomic::{AtomicU64, Ordering};
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::SeqCst);
format!("test-query-{}", n)
}
// ============================================================
// Budget enforcement
// ============================================================
#[test]
fn budget_overshoot_triggers_budget_exhausted_error() {
// Tier estimates 1 µJ, actually spends 1 J — way over the query's
// 1 mJ budget. The runtime should catch this and return
// BudgetExhausted.
let mut cascade = Cascade::new();
cascade.register(Box::new(OvershootingTier {
id: TierId::L4(L4ModelId(0)),
estimated_joules: 1e-6, // says it's cheap
actual_joules: 1.0, // actually expensive
}));
let mut rt = Runtime::new(cascade);
let q = Query {
input: QueryInput::Text("test".to_string()),
budget: JouleBudget { hard_limit: 1e-3, soft_target: 1e-4 }, // 1 mJ
quality: QualityFloor::chat(),
context: ContextRef::fresh(),
deadline: None,
};
let result = rt.answer(q);
assert!(matches!(result, Err(AnswerError::BudgetExhausted { .. })),
"tier overrun should produce BudgetExhausted; got {:?}", result);
// Calibration should have recorded a violation.
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L4(L4ModelId(0))).unwrap();
assert_eq!(entry.budget_violations, 1);
}
#[test]
fn tier_within_budget_still_works() {
// Tier estimates 1 µJ, actually spends 1 µJ. Budget is 1 mJ. Fine.
let mut cascade = Cascade::new();
cascade.register(Box::new(OvershootingTier {
id: TierId::L1(L1Primitive::Execute),
estimated_joules: 1e-6,
actual_joules: 1e-6,
}));
let mut rt = Runtime::new(cascade);
let q = Query {
input: QueryInput::Text("test".to_string()),
budget: JouleBudget { hard_limit: 1e-3, soft_target: 1e-4 },
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
};
let result = rt.answer(q).unwrap();
assert_eq!(result.output, AnswerOutput::Text("done".into()));
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L1(L1Primitive::Execute)).unwrap();
assert_eq!(entry.budget_violations, 0);
}
#[test]
fn small_overshoot_within_budget_is_allowed() {
// Tier estimates 1 µJ, actually spends 10 µJ (10× over estimate).
// Budget is 1 mJ — so it still fits comfortably. The runtime
// records calibration drift but doesn't fail the query.
let mut cascade = Cascade::new();
cascade.register(Box::new(OvershootingTier {
id: TierId::L1(L1Primitive::Execute),
estimated_joules: 1e-6,
actual_joules: 1e-5,
}));
let mut rt = Runtime::new(cascade);
let q = Query {
input: QueryInput::Text("test".to_string()),
budget: JouleBudget { hard_limit: 1e-3, soft_target: 1e-4 },
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
};
let result = rt.answer(q).unwrap();
assert_eq!(result.output, AnswerOutput::Text("done".into()));
let cal = rt.calibration();
let entry = cal.per_tier.get(&TierId::L1(L1Primitive::Execute)).unwrap();
assert!(entry.mean_ratio() > 5.0); // 10x ratio
assert_eq!(entry.budget_violations, 0);
}
#[test]
fn cumulative_budget_exhausted_after_walking_failed_tiers() {
// Each tier in turn spends almost the full budget, but refuses.
// After two refusals the budget is exhausted; the third tier
// should be skipped.
let mut cascade = Cascade::new();
struct ExpensiveRefuser { id: TierId, cost: f64 }
impl Tier for ExpensiveRefuser {
fn id(&self) -> TierId { self.id }
fn estimate_cost(&self, _q: &Query) -> Option<TierEstimate> {
Some(TierEstimate {
joules: self.cost, latency: Duration::from_micros(1),
confidence_floor: 1.0,
})
}
fn try_answer(&mut self, _q: &Query, _b: f64) -> Result<Answer, AnswerError> {
Ok(Answer {
output: AnswerOutput::Refused(RefusalReason::TierSpecific("nope".into())),
tier_used: self.id, joules_spent: self.cost,
confidence: 0.0, trace: ExecutionTrace::default(),
verification: crate::verification::VerificationStatus::Resolved,
})
}
}
cascade.register(Box::new(ExpensiveRefuser {
id: TierId::L1(L1Primitive::Execute), cost: 0.4,
}));
cascade.register(Box::new(ExpensiveRefuser {
id: TierId::L1(L1Primitive::Regex), cost: 0.4,
}));
cascade.register(Box::new(ExpensiveRefuser {
id: TierId::L1(L1Primitive::TemplateFill), cost: 0.4,
}));
let mut rt = Runtime::new(cascade);
let q = Query {
input: QueryInput::Text("test".to_string()),
budget: JouleBudget { hard_limit: 1.0, soft_target: 0.5 },
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
};
let result = rt.answer(q);
// After the first two refusals (spending 0.8 J), the third tier's
// 0.4 J estimate exceeds the remaining 0.2 J budget. The runtime
// should skip it and return NoTierSatisfied (refusals) or
// BudgetExhausted (if it interprets the unaffordable third as
// budget exhaustion).
match result {
Err(AnswerError::NoTierSatisfied { refusals }) => {
// Two refusals recorded; third was skipped.
assert_eq!(refusals.len(), 2);
}
Err(AnswerError::BudgetExhausted { .. }) => {
// Acceptable alternative outcome.
}
other => panic!("expected refusals/budget; got {:?}", other),
}
}
// ============================================================
// Demo
// ============================================================
#[test]
fn r7_calibration_demo() {
println!("\n=== R7: budget enforcement + tier calibration ===\n");
// Each tier here only fires when its specific cascade is active.
// We'll run three separate runtimes, one per tier-honesty case.
fn run_tier(label: &str, est: f64, actual: f64, tid: TierId, budget: JouleBudget) -> CalibrationReport {
let mut cascade = Cascade::new();
cascade.register(Box::new(OvershootingTier {
id: tid, estimated_joules: est, actual_joules: actual,
}));
let mut rt = Runtime::new(cascade);
for i in 0..20 {
let q = Query {
input: QueryInput::Text(format!("{}-q{}", label, i)),
budget,
quality: QualityFloor::any(),
context: ContextRef::fresh(),
deadline: None,
};
let _ = rt.answer(q);
}
rt.calibration().clone()
}
let exec_cal = run_tier(
"exec", 1e-6, 1e-6, TierId::L1(L1Primitive::Execute),
JouleBudget::standard());
let regex_cal = run_tier(
"regex", 1e-6, 5e-6, TierId::L1(L1Primitive::Regex),
JouleBudget::standard());
let l4_cal = run_tier(
"l4", 1.0, 1e-2, TierId::L4(L4ModelId(0)),
JouleBudget::expensive());
// Combine summaries for printing.
let mut combined = CalibrationReport::default();
for (tid, e) in exec_cal.per_tier { combined.per_tier.insert(tid, e); }
for (tid, e) in regex_cal.per_tier { combined.per_tier.insert(tid, e); }
for (tid, e) in l4_cal.per_tier { combined.per_tier.insert(tid, e); }
println!("Calibration report after 20 queries per tier:");
for line in combined.summary() {
println!(" {}", line);
}
println!();
let dishonest = combined.dishonest_tiers(0.2);
println!("Tiers with mean_ratio drifting >20% from 1.0:");
for (tid, ratio) in &dishonest {
let direction = if *ratio > 1.0 { "UNDERESTIMATING" } else { "OVERESTIMATING" };
println!(" {:<25} ratio={:.3} ({})",
format!("{:?}", tid), ratio, direction);
}
println!();
println!("L1::Execute is honest — keep it as is.");
println!("L1::Regex understates 5× — bump its c_per_byte constant up.");
println!("L4 overstates ~100× — its cost model is too conservative.");
println!();
println!("This data feeds future per-tier cost-model recalibration.");
println!("A tier whose estimates drift triggers a calibration cycle");
println!("that updates the underlying constants.");
}