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
Owner

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". That is an assumption nothing enforces, and it fails two concrete ways. 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 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. That 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.

So 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.

Verification

  • cargo check --workspace --all-targets --locked (jouleclaw-rs, RUSTFLAGS=-C target-feature=+fp16) — 0 errors
  • cargo test --workspace --locked — 486 test binaries green, 0 failures
  • cargo doc --no-deps -p jouleclaw-routing — 0 warnings
  • No clippy finding in either edited file

I also caught one of my own intra-doc links before it shipped: route is a trait method, so [LearnedRouter::route] does not resolve on the type. Given that two of my recent PRs were about broken doc links, shipping one here would have been poor form.

Not touched

jouleclaw-cascade carries 2 pre-existing doc warnings (jouleclaw_calibration::CalibratedGate, top), and these two crates have 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, on the same reasoning as #353 and #385.

🤖 Generated with Claude Code

`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"*. That is an assumption nothing enforces, and it fails two concrete ways. 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 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.** That 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. So 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. ## Verification - `cargo check --workspace --all-targets --locked` (`jouleclaw-rs`, `RUSTFLAGS=-C target-feature=+fp16`) — **0** errors - `cargo test --workspace --locked` — **486** test binaries green, **0** failures - `cargo doc --no-deps -p jouleclaw-routing` — **0** warnings - No clippy finding in either edited file I also caught one of my own intra-doc links before it shipped: `route` is a trait method, so `[`LearnedRouter::route`]` does not resolve on the type. Given that two of my recent PRs were *about* broken doc links, shipping one here would have been poor form. ## Not touched `jouleclaw-cascade` carries 2 pre-existing doc warnings (`jouleclaw_calibration::CalibratedGate`, `top`), and these two crates have 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, on the same reasoning as #353 and #385. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
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
e078a53f29
`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]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!390
No description provided.