open-standards/jcp/jcp-rs/crates/jcp-schema
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Transaction Science c31e0143d0 JCP §8.1: a hold reserves money too, and a stated per-call ceiling makes the path concurrent
The two-phase path took an upper bound before the work and debited what ran —
in joules. Money had only the single-phase form: debit at authorization, and a
monotonic counter afterwards. That is the wrong shape for every usage-based
charge, which is most of what an agent buys: the payer authorizes a maximum,
the work runs, and the real amount is known only afterwards. With no release,
a ceiling is consumed by the worst case of every call, so a session settling at
a tenth of its maxima exhausts a ceiling ten times too fast. We had measured
that and reported it rather than fixed it.

A priced hold now reserves a money bound beside its joule bound, and resolves
with what the work really cost. The reservation is checked against the
*committed* money spend, so concurrent holds bound one another — a reservation
that does not bind the next decision is not a reservation — and the remainder
is released by the hold's removal, so the same ceiling admits a later charge
the unreleased reservation would have blocked.

Three rules the money dimension needed, each for a reason the joule side
already settled:

- A resolution naming more than its hold reserved debits what settled and
  denies `funds-exceeded`. The money has already moved on the rail, so capping
  it silently would absorb the breach; the receipt is the evidence. Same trade
  `charge` already makes for a joule overrun.
- A resolution in a currency the grant is not denominated in is refused and
  **leaves the hold outstanding**, as a wrong-subject cancellation does. A
  malformed resolution must not be the one move that loses the bound.
- A grant with no money ceiling reserves no money. The dimension is absent, not
  zero.

**And a hold now reserves what the grant says a call may cost.** `hold_bound`
took the whole free joule remainder, which is a correct bound and a serializing
one: one outstanding hold leaves nothing to admit a second call from. Where the
grant states a per-invocation ceiling in advance — §4.9's `MaxCostUj`, the
tightest where several are stated — that ceiling *is* an upper bound the work is
not permitted to exceed, so the hold reserves it, and two metered calls can be
in flight at once. A grant stating none still gets the remainder and still
serializes, which the spec now says out loud rather than leaving to be
discovered. A call overrunning its stated ceiling has under-reserved and its
debit still stands, recorded as `caveat-unsatisfied` over a real debit rather
than absorbed — the trade the debit point already makes, moved to where the
reservation is sized.

Also corrected while there: `hold_bound` sized itself from the debited spend,
so two holds could each size against the same room. It now reads the committed
spend, in both the joule and the tainted dimension.

SPEC §4.8 gains the two-phase money form, §8.1 gains what a hold reserves and
the money dimension, the receipt grammar gains `held_minor` (omitted when a
hold reserves no money, so a joules-only receipt is byte-identical to one
sealed before), and conformance class C31 states the lot.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-11-10 13:01:56 -05:00
..
src JCP §8.1: a hold reserves money too, and a stated per-call ceiling makes the path concurrent 2026-11-10 13:01:56 -05:00
Cargo.toml Cargo: no minimum-supported-version floor, because the policy is latest stable 2026-11-03 17:42:20 -05:00
FLOW_LATTICE_PLAN.md jcp-schema: Add the flow lattice — propagated provenance labels 2026-07-22 02:08:26 -04:00