The joule ceiling is JCP's flagship enforcement, and the spec's contract is that
every decision is sealed, allow and deny alike. dispatch() did not honour that.
route() pre-filtered out any harness whose remaining budget could not cover the
task's quote, so an over-budget harness never reached runtime.decide() -- the
ceiling fired, the loop failed over, and no receipt recorded that it had. The
enforcement happened; the audit of it did not.
Replace route() with candidates(), which filters on capability only. dispatch()
now offers the task to each granted harness in turn, letting the runtime make and
seal the decision -- so a harness that cannot pay is denied on the record before
the loop fails over to one that can. Denied dispatches are still not debited
(Runtime::decide only debits on allow), so per-harness joule totals are unchanged.
One refusal deliberately seals nothing: a task whose kind no registered harness
holds. There is no grant to decide against, and JCP seals decisions about a
grant, not the absence of one. It is recorded denied in the RunReport instead.
Module doc and MAPPING.md now state both cases precisely rather than claiming a
blanket allow-and-deny seal.
Two regression tests: budget_denials_are_sealed_not_silently_skipped (the deny
lands in the ledger, verifies under the steward key, and is not debited) and
ungranted_kind_seals_nothing. All six pre-existing tests pass unchanged. The
background_agents example now shows 7 sealed decisions, up from 6 -- the seventh
being the budget denial that used to disappear.
Co-Authored-By: Claude Opus 4.8 <[email protected]>