jouleclaw-routing: ROUTING_JOULES is an Estimator-class floor, and says so #390

Merged
dcharlot merged 1 commit from fix/routing-joules-is-an-estimator into main 2026-10-21 10:19:46 -04:00

2026-10-21

Transaction Science
jouleclaw-routing: ROUTING_JOULES is an Estimator-class floor, and says so
All checks were successful
jouleclaw public surface must stay documented / Public-surface audit (jouleclaw-rs) (pull_request) Successful in 1m8s
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 5s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 48s
standards workspaces build + test / mesh-rs (mesh/mesh-rs, -p mesh-engine-gguf -p mesh-openai --features mesh-engine-gguf/gguf,mesh-openai/server, mesh-rs, linux-arm64) (pull_request) Successful in 59s
standards workspaces build + test / jouleclaw-rs (-nodebug, jouleclaw/jouleclaw-rs, -p jouleclaw-energy --features rapl,nvml, jouleclaw-rs, linux-arm64, -C target-feature=+fp16) (pull_request) Successful in 2m26s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m33s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 2m36s
`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 <[email protected]>
2026-10-21 07:31:42 -04:00