jcp: a chain is bounded in money on every link, and a delegated grant is never decided alone #304
Merged
dcharlot
merged 1 commit from 2026-10-17 17:03:10 -04:00
fix/jcp-chained-money into main 2026-10-17
jcp: a chain is bounded in money on every link, and a delegated grant is never decided alone
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 6s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m1s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m13s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m32s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m14s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m19s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 6m18s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m4s
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 3m53s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 5m19s
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 9m23s
Two gaps in how JCP decides a delegated grant. A chain could not be priced. The single-grant path has `decide_priced`, which enforces the money ceiling beside the joule ceiling (§4.8). Chains had no priced decision at all, so a delegated grant's money ceiling was never consulted: not the leaf's, and not the parent's whose budget the leaf draws on. A delegated grant could be decided on its own. `decide`, `decide_priced`, `authorize` and the flow variants accepted a grant with a `parent`, decided it as a root, and never saw the chain. §4.2 says a delegated grant MUST be decided with its full chain. Alone, nothing shows it narrows what its issuer held, so its budget, caveats and expiry were taken at their word. - `decide_chained_priced` applies both ceilings to every link. Joules are checked first on every link, then the money ceiling of every link that has one, refusing a foreign currency. An allow debits both dimensions from every link, so siblings share their parent's money as they share its joules. A deny debits neither. A chain with no money ceiling ignores the price. - The single-grant checks deny a grant with a `parent` `chain-broken`. Nothing in jcp-rs or its consumers relied on the old behaviour. - SPEC §4.2 states both rules, and C22 covers a priced chain and a delegated grant decided alone. Checked on the build host: - jcp-rs: 504 tests pass, three of them new, and the conformance suite passes all checks. - Every vector is byte-identical. - jouleclaw-jcp, joule-code-jcp, sandbox-jcp, mesh-core and joule-ui-interaction pass their tests. - Mutations each fail tests and C22: allowing a lone delegated grant, skipping the money check on chains, and debiting money on the leaf only (that one fails tests). Co-Authored-By: Claude Opus 5.5 <[email protected]>