jouleclaw: a routing plan carries the class of its energy figure #418
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/routing-plan-carries-its-class"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
#390 said the enforceable fix for
router_jouleswas to giveRoutingPlana provenance, so a declared constant stops sharing a type with a measurement. This is that change.RoutingPlangainsrouter_energy_class: Provenance. Every router in this workspace setsEstimator, because every one charges a constant or a sum of constants — nothing in routing is measured today, and the type now says so rather than leaving a reader to discover it.It matters because the figure is debited:
tier.rsdoesspent += plan.router_joules. A consumer that cannot tell a measurement from a declared constant cannot tell whether the budget it is enforcing means anything.Alternatives rejected on the merits
JouleClassis a tier taxonomy (Cache/Lawful/Embed/Model/Wire) — not a grade.Provenanceintojouleclaw-coreis the tidier home, but it has 61 importers; churning them to serve two crates isn't proportionate.So cascade and routing depend on
jouleclaw-energywithdefault-features = false. Every hardware backend there is already opt-in, so this adds the type and no meter.The magnitude is unchanged, deliberately
ROUTING_JOULES = 8e-9J is 0.008 µJ:EnergyReadingisu64µJ, and "floating-point is forbidden in the protocol for determinism" — so it rounds to zero there;jouleclaw-energyitself calls realistic for anEstimatorsource.The significand is unjustified. Correcting it needs a measurement — substituting a different invented number would repeat the error this change exists to label. What the change does is make the class legible so no consumer reads 8 nJ as measured.
Falsification found two real gaps in my own tests
Mutating
LearnedRouter's andPhasorRouter's ownRoutingPlanliterals changed nothing. The test exercisedRoutingPlan::fallbackfor both routers, so their own constructions were never reached and could have claimedHwShuntfreely. Fixed by driving each past cold start withrecord_outcomeand asserting!plan.is_fallback()first, so the test cannot silently slide back onto the path that pins nothing.Mutating
PrivacyRouter's composition to take the inner plan's class was invisible. With every shipped router onEstimator,inner.router_energy_classandProvenance::Estimatorread identically — the mutation was undetectable by construction. Separating them needed a stub inner router claimingHwShunt, which pins the actual rule: a sum containing an unmeasured constant is an estimate, however good the measured term was.fallback()claimsHwShunta_fallback_plan_is_estimator_classFAILEDLearnedRouter's literal claimsModelBasedevery_shipped_router_declares_an_estimator_classFAILEDPhasorRouter's literal claimsHwShuntevery_shipped_router_declares_an_estimator_classFAILEDa_constant_added_to_a_measured_figure_degrades_the_classFAILEDVerification
cargo check --workspace --all-targets --locked(jouleclaw-rs,RUSTFLAGS=-C target-feature=+fp16) — 0 errorscargo test --workspace --locked— 486 test binaries green, 0 failuresjoule-code/examples/l3-jouleclawcheck — 0 errorscheck-lockfiles— 47 lockfiles current; both workspaces naming these crates were refreshed (jouleclaw-rsandl3-jouleclaw— found by greppingCargo.lock, notCargo.toml, since the second only appears in the lock)The 10 clippy findings in these crates are pre-existing, unchanged in count, and all in lines this change did not author (
coord_router_impl.rs:241shifted to:242behind an inserted line). Neither crate is in CI's clippy list.🤖 Generated with Claude Code