jcp §4.7: an amendment may only tighten against what the grant has LEFT #165

Merged
dcharlot merged 1 commit from feat/checkpoint-energy into main 2026-10-08 19:55:19 -04:00
Owner

The hole

jcp-grant's checkpoint module promises, in its own words, that "an amendment
may only tighten … so steering can never escalate a running agent's authority."

Both amend and verify_amendment enforced that with:

if new_budget_uj > grant.grant.budget_uj {
    return Err(CheckpointError::Amplifies("amended budget exceeds the grant"));
}

That compares against the budget as issued. On a 1 000 µJ grant whose run
has already spent 900, an amendment of 1 000 passes as a "tightening" and hands
the run ten times its remainder. The steering mechanism built to re-bound a long
run could re-fund it instead.

The sweep that found this framed it as "Checkpoint carries no energy field",
which understates it. The missing field is why the check could not be written
correctly: there was nothing for it to measure against.

What changed

Checkpoint gains spent: Option<Spend> — a cumulative microjoule figure with
its §5 provenance grade. Cumulative rather than per-interval, so one checkpoint
answers "how much is left" on its own, without the verifier holding the whole
chain and without a missing link silently understating the total.

remaining_uj(grant, cp) derives the remainder, and returns None when the
checkpoint is silent. Unknown is not zero, and conflating them is exactly how an
amendment hands back a whole grant.

The ceiling is now the remainder where the checkpoint states its spend, and the
issued budget where it does not. The fallback is deliberately the old, weaker
rule rather than a refusal:
a checkpoint written before the field existed is
still a valid checkpoint, and rejecting it would break running deployments in
order to fix a hole those checkpoints cannot express. The guarantee is only as
strong as the checkpoint — which remaining_uj returning None is how a
verifier finds out.

Enforced on both sides. amend refusing is not enough: a steward can sign an
over-budget amendment with its own key and hand it to an enforcement point,
which never calls amend.

What the figure is actually worth

Stated in the module docs rather than left implied: the checkpoint is authored
and signed by the agent — the party the remainder constrains — so its
declared spend is a claim, not an independent measurement. The provenance
grade travels with it so the amender can see which kind of claim it is. Two
things make the claim hard to shrink: it is signed, so understating it is
attributable, and it is cumulative, so the new checkpoint_follows(prev, next)
refuses a successor that declares less than its predecessor or drops a field it
once carried — otherwise an agent recovers its full issued ceiling simply by
going quiet. What this layer does not do is independently meter the run.

The wire change is additive, and that is checked

spent: None is skipped, so Checkpoint::id() and every sealed vector are
byte-identical to before the field existed. Regenerating the pack is the proof:
standing.json gains one entry and no published id or signature moves,
checkpoint included.

skip_serializing_if turned out to be load-bearing rather than tidy — the JCR-1
signed core forbids nulls, so removing it makes Checkpoint::id() fail outright
with DisallowedType("null") on every checkpoint that states no spend. Omission
is the only encoding the canonical form permits, and the field's doc comment now
says so where an editor would see it.

The new checkpoint_with_spend vector publishes the issued budget beside the
spend so a third party can redo the arithmetic, and its id necessarily differs
from checkpoint's — which is what makes the additivity claim non-vacuous. The
remainder rule itself is a refusal, and a refusal has nothing to sign, so it is
pinned by C21 and unit tests rather than by a vector.

Spec

§4.7 and C21 now make the remainder normative: both sides MUST enforce it, an
absent spend MUST NOT be read as zero, and a declared spend MUST NOT decrease.

Verification

cargo test --workspace in jcp/jcp-rs: 407 passed, 0 failed. No new clippy
warnings (the one jcp-schema warning predates this branch).

Six falsifications run from the committed baseline, each reverting one thing and
confirming a test dies:

reverted caught by
ceiling back to the issued budget 3 tests fail
absent spend read as zero spend a_silent_checkpoint_keeps_the_older_weaker_ceiling
saturating_sub → wrapping_sub a_spend_beyond_the_budget_leaves_nothing_rather_than_wrapping
verify-side check removed (kept only in amend) verify_amendment_enforces_the_remainder_too
monotonicity branch removed cumulative_spend_may_not_fall_or_go_silent_across_a_run
skip_serializing_if removed 5 unit tests + the pack's drift check

🤖 Generated with Claude Code

