//! Harness economics on a recurring code-generation class. //! //! Scenario: an agent is repeatedly asked to declare a struct field — the kind //! of boilerplate that dominates real coding sessions. We run the same suite twice //! through a JouleClaw cascade (`SkillTier` over a frontier model stand-in) and //! print the joules-and-dollars **per verified fix**: //! //! 1. **Baseline** (compilation off): every getter re-pays the frontier //! model — the flat, token-priced curve everyone ships today. //! 2. **JouleClaw** (compilation on): once the field-declaration *class* //! recurs past a threshold it compiles into ONE deterministic skill, and //! every later field — including names the model is never asked about //! again — resolves at ~20 nJ / ~$0, deterministically correct. //! //! Run: `cargo run -p jouleclaw-harness-econ --example econ_demo` use jouleclaw_cascade::{ Cascade, ContextRef, JouleBudget, QualityFloor, Query, QueryInput, Runtime, }; use jouleclaw_harness_econ::{AnswerOutput, EconHarness, FixedModelTier, Task}; use jouleclaw_skill::{PatternInducer, SkillTier}; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Arc; /// The query the agent receives for field `name`. fn prompt(name: &str) -> String { format!("declare public string field {name}") } /// The correct declaration for field `name`. (The model's answer AND the /// verifier's oracle — `name` appears verbatim, which is what lets a pattern /// skill compile, and the artifact is brace-free so the skill round-trips.) fn getter(name: &str) -> String { format!("pub {name}: String,") } fn text_query(s: &str) -> Query { Query { input: QueryInput::Text(s.to_string()), budget: JouleBudget::expensive(), quality: QualityFloor::chat(), context: ContextRef::fresh(), deadline: None, } } fn main() { // The fields an agent gets asked for across a working session. let fields = ["user_id", "email", "created_at", "is_active", "balance_cents", "tenant_id"]; // The frontier model "knows" how to write any getter — at a frontier price. let table: Vec<(String, String)> = fields.iter().map(|f| (prompt(f), getter(f))).collect(); // One task per field; each verifies byte-exactly against the correct getter. let build_tasks = || -> Vec { fields .iter() .map(|f| { let want = getter(f); Task::text( format!("getter-{f}"), "getter", // one recurrence class — what compiles into a skill text_query(&prompt(f)), move |o| matches!(o, AnswerOutput::Text(t) if *t == want), ) }) .collect() }; let rounds = 5; let frontier_joules = 0.5; // 500 mJ per frontier dispatch // A pattern inducer that recognises the getter class and generalises it: // any "generate getter for field {name}" compiles to one Format skill. let make_inducer = || Box::new(PatternInducer::from_patterns(["declare public string field {name}"])); // ---- 1. Baseline: skill-compilation OFF ------------------------------- let base_calls = Arc::new(AtomicU32::new(0)); let bc = base_calls.clone(); let bt = table.clone(); let baseline = EconHarness::new(make_inducer(), 3) .without_compilation() .run( move |store| { let mut casc = Cascade::new(); casc.register(Box::new(SkillTier::new(store))); casc.register(Box::new( FixedModelTier::new(bt.clone(), frontier_joules) .with_call_counter(bc.clone()), )); Runtime::new_without_l0(casc) }, &build_tasks(), rounds, ); // ---- 2. JouleClaw: skill-compilation ON ------------------------------- let jc_calls = Arc::new(AtomicU32::new(0)); let jc = jc_calls.clone(); let jt = table.clone(); let jouleclaw = EconHarness::new(make_inducer(), 3).run( move |store| { let mut casc = Cascade::new(); casc.register(Box::new(SkillTier::new(store))); casc.register(Box::new( FixedModelTier::new(jt.clone(), frontier_joules).with_call_counter(jc.clone()), )); Runtime::new_without_l0(casc) }, &build_tasks(), rounds, ); let total_dispatches = fields.len() * rounds; println!("\n=== Recurring field-declaration codegen — {total_dispatches} dispatches ({} fields × {rounds} rounds) ===\n", fields.len()); println!("--- BASELINE (token-priced, no skill-compilation) ---"); print!("{}", baseline.to_table()); println!("model calls paid: {}/{total_dispatches}\n", base_calls.load(Ordering::SeqCst)); println!("--- JOULECLAW (skill-compilation on) ---"); print!("{}", jouleclaw.to_table()); println!("model calls paid: {}/{total_dispatches}\n", jc_calls.load(Ordering::SeqCst)); // ---- Headline --------------------------------------------------------- let usd_drop = baseline.usd_per_verified_fix / jouleclaw.usd_per_verified_fix; let j_drop = baseline.joules_per_verified_fix / jouleclaw.joules_per_verified_fix; println!("=== Headline (both verified {}/{}) ===", jouleclaw.verified, total_dispatches); println!( " $/verified fix : ${:.6} -> ${:.9} ({:.0}× cheaper)", baseline.usd_per_verified_fix, jouleclaw.usd_per_verified_fix, usd_drop ); println!( " J/verified fix : {:.3e} -> {:.3e} ({:.0}× cheaper)", baseline.joules_per_verified_fix, jouleclaw.joules_per_verified_fix, j_drop ); println!( " model calls : {} -> {} (the recurring class stopped invoking the model)", base_calls.load(Ordering::SeqCst), jc_calls.load(Ordering::SeqCst) ); println!( "\nEvery verified fix is byte-identical across both runs — JouleClaw just stopped\n\ paying the model for work it had already proven how to do." ); }