@aldus-runtime/gate-engine 0.2.0-next.6 → 0.2.0-next.60

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/spend.d.ts CHANGED
@@ -11,12 +11,12 @@
11
11
  * `GateDecision` stores only digests. Carrying the limit in a record beside the decision would
12
12
  * let someone raise the ceiling without touching the approval.
13
13
  *
14
- * So a grant does both: it holds the values, and {@link grantLimitsDigest} is included among the
14
+ * So a grant does both: it holds the values, and {@link grantTermsDigest} is included among the
15
15
  * gate's bound subjects. Raising a limit changes that digest, which drifts from `subjectHashes`,
16
16
  * which voids the authorization exactly as §13.2 requires. The ceiling cannot move without an
17
17
  * operator re-approving it.
18
18
  */
19
- import type { CostRecord, Money } from "@aldus-runtime/core";
19
+ import { type CostRecord, type Money, type SpendReservation } from "@aldus-runtime/core";
20
20
  /**
21
21
  * The subject key under which a grant's limits are bound.
22
22
  *
@@ -40,18 +40,89 @@ export interface SpendGrant {
40
40
  * back to the approval that permitted it (§19.3 "explicit spend authorization").
41
41
  */
42
42
  decisionId: string;
43
- /** Maximum total spend authorized across the Run (§13.2 "maximum authorized cost"). */
43
+ /**
44
+ * What this grant authorizes spending **on** (§13.2, §4.2).
45
+ *
46
+ * Operation names are adopter-defined open strings — `"agent.execute"`, `"tts.synthesize"`.
47
+ * Core names none.
48
+ *
49
+ * Present so that handing the wrong grant to an execution gateway cannot authorize an unrelated
50
+ * operation. Without it, "a grant" is a pool of money with no statement about what it is for,
51
+ * and the only thing keeping agent spend out of a synthesis ledger is which `decisionId` each
52
+ * happened to carry.
53
+ */
54
+ scope: {
55
+ operations: readonly string[];
56
+ };
57
+ /**
58
+ * Whether an execution with no estimate may be dispatched (§13.2, §19.3; ADR-0044).
59
+ *
60
+ * Operator-approved policy, so it lives on the grant and is bound by
61
+ * {@link grantTermsDigest} — changing it invalidates the approval exactly as changing a ceiling
62
+ * or the scope does. It must never be supplied by an execution input or an adapter: a caller
63
+ * that could assert its own permission is the shape #107 exists to prevent.
64
+ *
65
+ * A closed pair rather than a boolean. `permitUnestimated: true` invites being flipped in a
66
+ * config file; naming the reservation it implies makes the consequence part of the decision.
67
+ *
68
+ * Absent reads as `"refuse"`.
69
+ */
70
+ unestimatedExecution?: "refuse" | "reserve_max_per_request";
71
+ /**
72
+ * Maximum total spend authorized across the Run (§13.2 "maximum authorized cost").
73
+ *
74
+ * **Two different things consume it**, and confusing them mis-sizes a grant in either direction:
75
+ *
76
+ * - **settled charges**, permanently — the actual `CostRecord` amounts;
77
+ * - **active and unresolved reservations**, at their *reserved* amount until they settle.
78
+ *
79
+ * `availableAuthorization` is `maxTotal − settled − active`, and on settlement a reservation
80
+ * stops being counted at what it reserved and starts being counted at what it actually cost —
81
+ * so **unused headroom returns**.
82
+ *
83
+ * That matters under `unestimatedExecution: "reserve_max_per_request"`, where each unestimated
84
+ * dispatch reserves the *whole* per-request ceiling because there is no smaller truthful number.
85
+ * `maxTotal ÷ maxPerRequest` bounds how many such dispatches can be **outstanding at once** — in
86
+ * flight, or with billing still unresolved. It does **not** bound the run: once they settle
87
+ * below the ceiling the difference is available again, and a $25 / $3 grant can authorize far
88
+ * more than eight sequential dispatches when each settles cheaply.
89
+ *
90
+ * Both readings mis-size a grant. Read as a lifetime pool it ignores that eight worst-case
91
+ * reservations can be outstanding before any of them settles; read as a concurrency bound alone
92
+ * it over-provisions a run whose charges are small. Measured by an adopter: a $3 / $3 grant
93
+ * permitted one dispatch and refused a case dispatching twice, with the budget almost untouched
94
+ * — because the first had not settled yet.
95
+ */
44
96
  maxTotal: Money;
