Summary
The cap-token cost caveat is not a spend control. It is verified against a fixed nominal constant (default 1), and the cost gate never reads VerifiedClaims.budget_remaining. As a result, AttenuationRule::ReduceBudget on a delegated token does not limit that delegate's real provider spend.
Evidence
crates/fused-runtime/src/runtime.rs:1040-1045 — RequiredCaveats { … cost: self.cost_units }; cost_units defaults to 1. Same at the other verify sites (:2188, :2841). crates/multi-agent/src/child.rs:121 hardcodes cost: 1.
- The Biscuit check
check if cost($c), $c <= budget therefore passes for any token with budget_remaining >= 1.
stage_cost_setup (runtime.rs:1338-1358) resolves the real ceiling from provisioning.budget / existing balance; claims.budget_remaining is consumed only as a Cedar attribute (cedar_attributes_from_claims, :2986-2989).
Design note
The in-code comment (child.rs:117-121) states this is intentional — the real per-turn ceiling is the CostEnvelope / cost-gate, not the cap-token caveat. The issue is the mismatch with operator expectation: ReduceBudget reads like a spend cap and is documented as an attenuation axis, but binds only the nominal cost=1.
Impact
An operator attenuating a delegated token with ReduceBudget(n) to cap a sub-agent's spend gets no real spend limit; the sub-agent can spend up to the subject's whole provisioned budget.
Suggested fix
Either feed the request's real projected envelope cost into RequiredCaveats.cost so the caveat bites, or resolve the cost-gate ceiling from min(provisioned, claims.budget_remaining). Document precisely what ReduceBudget binds. Add an E2E test that a ReduceBudget-attenuated token is denied once cumulative real spend exceeds the caveat.
Summary
The cap-token
costcaveat is not a spend control. It is verified against a fixed nominal constant (default1), and the cost gate never readsVerifiedClaims.budget_remaining. As a result,AttenuationRule::ReduceBudgeton a delegated token does not limit that delegate's real provider spend.Evidence
crates/fused-runtime/src/runtime.rs:1040-1045—RequiredCaveats { … cost: self.cost_units };cost_unitsdefaults to1. Same at the other verify sites (:2188,:2841).crates/multi-agent/src/child.rs:121hardcodescost: 1.check if cost($c), $c <= budgettherefore passes for any token withbudget_remaining >= 1.stage_cost_setup(runtime.rs:1338-1358) resolves the real ceiling fromprovisioning.budget/ existing balance;claims.budget_remainingis consumed only as a Cedar attribute (cedar_attributes_from_claims,:2986-2989).Design note
The in-code comment (
child.rs:117-121) states this is intentional — the real per-turn ceiling is theCostEnvelope/ cost-gate, not the cap-token caveat. The issue is the mismatch with operator expectation:ReduceBudgetreads like a spend cap and is documented as an attenuation axis, but binds only the nominalcost=1.Impact
An operator attenuating a delegated token with
ReduceBudget(n)to cap a sub-agent's spend gets no real spend limit; the sub-agent can spend up to the subject's whole provisioned budget.Suggested fix
Either feed the request's real projected envelope cost into
RequiredCaveats.costso the caveat bites, or resolve the cost-gate ceiling frommin(provisioned, claims.budget_remaining). Document precisely whatReduceBudgetbinds. Add an E2E test that aReduceBudget-attenuated token is denied once cumulative real spend exceeds the caveat.