From e078a53f29abf2871c3d60280a54b11a01e6791e Mon Sep 17 00:00:00 2001 From: Transaction Science Date: Wed, 21 Oct 2026 07:31:42 -0400 Subject: [PATCH] jouleclaw-routing: ROUTING_JOULES is an Estimator-class floor, and says so MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `RoutingPlan::router_joules` is "counted against the query budget" — it is a **debit** — and it is a bare `f64` carrying no provenance, so a figure a router measured and a hard-coded constant are indistinguishable in the type. `ROUTING_JOULES = 8e-9` was documented as "the (negligible) joule cost ... client CPU only and ... dwarfed by every execution tier it orders", which is an assumption nothing enforces. Two concrete ways it fails, and because the figure is a debit both fail in the direction `jouleclaw/SPEC.md` §1.3 calls unsafe — under-charging a ceiling admits further work on the strength of the shortfall: 1. **It does not scale with the work.** `LearnedRouter`'s `route` tokenizes the query and scans its episode memory for neighbours — work proportional to the number of episodes — and charges the flat constant. Nothing bounds that memory, so the gap grows silently with it. This is an actual path in the crate, not a hypothetical. 2. **A router that runs a model must not use it.** A classifier or an LLM choosing a route costs orders more, and charging the constant would report a model inference as picojoules. The constant and the field now state that: an `Estimator`-class declared floor, with a router whose routing cost scales with its own state, or that runs a model, required to report what it actually spent. **What this does not do, and why.** It does not make the charge scale. Doing so needs a calibrated per-episode cost, and inventing a coefficient is exactly what this family forbids — `jouleclaw/SPEC.md` already says a constant nobody calibrated there is `Estimator`, however it is multiplied. Multiplying this constant by the episode count would assert that one episode costs what a whole plan costs, which nothing measured. The enforceable follow-up is named rather than implied: give `RoutingPlan` a provenance field, so a measured figure and this floor stop being the same type. That is an API change with consumers, not a doc fix. Also fixes one of my own intra-doc links before it shipped: `route` is a trait method, so `[`LearnedRouter::route`]` does not resolve on the type. `jouleclaw-routing` is at 0 `cargo doc` warnings. Not touched: `jouleclaw-cascade` has 2 pre-existing doc warnings (`jouleclaw_calibration::CalibratedGate`, `top`) and these two crates carry 10 pre-existing clippy findings, all in files this change does not touch (`active.rs`, `body.rs`, `coord_router_impl.rs`, `cost.rs`, `tier.rs`). Neither crate is in CI's clippy list, so that is unlinted code I can only half-see from one host; reported rather than rewritten. Verified: workspace check 0 errors, 486 test binaries green, 0 failures; `jouleclaw-routing` doc clean; no clippy finding in either edited file. Co-Authored-By: Claude Opus 5 --- .../crates/jouleclaw-cascade/src/router.rs | 10 +++++++ .../crates/jouleclaw-routing/src/lib.rs | 30 +++++++++++++++++-- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/jouleclaw/jouleclaw-rs/crates/jouleclaw-cascade/src/router.rs b/jouleclaw/jouleclaw-rs/crates/jouleclaw-cascade/src/router.rs index 0e6fb96eb..cf24ce879 100644 --- a/jouleclaw/jouleclaw-rs/crates/jouleclaw-cascade/src/router.rs +++ b/jouleclaw/jouleclaw-rs/crates/jouleclaw-cascade/src/router.rs @@ -55,6 +55,16 @@ pub struct RoutingPlan { /// Joules spent producing this plan. Counted against the query /// budget. + /// + /// **Ungraded.** This is a bare `f64`, so a figure a router measured and a + /// declared constant are indistinguishable here — and because it is a + /// debit, a figure below the work actually done under-charges the budget, + /// which is the unsafe direction at a ceiling (`jouleclaw/SPEC.md` §1.3). + /// A router whose routing cost scales with its own state, or that runs a + /// model to choose a route, must report what it spent rather than a + /// constant; see `jouleclaw_routing::ROUTING_JOULES` for why the flat + /// constant is a floor. Carrying a provenance beside this field is what + /// would let a verifier tell the two apart. pub router_joules: f64, /// Human-readable explanation of WHY this ordering. Surfaces in diff --git a/jouleclaw/jouleclaw-rs/crates/jouleclaw-routing/src/lib.rs b/jouleclaw/jouleclaw-rs/crates/jouleclaw-routing/src/lib.rs index acb1a2a30..8f213ade9 100644 --- a/jouleclaw/jouleclaw-rs/crates/jouleclaw-routing/src/lib.rs +++ b/jouleclaw/jouleclaw-rs/crates/jouleclaw-routing/src/lib.rs @@ -43,7 +43,31 @@ pub use privacy::{ SensitivityLevel, TierSensitivityPolicy, PRIVACY_FILTER_JOULES, }; -/// The (negligible) joule cost charged for producing a routing plan. -/// L5 lives in the meta-cognitive control plane; its energy is client -/// CPU only and is dwarfed by every execution tier it orders. +/// The joule cost charged for producing a routing plan: an **`Estimator`-class +/// declared floor**, not a measurement. +/// +/// L5 lives in the meta-cognitive control plane, and for a table-driven or +/// heuristic router its energy really is client CPU only and dwarfed by every +/// execution tier it orders. Two things keep that from being the whole story, +/// and both matter because [`RoutingPlan::router_joules`] is **counted against +/// the query budget**, so a charge below the work done under-debits it — the +/// unsafe direction at a ceiling (`jouleclaw/SPEC.md` §1.3). +/// +/// 1. **It does not scale with the work.** [`LearnedRouter`]'s `route` tokenizes +/// the query and scans its episode memory for neighbours, which is work +/// proportional to the number of episodes; this constant is flat. Nothing +/// bounds that memory, so the gap grows silently with it. +/// 2. **A router that runs a model must not use it.** A classifier or an LLM +/// choosing a route costs orders more than this, and charging the constant +/// would report a model inference as picojoules. +/// +/// A router whose routing cost scales with its own state, or that runs a +/// model, **MUST** report what it actually spent rather than this constant. +/// Nothing enforces that today: `router_joules` is a bare `f64` and carries no +/// provenance, so a measured figure and this floor are indistinguishable in +/// the type. Giving [`RoutingPlan`] a provenance field is what would make the +/// distinction checkable instead of documented. +/// +/// [`RoutingPlan`]: jouleclaw_cascade::router::RoutingPlan +/// [`RoutingPlan::router_joules`]: jouleclaw_cascade::router::RoutingPlan::router_joules pub const ROUTING_JOULES: f64 = 8e-9; -- 2.54.0 (Apple Git-157)