45
- /** Maximum spend authorized for any single request (§19.3 "per-request ... limits"). */
97
+ /**
98
+ * Maximum spend authorized for any single request (§19.3 "per-request ... limits").
99
+ *
100
+ * **What this means changed with ADR-0044 while its type did not.** It was a statement about
101
+ * what a *backend* enforces, so leaving it unset where the backend enforces nothing was the
102
+ * honest choice — binding a ceiling would have recorded a protection that did not exist.
103
+ *
104
+ * It is now what the *runtime reserves* before dispatch. Unset is still valid and still
105
+ * compiles, and under `unestimatedExecution: "reserve_max_per_request"` it makes every
106
+ * unestimated dispatch refuse, because there is no truthful amount to reserve
107
+ * (see {@link unestimatedPolicyIsSatisfiable}).
108
+ *
109
+ * Called out because the type system has nothing to say about a field whose meaning moved: an
110
+ * adopter migrating hit exactly this, and the comment in their own file argued for the choice
111
+ * that is now wrong.
112
+ */
46
113
  maxPerRequest?: Money;
47
114
  }
48
115
  /**
49
- * The digest a gate must bind for the grant's limits to be tamper-evident.
116
+ * The digest a gate must bind for the grant's **terms** to be tamper-evident (§13.2).
117
+ *
118
+ * Was `grantTermsDigest`, covering the ceilings alone. Scope is now a term too: changing a grant
119
+ * from agent-only to TTS-capable widens what an approval permits exactly as raising its ceiling
120
+ * does, and an approval that survives that change did not bind what it appeared to bind.
50
121
  *
51
- * Only the limits are digested, not the grant's identity: re-issuing an identical ceiling under a
52
- * new `grantId` should not read as the operator having approved something different.
122
+ * Only the terms are digested, not the grant's identity: re-issuing an identical grant under a new
123
+ * `grantId` should not read as the operator having approved something different.
53
124
  */
54
- export declare function grantLimitsDigest(grant: SpendGrant): string;
125
+ export declare function grantTermsDigest(grant: SpendGrant): string;
55
126
  /**
56
127
  * Whether a cost record consumes budget.
57
128
  *
@@ -82,7 +153,34 @@ export interface SpendLedger {
82
153
  counted: CostRecord[];
83
154
  /** Cost records excluded because they were voided. */
84
155
  excluded: CostRecord[];
156
+ /**
157
+ * Charges whose amount nobody knows yet (§19.3; #150).
158
+ *
159
+ * A provider may charge a request and withhold or delay the figure. While one of these stands
160
+ * against a grant, **Aldus cannot prove how much authorization remains** — the charge is real
161
+ * and its size is not yet a fact.
162
+ *
163
+ * A record here is never treated as free, voided, or a zero draw. Zero is a numerical assertion;
164
+ * this is an uncertainty state, and the two are not interchangeable.
165
+ */
166
+ unresolvedUnknown: CostRecord[];
167
+ /**
168
+ * Whether {@link SpendLedger.remaining} is a number anyone may spend against.
169
+ *
170
+ * `false` while any unresolved unknown charge stands. `remaining` still reports the arithmetic
171
+ * over what is known, because an operator wants the figure — but presenting it as headroom
172
+ * would state a safe amount that nothing establishes.
173
+ */
174
+ remainingIsDeterminate: boolean;
85
175
  }
176
+ /**
177
+ * Whether a record is a charge of unknown size (§19.3; #150).
178
+ *
179
+ * An estimate does not resolve it. An estimate is evidence about what a request was expected to
180
+ * cost, and the ruling on #150 is explicit that it does not confirm the final charge — so a record
181
+ * carrying both an estimate and `billingStatus: "unknown"` is still unresolved.
182
+ */
183
+ export declare function isUnresolvedUnknownCharge(record: CostRecord): boolean;
86
184
  /**
87
185
  * Total what has been drawn against a grant.
88
186
  *
@@ -91,6 +189,94 @@ export interface SpendLedger {
91
189
  * silently absorbing it would make one gate's ceiling depend on another's spending.
92
190
  */
