jcp: a chained decision enforces caveats, and a chain cannot drop one #300
Merged
dcharlot
merged 1 commit from 2026-10-17 16:03:08 -04:00
fix/jcp-chained-caveats into main 2026-10-17
jcp: a chained decision enforces caveats, and a chain cannot drop one
All checks were successful
standards workspaces build + test / which workspaces this change touches (pull_request) Successful in 9s
repository checks / lockfiles, tests in CI, workflow hygiene, spec and section references, public text (pull_request) Successful in 1m21s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m57s
standards workspaces build + test / jouledesk-rs (jouledesk/jouledesk-rs, jouledesk-rs, macos) (pull_request) Successful in 1m21s
standards workspaces build + test / jcp-rs (jcp-conformance, jcp/jcp-rs, -p jcp-energy --features nvml, jcp-rs, linux) (pull_request) Successful in 1m34s
standards workspaces build + test / joule-ui-rs (joule-ui/joule-ui-rs, joule-ui-rs, linux) (pull_request) Successful in 1m9s
standards workspaces build + test / joulehook-rs (joulehook/joulehook-rs, joulehook-rs, linux) (pull_request) Successful in 1m11s
standards workspaces build + test / sandbox-rs (sandbox-conformance, sandbox/sandbox-rs, sandbox-rs, linux) (pull_request) Successful in 2m36s
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 1m9s
standards workspaces build + test / joule-code-rs (joule-code/joule-code-rs, joule-code-rs, linux-arm64, true) (pull_request) Successful in 2m1s
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 3m13s
`decide_chained` evaluated no caveat at all: not the leaf's, not any ancestor's. It is the decision path behind jcp-wire's endpoint (§11), jcp-channel, jouleclaw-jcp and joule-code-jcp. Those always present a chain, even a one-link chain for an undelegated grant. So `PayloadIs`, `NotBefore`, `NotAfter`, `CallerIs`, `ActionIn`, `MaxCostUj` and `ModelIn` were enforced only on the single-grant `decide`/`authorize` path, which the conformance suite exercises. Separately, `verify_chain` checked capability, budget, expiry and depth at each hop, but not that a child carries its parent's caveats or no wider money ceiling. `attenuate` refuses both when it mints. A chain minted by hand verified anyway and conveyed the authority the caveat withheld. - `verify_chain` refuses a hop that drops a parent caveat (`caveats_narrow`) or widens or drops its money ceiling (`funds_narrows`). Both are denied `attenuation-violation`. - The chained decision's checks move into `check_chained`, which now evaluates every link's caveats. Cost-dependent ones are evaluated when the cost is known. - `authorize_chained` / `charge_chained` add the two-phase metered path for a chain. Authorize debits nothing and reports the least any link has left. Charge debits every link, so siblings share their parent as in `decide_chained`, and a per-call breach is reported with the joules still debited. - SPEC §4.2 states both rules and the chained metered path. C7's conformance check covers an inherited caveat and a dropped one. Checked on the build host: - jcp-rs: 501 tests pass, and the conformance suite passes all checks. - Every vector gen-vectors writes is byte-identical. - jouleclaw-jcp, joule-code-jcp and sandbox-jcp pass their tests. - mesh, joule-ui, joulehook and jouledesk compile. - Mutations each fail tests: chains ignoring caveats, `verify_chain` allowing a dropped caveat, and a charge debiting only the leaf. Co-Authored-By: Claude Opus 5.5 <[email protected]>