## The hole `jcp-grant`'s checkpoint module promises, in its own words, that *"an amendment may only tighten … so steering can never escalate a running agent's authority."* Both `amend` and `verify_amendment` enforced that with: ```rust if new_budget_uj > grant.grant.budget_uj { return Err(CheckpointError::Amplifies("amended budget exceeds the grant")); } ``` That compares against the budget as **issued**. On a 1 000 µJ grant whose run has already spent 900, an amendment of 1 000 passes as a "tightening" and hands the run ten times its remainder. The steering mechanism built to re-bound a long run could re-fund it instead. The sweep that found this framed it as "`Checkpoint` carries no energy field", which understates it. The missing field is *why* the check could not be written correctly: there was nothing for it to measure against. ## What changed `Checkpoint` gains `spent: Option<Spend>` — a cumulative microjoule figure with its §5 provenance grade. Cumulative rather than per-interval, so one checkpoint answers "how much is left" on its own, without the verifier holding the whole chain and without a missing link silently understating the total. `remaining_uj(grant, cp)` derives the remainder, and returns `None` when the checkpoint is silent. Unknown is not zero, and conflating them is exactly how an amendment hands back a whole grant. The ceiling is now the remainder where the checkpoint states its spend, and the issued budget where it does not. **The fallback is deliberately the old, weaker rule rather than a refusal:** a checkpoint written before the field existed is still a valid checkpoint, and rejecting it would break running deployments in order to fix a hole those checkpoints cannot express. The guarantee is only as strong as the checkpoint — which `remaining_uj` returning `None` is how a verifier finds out. Enforced on **both** sides. `amend` refusing is not enough: a steward can sign an over-budget amendment with its own key and hand it to an enforcement point, which never calls `amend`. ## What the figure is actually worth Stated in the module docs rather than left implied: the checkpoint is authored and signed by the **agent** — the party the remainder constrains — so its declared spend is a claim, not an independent measurement. The `provenance` grade travels with it so the amender can see which kind of claim it is. Two things make the claim hard to shrink: it is signed, so understating it is attributable, and it is cumulative, so the new `checkpoint_follows(prev, next)` refuses a successor that declares less than its predecessor or drops a field it once carried — otherwise an agent recovers its full issued ceiling simply by going quiet. What this layer does **not** do is independently meter the run. ## The wire change is additive, and that is checked `spent: None` is skipped, so `Checkpoint::id()` and every sealed vector are byte-identical to before the field existed. Regenerating the pack is the proof: `standing.json` gains one entry and **no published id or signature moves**, `checkpoint` included. `skip_serializing_if` turned out to be load-bearing rather than tidy — the JCR-1 signed core forbids nulls, so removing it makes `Checkpoint::id()` fail outright with `DisallowedType("null")` on every checkpoint that states no spend. Omission is the only encoding the canonical form permits, and the field's doc comment now says so where an editor would see it. The new `checkpoint_with_spend` vector publishes the issued budget beside the spend so a third party can redo the arithmetic, and its id necessarily differs from `checkpoint`'s — which is what makes the additivity claim non-vacuous. The remainder rule itself is a refusal, and a refusal has nothing to sign, so it is pinned by C21 and unit tests rather than by a vector. ## Spec §4.7 and C21 now make the remainder normative: both sides MUST enforce it, an absent spend MUST NOT be read as zero, and a declared spend MUST NOT decrease. ## Verification `cargo test --workspace` in `jcp/jcp-rs`: **407 passed, 0 failed.** No new clippy warnings (the one `jcp-schema` warning predates this branch). Six falsifications run from the committed baseline, each reverting one thing and confirming a test dies: | reverted | caught by | |---|---| | ceiling back to the issued budget | 3 tests fail | | absent spend read as zero spend | `a_silent_checkpoint_keeps_the_older_weaker_ceiling` | | `saturating_sub` → `wrapping_sub` | `a_spend_beyond_the_budget_leaves_nothing_rather_than_wrapping` | | verify-side check removed (kept only in `amend`) | `verify_amendment_enforces_the_remainder_too` | | monotonicity branch removed | `cumulative_spend_may_not_fall_or_go_silent_across_a_run` | | `skip_serializing_if` removed | 5 unit tests + the pack's drift check | 🤖 Generated with [Claude Code](https://claude.com/claude-code)
jcp §4.7: an amendment may only tighten against what the grant has LEFT
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m7s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m54s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m27s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m7s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m10s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m13s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m10s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m6s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m38s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 2m9s
standards workspaces build + test / aac-rs (pull_request) Successful in 1m6s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 55s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 38s
standards workspaces build + test / map-rs (pull_request) Successful in 33s
standards workspaces build + test / mesh-rs (pull_request) Successful in 28s
standards workspaces build + test / openpay (pull_request) Successful in 1m34s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m19s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 5m32s
9f0cfd74be
Both `amend` and `verify_amendment` compared the amended budget against
`grant.budget_uj` — the budget as ISSUED. On a 1 000 µJ grant whose run had
already spent 900, an amendment of 1 000 passed as a "tightening" while handing
the run ten times its remainder, contradicting the module's own guarantee that
"steering can never escalate a running agent's authority". The hole was not
that the check was missing; it was that the checkpoint had no way to say what
had been spent, so there was nothing for the check to measure against.

`Checkpoint` now carries `spent: Option<Spend>` — a cumulative microjoule
figure with its §5 provenance grade. Cumulative rather than per-interval, so
one checkpoint answers "how much is left" without the verifier holding the
whole chain and without a missing link silently understating the total.
`remaining_uj` derives the remainder and returns `None` when the checkpoint is
silent: unknown is not zero, and conflating them is how an amendment hands back
a whole grant.