93
191
  export declare function computeLedger(grant: SpendGrant, costs: readonly CostRecord[]): SpendLedger;
192
+ /**
193
+ * Authorization available to commit, derived rather than maintained (ADR-0044; #155).
194
+ *
195
+ * ```text
196
+ * available = authorized maximum − settled charges − active reservations
197
+ * ```
198
+ *
199
+ * Derived on every read, never stored as a balance. A maintained counter is a second source of
200
+ * truth about money, reconciled by hand against the records it summarises — and every defect this
201
+ * repository has fixed in the cost path has been a value asserting more than what established it.
202
+ */
203
+ export interface SpendAvailability {
204
+ /** The ceiling the operator approved. */
205
+ authorized: Money;
206
+ /** Charges already recorded against it. */
207
+ settled: Money;
208
+ /** Authorization committed to effects that have not settled. */
209
+ reserved: Money;
210
+ /**
211
+ * What may still be committed. Never negative.
212
+ *
213
+ * **Read {@link SpendAvailability.determinate} before spending against this.** The figure is the
214
+ * arithmetic over what is known, and while an unresolved charge of unknown size stands, what is
215
+ * known is not all there is.
216
+ */
217
+ available: Money;
218
+ /** Whether {@link SpendAvailability.available} is an amount anyone may commit against. */
219
+ determinate: boolean;
220
+ /**
221
+ * Why it is not, where it is not.
222
+ *
223
+ * Two independent sources, kept apart because they carry different evidence:
224
+ *
225
+ * - reservations in `billing_unknown` whose exposure is not bounded by an enforced ceiling;
226
+ * - cost records of unknown size with no reservation at all, which is every such record written
227
+ * before this protocol existed (#150).
228
+ */
229
+ indeterminate: {
230
+ unboundedReservations: SpendReservation[];
231
+ unreservedUnknownCharges: CostRecord[];
232
+ };
233
+ /**
234
+ * Reserved-but-unsettled amounts, per **authorization** currency (ADR-0044).
235
+ *
236
+ * Not the provider's billing currency. This states what Aldus set aside; what a provider charged,
237
+ * and in what currency, is a separate fact that may not exist yet. A report may say "USD 2.00
238
+ * remains reserved because billing is unresolved"; it must never restate that as "the provider
239
+ * made an unknown USD charge".
240
+ */
241
+ reservedUnknownByCurrency: Record<string, string>;
242
+ }
243
+ /**
244
+ * Derive what a grant still authorizes (§19.3; ADR-0044).
245
+ *
246
+ * Composes with #150 rather than replacing it. A charge of unknown size still makes the grant
247
+ * indeterminate — what a reservation adds is the possibility of a *bound*, and only when the
248
+ * execution that produced the charge was actually dispatched under an enforced ceiling. A backend
249
+ * that declares enforcement today is not evidence about a request dispatched by an earlier version
250
+ * (ADR-0030).
251
+ */
252
+ export declare function availableAuthorization(grant: SpendGrant, costs: readonly CostRecord[], reservations?: readonly SpendReservation[]): SpendAvailability;
253
+ /**
254
+ * Whether a grant can truthfully permit an unestimated execution (ADR-0044; #155).
255
+ *
256
+ * `reserve_max_per_request` promises to reserve the per-request ceiling. A grant that permits it
257
+ * and states no such ceiling promises an amount it does not have — and reserving zero instead
258
+ * would make unestimated executions invisible to concurrency control, which is the case most
259
+ * likely to be dispatched in a loop.
260
+ *
261
+ * Checked at decode **and** before dispatch: a grant assembled from configuration never passes
262
+ * through the constructor that would have caught it.
263
+ */
264
+ export declare function unestimatedPolicyIsSatisfiable(grant: SpendGrant): string | undefined;
265
+ /** Why a grant does not authorize an operation. */
266
+ export interface SpendScopeRefusal {
267
+ operation: string;
268
+ authorized: readonly string[];
269
+ explanation: string;
270
+ }
271
+ /**
272
+ * Whether a grant authorizes an operation (§13.2, §4.2; #155).
273
+ *
274
+ * Checked before reserving, so handing the wrong grant to an execution gateway cannot authorize
275
+ * unrelated work. Without it a grant is a pool of money with no statement about what it is for,
276
+ * and the only thing separating agent spend from a synthesis ledger is which decision each
277
+ * happened to name.
278
+ */
279
+ export declare function checkSpendScope(grant: SpendGrant, operation: string): SpendScopeRefusal | undefined;
94
280
  /** A request to spend against a grant. */
95
281
  export interface SpendRequest {
96
282
  /** The amount about to be committed. */
@@ -99,7 +285,16 @@ export interface SpendRequest {
99
285
  operation?: string;
100
286
  }
101
287
  /** Why a spend was refused. */
102
- export type SpendRefusalReason = "per-request-limit" | "total-limit" | "already-overspent" | "negative-amount";
288
+ export type SpendRefusalReason = "per-request-limit" | "total-limit" | "already-overspent" | "negative-amount"
289
+ /**
290
+ * A charge of unknown size stands against this grant (§19.3; #150).
291
+ *
292
+ * Refused rather than allowed-with-a-warning: while the size of a real charge is unknown, the
293
+ * remaining headroom is not a fact, and spending against a figure nobody can establish is how a
294
+ * ceiling is exceeded without any single decision being wrong. Resolution is a reconciled amount
295
+ * or a new authorization under an explicit policy — both human acts.
296
+ */
297
+ | "billing-unconfirmed";
103
298
  /** The outcome of a stop-on-budget check. */
104
299
  export type SpendCheck = {
105
300
  allowed: true;
@@ -1 +1 @@
1
- {"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAY7D;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,eAAe,CAAC;AAEpD,8FAA8F;AAC9F,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,uFAAuF;IACvF,QAAQ,EAAE,KAAK,CAAC;IAChB,wFAAwF;IACxF,aAAa,CAAC,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAK3D;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAE1D;AAED;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,GAAG,KAAK,CAG1E;AAED,uDAAuD;AACvD,MAAM,WAAW,WAAW;IAC1B,qCAAqC;IACrC,QAAQ,EAAE,KAAK,CAAC;IAChB,8EAA8E;IAC9E,SAAS,EAAE,KAAK,CAAC;IACjB,yDAAyD;IACzD,SAAS,EAAE,OAAO,CAAC;IACnB,mDAAmD;IACnD,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,sDAAsD;IACtD,QAAQ,EAAE,UAAU,EAAE,CAAC;CACxB;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,UAAU,EAAE,GAAG,WAAW,CAyB1F;AAED,0CAA0C;AAC1C,MAAM,WAAW,YAAY;IAC3B,wCAAwC;IACxC,MAAM,EAAE,KAAK,CAAC;IACd,6EAA6E;IAC7E,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,+BAA+B;AAC/B,MAAM,MAAM,kBAAkB,GAC5B,mBAAmB,GAAG,aAAa,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;AAEhF,6CAA6C;AAC7C,MAAM,MAAM,UAAU,GAClB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,WAAW,CAAC;IAAC,cAAc,EAAE,KAAK,CAAA;CAAE,GAC7D;IACE,OAAO,EAAE,KAAK,CAAC;IACf,MAAM,EAAE,kBAAkB,CAAC;IAC3B,MAAM,EAAE,WAAW,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEN;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CACxB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,SAAS,UAAU,EAAE,EAC5B,OAAO,EAAE,YAAY,GACpB,UAAU,CAsDZ"}
1
+ {"version":3,"file":"spend.d.ts","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"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"}
package/dist/spend.js CHANGED
@@ -11,13 +11,14 @@
11
11
  * `GateDecision` stores only digests. Carrying the limit in a record beside the decision would
12
12
  * let someone raise the ceiling without touching the approval.
13
13
  *
14
- * So a grant does both: it holds the values, and {@link grantLimitsDigest} is included among the
14
+ * So a grant does both: it holds the values, and {@link grantTermsDigest} is included among the
15
15
  * gate's bound subjects. Raising a limit changes that digest, which drifts from `subjectHashes`,
16
16
  * which voids the authorization exactly as §13.2 requires. The ceiling cannot move without an
17
17
  * operator re-approving it.
18
18
  */
19
+ import { reservationExposureIsBounded, reservationIsActive, } from "@aldus-runtime/core";
19
20
  import { digestSubjectValue } from "./binding.js";
20
- import { addMoney, compareMoney, formatMoney, isNegativeMoney, subtractMoney, zeroMoney, } from "./money.js";
21
+ import { addMoney, compareMoney, formatMoney, isNegativeMoney, isPositiveMoney, subtractMoney, zeroMoney, } from "./money.js";
21
22
  /**
22
23
  * The subject key under which a grant's limits are bound.
23
24
  *
@@ -27,13 +28,21 @@ import { addMoney, compareMoney, formatMoney, isNegativeMoney, subtractMoney, ze
27
28
  */
28
29
  export const SPEND_LIMIT_SUBJECT_KEY = "spendLimit";
29
30
  /**
30
- * The digest a gate must bind for the grant's limits to be tamper-evident.
31
+ * The digest a gate must bind for the grant's **terms** to be tamper-evident (§13.2).
31
32
  *
32
- * Only the limits are digested, not the grant's identity: re-issuing an identical ceiling under a
33
- * new `grantId` should not read as the operator having approved something different.
33
+ * Was `grantTermsDigest`, covering the ceilings alone. Scope is now a term too: changing a grant
34
+ * from agent-only to TTS-capable widens what an approval permits exactly as raising its ceiling
35
+ * does, and an approval that survives that change did not bind what it appeared to bind.
36
+ *
37
+ * Only the terms are digested, not the grant's identity: re-issuing an identical grant under a new
38
+ * `grantId` should not read as the operator having approved something different.
34
39
  */
35
- export function grantLimitsDigest(grant) {
40
+ export function grantTermsDigest(grant) {
36
41
  return digestSubjectValue({
42
+ // Sorted, because the *set* of authorized operations is the term, not the order an adopter
43
+ // listed them in — the same reason ADR-0033 sorts release input hashes.
44
+ scope: { operations: [...grant.scope.operations].sort() },
45
+ unestimatedExecution: grant.unestimatedExecution ?? "refuse",
37
46
  maxTotal: grant.maxTotal,
38
47
  maxPerRequest: grant.maxPerRequest ?? null,
39
48
  });
@@ -62,6 +71,16 @@ export function costRecordDraw(record, currency) {
62
71
  return zeroMoney(currency);
63
72
  return record.actual ?? record.estimated ?? zeroMoney(currency);
64
73
  }
74
+ /**
75
+ * Whether a record is a charge of unknown size (§19.3; #150).
76
+ *
77
+ * An estimate does not resolve it. An estimate is evidence about what a request was expected to
78
+ * cost, and the ruling on #150 is explicit that it does not confirm the final charge — so a record
79
+ * carrying both an estimate and `billingStatus: "unknown"` is still unresolved.
80
+ */
81
+ export function isUnresolvedUnknownCharge(record) {
82
+ return record.billingStatus === "unknown";
83
+ }
65
84
  /**
66
85
  * Total what has been drawn against a grant.
67
86
  *
@@ -85,12 +104,122 @@ export function computeLedger(grant, costs) {
85
104
  }
86
105
  const headroom = subtractMoney(grant.maxTotal, consumed);
87
106
  const overspent = isNegativeMoney(headroom);
107
+ const unresolvedUnknown = counted.filter(isUnresolvedUnknownCharge);
88
108
  return {
89
109
  consumed,
90
110
  remaining: overspent ? zeroMoney(currency) : headroom,
91
111
  overspent,
92
112
  counted,
93
113
  excluded,
114
+ unresolvedUnknown,
115
+ remainingIsDeterminate: unresolvedUnknown.length === 0,
116
+ };
117
+ }
118
+ /**
119
+ * Derive what a grant still authorizes (§19.3; ADR-0044).
120
+ *
121
+ * Composes with #150 rather than replacing it. A charge of unknown size still makes the grant
122
+ * indeterminate — what a reservation adds is the possibility of a *bound*, and only when the
123
+ * execution that produced the charge was actually dispatched under an enforced ceiling. A backend
124
+ * that declares enforcement today is not evidence about a request dispatched by an earlier version
125
+ * (ADR-0030).
126
+ */
127
+ export function availableAuthorization(grant, costs, reservations = []) {
128
+ const currency = grant.maxTotal.currency;
129
+ const ledger = computeLedger(grant, costs);
130
+ // Per grant, never per decision. The grant is the budget pool; the decision is who authorized
131
+ // its terms. One decision may establish several grants — an episode-level ceiling above an agent
132
+ // grant and a TTS grant — and deriving availability per decision would silently aggregate them.
133
+ const mine = reservations.filter((reservation) => reservation.grantId === grant.grantId);
134
+ const active = mine.filter(reservationIsActive);
135
+ // Settled reservations are already represented by the cost records that settled them. Counting
136
+ // both would double-count the same money against the ceiling.
137
+ let reserved = zeroMoney(currency);
138
+ const reservedUnknownByCurrency = new Map();
139
+ for (const reservation of active) {
140
+ if (reservation.reserved.currency === currency) {
141
+ reserved = addMoney(reserved, reservation.reserved);
142
+ }
143
+ if (reservation.status === "billing_unknown") {
144
+ const running = reservedUnknownByCurrency.get(reservation.reserved.currency) ??
145
+ zeroMoney(reservation.reserved.currency);
146
+ reservedUnknownByCurrency.set(reservation.reserved.currency, addMoney(running, reservation.reserved));
147
+ }
148
+ }
149
+ const unboundedReservations = active.filter((reservation) => reservation.status === "billing_unknown" && !reservationExposureIsBounded(reservation));
150
+ // #150's rule, narrowed by what a reservation can now establish. A record of unknown size whose
151
+ // reservation is bounded is accounted for; one with no reservation is not, and that is every
152
+ // such record written before this protocol.
153
+ const boundedReservationIds = new Set(active
154
+ .filter((reservation) => reservationExposureIsBounded(reservation))
155
+ .map((reservation) => reservation.reservationId));
156
+ // `computeLedger` filters cost records by `authorizationId`, which is the legacy link and stays
157
+ // correct: a record written before reservations existed has no `reservationId` to reach a grant
158
+ // through, and its decision is the only thing tying it to an authorization (#155).
159
+ const unreservedUnknownCharges = ledger.counted.filter((record) => isUnresolvedUnknownCharge(record) &&
160
+ (record.reservationId === undefined || !boundedReservationIds.has(record.reservationId)));
161
+ const headroom = subtractMoney(subtractMoney(grant.maxTotal, ledger.consumed), reserved);
162
+ return {
163
+ authorized: grant.maxTotal,
164
+ settled: ledger.consumed,
165
+ reserved,
166
+ available: isNegativeMoney(headroom) ? zeroMoney(currency) : headroom,
167
+ determinate: unboundedReservations.length === 0 && unreservedUnknownCharges.length === 0,
168
+ indeterminate: { unboundedReservations, unreservedUnknownCharges },
169
+ reservedUnknownByCurrency: Object.fromEntries([...reservedUnknownByCurrency.entries()]
170
+ .sort(([a], [b]) => a.localeCompare(b))
171
+ .map(([code, money]) => [code, money.amount])),
172
+ };
173
+ }
174
+ /**
175
+ * Whether a grant can truthfully permit an unestimated execution (ADR-0044; #155).
176
+ *
177
+ * `reserve_max_per_request` promises to reserve the per-request ceiling. A grant that permits it
178
+ * and states no such ceiling promises an amount it does not have — and reserving zero instead
179
+ * would make unestimated executions invisible to concurrency control, which is the case most
180
+ * likely to be dispatched in a loop.
181
+ *
182
+ * Checked at decode **and** before dispatch: a grant assembled from configuration never passes
183
+ * through the constructor that would have caught it.
184
+ */
185
+ export function unestimatedPolicyIsSatisfiable(grant) {
186
+ if ((grant.unestimatedExecution ?? "refuse") !== "reserve_max_per_request")
187
+ return undefined;
188
+ const ceiling = grant.maxPerRequest;
189
+ if (ceiling === undefined) {
190
+ return (`Grant "${grant.grantId}" permits unestimated execution by reserving its per-request ` +
191
+ "ceiling, and states no ceiling. There is no truthful amount to reserve, so this cannot be " +
192
+ "dispatched (§13.2, §19.3).");
193
+ }
194
+ if (!isPositiveMoney(ceiling)) {
195
+ return (`Grant "${grant.grantId}" permits unestimated execution by reserving its per-request ` +
196
+ `ceiling, and that ceiling is ${formatMoney(ceiling)}. Reserving zero would make an ` +
197
+ "unestimated execution invisible to concurrency control.");
198
+ }
199
+ if (ceiling.currency !== grant.maxTotal.currency) {
200
+ return (`Grant "${grant.grantId}" states a per-request ceiling in ${ceiling.currency} and a total ` +
201
+ `in ${grant.maxTotal.currency}. A reservation is denominated in the grant's currency, and ` +
202
+ "converting implicitly is not something this runtime does (ADR-0044).");
203
+ }
204
+ return undefined;
205
+ }
206
+ /**
207
+ * Whether a grant authorizes an operation (§13.2, §4.2; #155).
208
+ *
209
+ * Checked before reserving, so handing the wrong grant to an execution gateway cannot authorize
210
+ * unrelated work. Without it a grant is a pool of money with no statement about what it is for,
211
+ * and the only thing separating agent spend from a synthesis ledger is which decision each
212
+ * happened to name.
213
+ */
214
+ export function checkSpendScope(grant, operation) {
215
+ if (grant.scope.operations.includes(operation))
216
+ return undefined;
217
+ return {
218
+ operation,
219
+ authorized: [...grant.scope.operations],
220
+ explanation: `Grant "${grant.grantId}" authorizes ${grant.scope.operations.map((entry) => `"${entry}"`).join(", ") || "nothing"} ` +
221
+ `and this operation is "${operation}". A grant states what it may be spent on as well as ` +
222
+ "how much, so passing the wrong one to a gateway cannot authorize unrelated work (§13.2).",
94
223
  };
95
224
  }
96
225
  /**
@@ -116,6 +245,23 @@ export function checkSpend(grant, costs, request) {
116
245
  "cost record, not as a negative draw, so that the audit trail keeps the original charge.",
117
246
  };
118
247
  }
248
+ // Before the arithmetic, because the arithmetic is what cannot be trusted. While a charge of
249
+ // unknown size stands against this grant, `remaining` is the total over what is *known* — and
250
+ // spending against it would treat an unresolved charge as a zero draw, which is the one thing
251
+ // the ruling on #150 forbids (§19.3).
252
+ if (!ledger.remainingIsDeterminate) {
253
+ return {
254
+ allowed: false,
255
+ reason: "billing-unconfirmed",
256
+ ledger,
257
+ explanation: `${ledger.unresolvedUnknown.length} charge(s) against this authorization have an ` +
258
+ "unconfirmed amount, so the remaining budget is indeterminate rather than " +
259
+ `${formatMoney(ledger.remaining)}. Automatic spend is refused until the amount is ` +
260
+ "reconciled or an operator issues a new authorization: an unknown charge is neither free " +
261
+ "nor zero, and drawing against a figure nobody can establish is how a ceiling is exceeded " +
262
+ "without any single decision being wrong (§19.3).",
263
+ };
264
+ }
119
265
  if (ledger.overspent) {
120
266
  return {
121
267
  allowed: false,
package/dist/spend.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"spend.js","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spend.js","sourceRoot":"","sources":["../src/spend.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aldus-runtime/gate-engine",
3
- "version": "0.2.0-next.6",
3
+ "version": "0.2.0-next.60",
4
4
  "type": "module",
5
5
  "description": "Aldus Gate Engine — hash-bound human gates, cascading invalidation, and spend authorization.",
6
6
  "license": "Apache-2.0",
@@ -33,10 +33,10 @@
33
33
  "typecheck:test": "tsc -p tsconfig.test.json"
34
34
  },
35
35
  "dependencies": {
36
- "@aldus-runtime/core": "0.2.0-next.6"
36
+ "@aldus-runtime/core": "0.2.0-next.60"
37
37
  },
38
38
  "devDependencies": {
39
- "@aldus-runtime/testkit": "0.2.0-next.6",
39
+ "@aldus-runtime/testkit": "0.2.0-next.60",
40
40
  "@types/node": "^26.2.0",
41
41
  "typescript": "^7.0.2",
42
42
  "vitest": "^4.1.10"