The ceiling is now the remainder where the checkpoint states its spend, and the
issued budget where it does not. The fallback is deliberately the old, weaker
rule rather than a refusal — a checkpoint written before the field existed is
still a valid checkpoint, and rejecting it would break running deployments to
fix a hole it cannot express. Enforced on both sides: `amend` refusing is not
enough, because an enforcement point handed a signed amendment never calls
`amend`.

What the figure is worth, stated rather than implied: the checkpoint is authored
by the agent, so its declared spend is a signed claim, not an independent
measurement. Two things make it hard to shrink — it is attributable, and
`checkpoint_follows` catches a later checkpoint that declares less than an
earlier one or drops a field it once carried, which would otherwise let an agent
recover its full ceiling by going quiet.

Wire change is additive: `spent: None` is skipped, so `Checkpoint::id()` and
every sealed vector are byte-identical to before the field existed. Regenerating
the pack proves it — `standing.json` gains one entry and no published id or
signature moves. The new `checkpoint_with_spend` vector publishes the issued
budget alongside the spend so a third party can redo the arithmetic, and its id
necessarily differs from `checkpoint`'s, which is what makes the additivity
claim non-vacuous. The remainder rule itself is a refusal, and a refusal has
nothing to sign, so it is pinned by C21 and unit tests instead.

SPEC §4.7 and C21 updated: the remainder is normative, both sides MUST enforce
it, an absent spend MUST NOT be read as zero, and a declared spend MUST NOT
decrease.

Co-Authored-By: Claude Opus 5 <[email protected]>
dcharlot force-pushed feat/checkpoint-energy from 9f0cfd74be
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 4s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / arl-rs (pull_request) Successful in 1m7s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m54s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m27s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 1m7s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 1m10s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m13s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m10s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m6s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m38s
standards workspaces build + test / jouleclaw-rs (pull_request) Successful in 2m9s
standards workspaces build + test / aac-rs (pull_request) Successful in 1m6s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 55s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 38s
standards workspaces build + test / map-rs (pull_request) Successful in 33s
standards workspaces build + test / mesh-rs (pull_request) Successful in 28s
standards workspaces build + test / openpay (pull_request) Successful in 1m34s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m19s
standards workspaces build + test / macOS energy backends (pull_request) Failing after 5m32s
to 9248799a6a
Some checks failed
standards workspaces build + test / sense-rs (pull_request) Successful in 1m6s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m35s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m37s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 24s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m29s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 29s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 6m15s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 9m57s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 30s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 11m40s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 26s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m4s
wai-pack / wai-pack · macos (pull_request) Failing after 4s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 46s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m36s
wai formal verification (integer core) / kani (pull_request) Successful in 4m57s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h14m38s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m1s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m3s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m13s
wai-pack / wai-pack · linux (pull_request) Failing after 14s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Failing after 53s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 9m58s
wai-pack / wai-pack · dependency licences (pull_request) Failing after 1m8s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m0s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 2m1s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
2026-10-07 10:07:41 -04:00
Compare
dcharlot force-pushed feat/checkpoint-energy from 9248799a6a
Some checks failed
standards workspaces build + test / sense-rs (pull_request) Successful in 1m6s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m35s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m37s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 24s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m29s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 29s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 6m15s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 9m57s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 30s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 11m40s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 8s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 26s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m4s
wai-pack / wai-pack · macos (pull_request) Failing after 4s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 46s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m36s
wai formal verification (integer core) / kani (pull_request) Successful in 4m57s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h14m38s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m1s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m3s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m13s
wai-pack / wai-pack · linux (pull_request) Failing after 14s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Failing after 53s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 9m58s
wai-pack / wai-pack · dependency licences (pull_request) Failing after 1m8s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m0s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 2m1s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
to 936d26761a
All checks were successful
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m15s
standards workspaces build + test / sense-rs (pull_request) Successful in 1m5s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m37s
wai cross-architecture byte-equality / linux-x86_64 (pull_request) Successful in 27s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 6m40s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 13s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 12m59s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 24s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 46s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m44s
WAI HTTP delivery / HTTP corpus · falsification and differential (pull_request) Successful in 1h19m13s
WAI HTTP delivery / cache rules · controls and labelled caches (pull_request) Successful in 1m4s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 1m3s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m7s
wai-pack / wai-pack · linux (pull_request) Successful in 1m4s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 1m40s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 22s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 1m6s
WAI section references / cited sections exist (pull_request) Successful in 10s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 4m21s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 2m42s
workflow hygiene / every workflow supersedes its own queue (pull_request) Successful in 4s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 3m3s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 5m1s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 30s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 10m35s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 29s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 3m33s
wai-pack / wai-pack · macos (pull_request) Successful in 6m13s
2026-10-08 10:29:44 -04:00
Compare
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!165
No description provided.