@aldus-runtime/gate-engine 0.1.0

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/engine.js ADDED
@@ -0,0 +1,361 @@
1
+ /**
2
+ * Gate evaluation, decision recording, and authorization (architecture contract §12, §13, §19.3).
3
+ *
4
+ * The central design choice: **invalidation is derived, never stored.**
5
+ *
6
+ * Contract §13.1 requires a content-changing edit to invalidate the Content Freeze "and
7
+ * downstream approvals". The obvious implementation writes invalidation records and walks the
8
+ * graph marking approvals dead. That implementation has a failure mode the contract cannot
9
+ * tolerate — if the cascade is ever interrupted, or a gate is added after the fact, some approval
10
+ * stays marked valid while the thing it approved has moved underneath it, and §13.2 forbids
11
+ * exactly that.
12
+ *
13
+ * So nothing is marked. A gate's state is computed on every evaluation from three inputs: its
14
+ * latest decision, the current digests of what it binds, and the state of the gates it depends
15
+ * on. A stale approval cannot survive because there is no stored "valid" flag for it to survive
16
+ * in. Adding a dependency edge invalidates downstream approvals immediately, with no migration.
17
+ */
18
+ import { SCHEMA_VERSION, newEventId, newGateDecisionId, validate } from "@aldus-runtime/core";
19
+ import { assertSubjectsCover, detectDrift, } from "./binding.js";
20
+ import { GateEngineErrorCodes, gateEngineError } from "./errors.js";
21
+ import { checkSpend, grantLimitsDigest, } from "./spend.js";
22
+ /**
23
+ * What a gate currently is.
24
+ *
25
+ * `stale` is deliberately distinct from `pending`: a gate that was approved and then drifted is
26
+ * not the same operator situation as one nobody has looked at, and §13.1 wants the difference
27
+ * visible. `blocked_upstream` is likewise distinct from both — the gate itself may be perfectly
28
+ * approved while something it depends on is not.
29
+ */
30
+ export const GATE_STATES = [
31
+ /** No decision has been recorded. */
32
+ "pending",
33
+ // NOTE: `blocked_upstream` below is used only when a gate is otherwise fine. A gate that is
34
+ // itself stale or rejected keeps that state and carries `blockedBy` alongside it, because the
35
+ // more specific label is the one an operator can act on.
36
+ /** Approved, and still bound to the current inputs. */
37
+ "satisfied",
38
+ /** Approved, but a bound value has changed since (§13.1, §13.2). */
39
+ "stale",
40
+ /** The operator rejected it. */
41
+ "rejected",
42
+ /** The operator asked for changes. */
43
+ "changes_requested",
44
+ /** The operator deliberately bypassed the check (§13, distinct from approval). */
45
+ "waived",
46
+ /** A blocking gate this one depends on is not satisfied (§13.1 cascade). */
47
+ "blocked_upstream",
48
+ ];
49
+ /** True if two id lists hold the same ids in the same order. */
50
+ function sameIds(a, b) {
51
+ return a.length === b.length && a.every((id, index) => id === b[index]);
52
+ }
53
+ /**
54
+ * Evaluates gates, records decisions, and authorizes operations and spend.
55
+ */
56
+ export class GateEngine {
57
+ #registry;
58
+ #decisions;
59
+ #events;
60
+ #costs;
61
+ constructor(options) {
62
+ this.#registry = options.registry;
63
+ this.#decisions = options.decisions;
64
+ this.#events = options.events;
65
+ this.#costs = options.costs;
66
+ }
67
+ /** The gate definitions this engine evaluates. */
68
+ get registry() {
69
+ return this.#registry;
70
+ }
71
+ /**
72
+ * Record a human decision (contract §3.6).
73
+ *
74
+ * §3.6: "Human review MUST create a durable `GateDecision`. A chat message saying 'looks good'
75
+ * is not enough unless it is translated into a recorded decision tied to exact inputs." This is
76
+ * that translation, and it refuses anything that would produce a decision tied to less than the
77
+ * gate binds.
78
+ *
79
+ * @throws {AldusError} `ALDUS_GATE_NOT_FOUND` if the gate is not registered.
80
+ * @throws {AldusError} `ALDUS_GATE_SUBJECTS_INCOMPLETE` if the subjects do not cover the gate.
81
+ * @throws {AldusError} `ALDUS_GATE_ACTOR_NOT_PERMITTED` if the actor may not decide this gate.
82
+ */
83
+ async decide(input) {
84
+ const gate = this.#registry.require(input.gateId);
85
+ assertSubjectsCover(gate, input.subjects);
86
+ if (!gate.permittedActorKinds.includes(input.decidedBy.kind)) {
87
+ throw gateEngineError(GateEngineErrorCodes.GATE_ACTOR_NOT_PERMITTED, `Gate "${gate.gateId}" accepts decisions from [${gate.permittedActorKinds.join(", ")}], ` +
88
+ `but "${input.decidedBy.id}" is a ${input.decidedBy.kind}. Contract §12 forbids ` +
89
+ "presenting a machine pass as semantic correctness, and §13.3 keeps final performance " +
90
+ "approval human-owned.", {
91
+ category: "policy",
92
+ details: {
93
+ gateId: gate.gateId,
94
+ actorKind: input.decidedBy.kind,
95
+ permitted: [...gate.permittedActorKinds],
96
+ },
97
+ });
98
+ }
99
+ const decision = {
100
+ schemaVersion: SCHEMA_VERSION,
101
+ decisionId: input.decisionId ?? newGateDecisionId(),
102
+ gateId: gate.gateId,
103
+ runId: input.runId,
104
+ decision: input.decision,
105
+ subjectHashes: [...input.subjects].map((subject) => subject.sha256).sort(),
106
+ decidedBy: input.decidedBy,
107
+ decidedAt: input.decidedAt,
108
+ ...(input.comment !== undefined ? { comment: input.comment } : {}),
109
+ expiresOnChange: input.expiresOnChange ?? gate.expiresOnChange,
110
+ };
111
+ // Validate before persisting. A malformed decision written to the approvals log is worse
112
+ // than a rejected call, because §13 treats what is written there as authoritative.
113
+ const validated = validate("GateDecision", decision);
114
+ if (!validated.ok) {
115
+ throw gateEngineError(GateEngineErrorCodes.GATE_DEFINITION_INVALID, `The decision built for gate "${gate.gateId}" is not a valid GateDecision.`, { category: "internal", details: { issues: validated.error.details } });
116
+ }
117
+ await this.#decisions.append(input.runId, decision);
118
+ await this.#emitDecisionEvent(input, decision);
119
+ return decision;
120
+ }
121
+ /** Emit the §6.4 event for a recorded decision. */
122
+ async #emitDecisionEvent(input, decision) {
123
+ const event = {
124
+ schemaVersion: SCHEMA_VERSION,
125
+ eventId: input.eventId ?? newEventId(),
126
+ occurredAt: decision.decidedAt,
127
+ episodeId: input.episodeId,
128
+ runId: decision.runId,
129
+ action: `gate.${decision.decision}`,
130
+ actor: decision.decidedBy,
131
+ inputRefs: [],
132
+ outputRefs: [],
133
+ details: {
134
+ gateId: decision.gateId,
135
+ decisionId: decision.decisionId,
136
+ subjectCount: decision.subjectHashes.length,
137
+ expiresOnChange: decision.expiresOnChange,
138
+ },
139
+ };
140
+ await this.#events.emit(event);
141
+ }
142
+ /**
143
+ * Evaluate every registered gate for a Run.
144
+ *
145
+ * `subjects` supplies the current digests of what each gate binds. A gate absent from it is
146
+ * evaluated as having no current inputs, which reads as `pending` — never as satisfied.
147
+ */
148
+ async evaluate(runId, subjects) {
149
+ const decisions = await this.#decisions.list(runId);
150
+ return this.evaluateWith(decisions, subjects);
151
+ }
152
+ /**
153
+ * Evaluate against a decision list already in hand.
154
+ *
155
+ * Separated from {@link GateEngine.evaluate} so the whole cascade is a pure function of its
156
+ * inputs — which is what makes it testable without a store and impossible to get into a
157
+ * partially-updated state.
158
+ */
159
+ evaluateWith(decisions, subjects) {
160
+ const own = new Map();
161
+ for (const gate of this.#registry.list()) {
162
+ own.set(gate.gateId, this.#evaluateOne(gate, decisions, subjects[gate.gateId] ?? []));
163
+ }
164
+ return this.#applyCascade(own);
165
+ }
166
+ /** A gate's state from its own decision and subjects, before the cascade. */
167
+ #evaluateOne(gate, decisions, subjects) {
168
+ const base = { gateId: gate.gateId, level: gate.level, enforcement: gate.enforcement };
169
+ const blocks = (state) => gate.enforcement === "blocking" && state !== "satisfied" && state !== "waived";
170
+ // Append order is authoritative, not `decidedAt`: the log is the durable fact, and two
171
+ // machines with disagreeing clocks must not be able to reorder which approval is current.
172
+ const latest = [...decisions].reverse().find((entry) => entry.gateId === gate.gateId);
173
+ if (latest === undefined) {
174
+ return {
175
+ ...base,
176
+ state: "pending",
177
+ blocking: blocks("pending"),
178
+ explanation: `Gate "${gate.gateId}" has no recorded decision.`,
179
+ };
180
+ }
181
+ if (latest.decision === "rejected" || latest.decision === "changes_requested") {
182
+ return {
183
+ ...base,
184
+ state: latest.decision,
185
+ decision: latest,
186
+ blocking: blocks(latest.decision),
187
+ explanation: latest.comment ?? `Gate "${gate.gateId}" was ${latest.decision.replace("_", " ")}.`,
188
+ };
189
+ }
190
+ const drift = detectDrift(latest, subjects);
191
+ // A waiver records that a check was bypassed (§13). It still binds: waiving a gate for one
192
+ // version of the content says nothing about the next, so drift voids it exactly as it voids
193
+ // an approval.
194
+ if (drift !== undefined && latest.expiresOnChange) {
195
+ return {
196
+ ...base,
197
+ state: "stale",
198
+ decision: latest,
199
+ drift,
200
+ blocking: blocks("stale"),
201
+ explanation: `Gate "${gate.gateId}" was ${latest.decision}, but ` +
202
+ `${drift.changed.length > 0 ? `[${drift.changed.join(", ")}] changed` : "its bound inputs changed"}` +
203
+ " since. Contract §13.1 voids an approval once what it approved has moved.",
204
+ };
205
+ }
206
+ const state = latest.decision === "waived" ? "waived" : "satisfied";
207
+ return { ...base, state, decision: latest, blocking: blocks(state) };
208
+ }
209
+ /**
210
+ * Propagate §13.1's cascade over the dependency graph.
211
+ *
212
+ * Only **blocking** gates propagate. §12 level 2 defines an advisory signal as one that
213
+ * "reports a possible issue without blocking", so an un-run advisory check must not halt the
214
+ * gates downstream of it — otherwise every advisory would be a hard gate wearing a different
215
+ * label, and §12's four levels would collapse into two.
216
+ */
217
+ #applyCascade(own) {
218
+ const result = new Map(own);
219
+ // Registry construction already refused a cycle, so a fixpoint loop terminates. Bounding it
220
+ // by the gate count keeps a future graph bug from hanging an operator's session regardless.
221
+ for (let pass = 0; pass < result.size + 1; pass += 1) {
222
+ let changed = false;
223
+ for (const gate of this.#registry.list()) {
224
+ const current = result.get(gate.gateId);
225
+ if (current === undefined)
226
+ continue;
227
+ const blockedBy = gate.dependsOn.filter((dependency) => {
228
+ const upstream = result.get(dependency);
229
+ return upstream !== undefined && upstream.blocking;
230
+ });
231
+ if (blockedBy.length === 0)
232
+ continue;
233
+ if (current.blockedBy !== undefined && sameIds(current.blockedBy, blockedBy))
234
+ continue;
235
+ // A gate can be broken on its own account *and* blocked by something upstream. When it
236
+ // has a decision that is itself broken, that state wins: an operator told only "blocked
237
+ // upstream" would fix the upstream gate and be surprised this one still needs
238
+ // re-approving. `blockedBy` is set either way, so the cascade is never lost.
239
+ //
240
+ // A `pending` gate has no such decision to preserve, and there "blocked upstream" is the
241
+ // more useful label — it says the gate cannot even be started on yet, rather than merely
242
+ // that nobody has.
243
+ const ownStateIsInformative = current.state === "stale" ||
244
+ current.state === "rejected" ||
245
+ current.state === "changes_requested";
246
+ const state = ownStateIsInformative ? current.state : "blocked_upstream";
247
+ const upstreamNote = `[${blockedBy.join(", ")}] is not satisfied, so contract §13.1 invalidates this ` +
248
+ "approval along with the gate it depends on.";
249
+ result.set(gate.gateId, {
250
+ ...current,
251
+ state,
252
+ blockedBy,
253
+ blocking: gate.enforcement === "blocking",
254
+ explanation: ownStateIsInformative
255
+ ? `${current.explanation ?? `Gate "${gate.gateId}" is ${current.state}.`} Additionally, ${upstreamNote}`
256
+ : `Gate "${gate.gateId}" cannot be relied on because ${upstreamNote}`,
257
+ });
258
+ changed = true;
259
+ }
260
+ if (!changed)
261
+ break;
262
+ }
263
+ return result;
264
+ }
265
+ /**
266
+ * Whether an operation is authorized (contract §13.4).
267
+ *
268
+ * An approval authorizes exactly the operations its gate names in `grants` and nothing else.
269
+ * That is what keeps §13.4's "Uploading and making public SHOULD be separate operations"
270
+ * enforceable: they are two gates granting two operations, and approving one leaves the other
271
+ * refused.
272
+ *
273
+ * Returns a refusal rather than throwing, because a caller needs to display why an operation is
274
+ * unavailable.
275
+ */
276
+ async authorize(runId, operation, subjects) {
277
+ const statuses = await this.evaluate(runId, subjects);
278
+ const candidates = this.#registry.list().filter((gate) => gate.grants.includes(operation));
279
+ if (candidates.length === 0) {
280
+ return {
281
+ authorized: false,
282
+ statuses: [],
283
+ explanation: `No registered gate grants "${operation}". An operation nobody authorizes is refused ` +
284
+ "rather than allowed, so that adding a gate is what enables an action, never omitting one.",
285
+ };
286
+ }
287
+ const relevant = candidates.flatMap((gate) => {
288
+ const status = statuses.get(gate.gateId);
289
+ return status === undefined ? [] : [status];
290
+ });
291
+ const granted = relevant.find((status) => status.state === "satisfied" && status.decision?.decision === "approved");
292
+ if (granted?.decision !== undefined) {
293
+ return { authorized: true, gateId: granted.gateId, decision: granted.decision };
294
+ }
295
+ return {
296
+ authorized: false,
297
+ statuses: relevant,
298
+ explanation: `"${operation}" is not authorized. ` +
299
+ relevant
300
+ .map((status) => `${status.gateId}: ${status.state}${status.explanation === undefined ? "" : ` — ${status.explanation}`}`)
301
+ .join("; "),
302
+ };
303
+ }
304
+ /**
305
+ * Whether a paid request may proceed (contract §13.2, §19.3).
306
+ *
307
+ * Three things must hold, and all three are checked here because any one of them alone is
308
+ * insufficient:
309
+ *
310
+ * 1. the gate is satisfied — §13.2 forbids paid synthesis before the operator approves;
311
+ * 2. the grant's limits are among what that decision bound — otherwise the ceiling could be
312
+ * raised after approval without voiding it;
313
+ * 3. the spend fits the remaining budget — §19.3 stop-on-budget.
314
+ */
315
+ async authorizeSpend(runId, grant, request, subjects, costs) {
316
+ const statuses = await this.evaluate(runId, subjects);
317
+ const status = statuses.get(grant.gateId);
318
+ if (status === undefined) {
319
+ return {
320
+ authorized: false,
321
+ explanation: `Gate "${grant.gateId}" is not registered, so it cannot authorize spend.`,
322
+ };
323
+ }
324
+ if (status.state !== "satisfied" || status.decision?.decision !== "approved") {
325
+ return {
326
+ authorized: false,
327
+ statuses: [status],
328
+ explanation: `Paid work is refused: gate "${grant.gateId}" is ${status.state}. Contract §13.2 ` +
329
+ "forbids paid synthesis until the operator has approved, and voids that approval once " +
330
+ `any bound value changes.${status.explanation === undefined ? "" : ` ${status.explanation}`}`,
331
+ };
332
+ }
333
+ const decision = status.decision;
334
+ if (decision.decisionId !== grant.decisionId) {
335
+ return {
336
+ authorized: false,
337
+ statuses: [status],
338
+ explanation: `The grant cites decision "${grant.decisionId}", but the current decision on gate ` +
339
+ `"${grant.gateId}" is "${decision.decisionId}". A grant from a superseded decision ` +
340
+ "does not carry forward (§13.2).",
341
+ };
342
+ }
343
+ if (!decision.subjectHashes.includes(grantLimitsDigest(grant))) {
344
+ return {
345
+ authorized: false,
346
+ statuses: [status],
347
+ explanation: `The authorized maximum of ${grant.maxTotal.amount} ${grant.maxTotal.currency} is not ` +
348
+ `among what decision "${decision.decisionId}" bound. Contract §13.2 requires the ` +
349
+ "operator to approve a maximum authorized cost; a limit the approval never covered is " +
350
+ "not an authorization.",
351
+ };
352
+ }
353
+ const recorded = costs ?? (this.#costs === undefined ? [] : await this.#costs.list(runId));
354
+ const check = checkSpend(grant, recorded, request);
355
+ if (!check.allowed) {
356
+ return { authorized: false, explanation: check.explanation, statuses: [status], check };
357
+ }
358
+ return { authorized: true, gateId: grant.gateId, decision, check };
359
+ }
360
+ }
361
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Failures specific to gate evaluation and spend authorization.
3
+ *
4
+ * Aldus Core deliberately keeps no central error-code registry, so that a package can name a new
5
+ * failure without forking Core. These codes are this package's contribution; they carry the same
6
+ * `ALDUS_` prefix and `SCREAMING_SNAKE_CASE` shape so production trace (contract §20) stays
7
+ * uniform across packages.
8
+ */
9
+ import { AldusError, type ErrorCategory } from "@aldus-runtime/core";
10
+ /** Error codes raised by the gate engine. */
11
+ export declare const GateEngineErrorCodes: {
12
+ /** A gate was referenced that is not registered. */
13
+ readonly GATE_NOT_FOUND: "ALDUS_GATE_NOT_FOUND";
14
+ /** A gate definition is internally inconsistent and was refused at registration. */
15
+ readonly GATE_DEFINITION_INVALID: "ALDUS_GATE_DEFINITION_INVALID";
16
+ /**
17
+ * Gate dependencies form a cycle.
18
+ *
19
+ * A configuration fault, not a data fault: contract §13.1's cascade is only meaningful over a
20
+ * directed acyclic graph, and a cycle would make "what does this invalidate" unanswerable.
21
+ */
22
+ readonly GATE_DEPENDENCY_CYCLE: "ALDUS_GATE_DEPENDENCY_CYCLE";
23
+ /**
24
+ * A decision was submitted by an actor the gate does not accept.
25
+ *
26
+ * Contract §13.3 keeps final performance approval human-owned, and §12 reserves the human
27
+ * oracle level for subjective and asymmetric-risk judgements. A machine actor satisfying such
28
+ * a gate would present a machine pass as semantic correctness, which §12 forbids outright.
29
+ */
30
+ readonly GATE_ACTOR_NOT_PERMITTED: "ALDUS_GATE_ACTOR_NOT_PERMITTED";
31
+ /** A decision was submitted that does not bind the subjects its gate requires. */
32
+ readonly GATE_SUBJECTS_INCOMPLETE: "ALDUS_GATE_SUBJECTS_INCOMPLETE";
33
+ /**
34
+ * An operation requiring authorization was attempted without a valid one.
35
+ *
36
+ * Contract §13.2: paid TTS MUST NOT run until the operator approves. This is the refusal that
37
+ * enforces it.
38
+ */
39
+ readonly AUTHORIZATION_MISSING: "ALDUS_AUTHORIZATION_MISSING";
40
+ /**
41
+ * An authorization exists but no longer binds the current inputs.
42
+ *
43
+ * Contract §13.2: "The authorization MUST be invalidated if any bound value changes."
44
+ */
45
+ readonly AUTHORIZATION_STALE: "ALDUS_AUTHORIZATION_STALE";
46
+ /** A spend request would exceed the authorized maximum (contract §19.3 stop-on-budget). */
47
+ readonly SPEND_LIMIT_EXCEEDED: "ALDUS_SPEND_LIMIT_EXCEEDED";
48
+ /** Two monetary values in different currencies were combined or compared. */
49
+ readonly CURRENCY_MISMATCH: "ALDUS_CURRENCY_MISMATCH";
50
+ /** A monetary amount was not a well-formed decimal string. */
51
+ readonly MONEY_MALFORMED: "ALDUS_MONEY_MALFORMED";
52
+ };
53
+ /** @see GateEngineErrorCodes */
54
+ export type GateEngineErrorCode = (typeof GateEngineErrorCodes)[keyof typeof GateEngineErrorCodes];
55
+ /** Construct an {@link AldusError} with a gate-engine code. */
56
+ export declare function gateEngineError(code: GateEngineErrorCode, message: string, options: {
57
+ category: ErrorCategory;
58
+ retryable?: boolean;
59
+ details?: Record<string, unknown>;
60
+ }): AldusError;
61
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAE,KAAK,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAErE,6CAA6C;AAC7C,eAAO,MAAM,oBAAoB;IAC/B,oDAAoD;aACpD,cAAc,EAAE,sBAAsB;IACtC,oFAAoF;aACpF,uBAAuB,EAAE,+BAA+B;IACxD;;;;;OAKG;aACH,qBAAqB,EAAE,6BAA6B;IACpD;;;;;;OAMG;aACH,wBAAwB,EAAE,gCAAgC;IAC1D,kFAAkF;aAClF,wBAAwB,EAAE,gCAAgC;IAC1D;;;;;OAKG;aACH,qBAAqB,EAAE,6BAA6B;IACpD;;;;OAIG;aACH,mBAAmB,EAAE,2BAA2B;IAChD,2FAA2F;aAC3F,oBAAoB,EAAE,4BAA4B;IAClD,6EAA6E;aAC7E,iBAAiB,EAAE,yBAAyB;IAC5C,8DAA8D;aAC9D,eAAe,EAAE,uBAAuB;CAChC,CAAC;AAEX,gCAAgC;AAChC,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,OAAO,oBAAoB,CAAC,CAAC;AAEnG,+DAA+D;AAC/D,wBAAgB,eAAe,CAC7B,IAAI,EAAE,mBAAmB,EACzB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;IAAE,QAAQ,EAAE,aAAa,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,GAC3F,UAAU,CAEZ"}
package/dist/errors.js ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Failures specific to gate evaluation and spend authorization.
3
+ *
4
+ * Aldus Core deliberately keeps no central error-code registry, so that a package can name a new
5
+ * failure without forking Core. These codes are this package's contribution; they carry the same
6
+ * `ALDUS_` prefix and `SCREAMING_SNAKE_CASE` shape so production trace (contract §20) stays
7
+ * uniform across packages.
8
+ */
9
+ import { AldusError } from "@aldus-runtime/core";
10
+ /** Error codes raised by the gate engine. */
11
+ export const GateEngineErrorCodes = {
12
+ /** A gate was referenced that is not registered. */
13
+ GATE_NOT_FOUND: "ALDUS_GATE_NOT_FOUND",
14
+ /** A gate definition is internally inconsistent and was refused at registration. */
15
+ GATE_DEFINITION_INVALID: "ALDUS_GATE_DEFINITION_INVALID",
16
+ /**
17
+ * Gate dependencies form a cycle.
18
+ *
19
+ * A configuration fault, not a data fault: contract §13.1's cascade is only meaningful over a
20
+ * directed acyclic graph, and a cycle would make "what does this invalidate" unanswerable.
21
+ */
22
+ GATE_DEPENDENCY_CYCLE: "ALDUS_GATE_DEPENDENCY_CYCLE",
23
+ /**
24
+ * A decision was submitted by an actor the gate does not accept.
25
+ *
26
+ * Contract §13.3 keeps final performance approval human-owned, and §12 reserves the human
27
+ * oracle level for subjective and asymmetric-risk judgements. A machine actor satisfying such
28
+ * a gate would present a machine pass as semantic correctness, which §12 forbids outright.
29
+ */
30
+ GATE_ACTOR_NOT_PERMITTED: "ALDUS_GATE_ACTOR_NOT_PERMITTED",
31
+ /** A decision was submitted that does not bind the subjects its gate requires. */
32
+ GATE_SUBJECTS_INCOMPLETE: "ALDUS_GATE_SUBJECTS_INCOMPLETE",
33
+ /**
34
+ * An operation requiring authorization was attempted without a valid one.
35
+ *
36
+ * Contract §13.2: paid TTS MUST NOT run until the operator approves. This is the refusal that
37
+ * enforces it.
38
+ */
39
+ AUTHORIZATION_MISSING: "ALDUS_AUTHORIZATION_MISSING",
40
+ /**
41
+ * An authorization exists but no longer binds the current inputs.
42
+ *
43
+ * Contract §13.2: "The authorization MUST be invalidated if any bound value changes."
44
+ */
45
+ AUTHORIZATION_STALE: "ALDUS_AUTHORIZATION_STALE",
46
+ /** A spend request would exceed the authorized maximum (contract §19.3 stop-on-budget). */
47
+ SPEND_LIMIT_EXCEEDED: "ALDUS_SPEND_LIMIT_EXCEEDED",
48
+ /** Two monetary values in different currencies were combined or compared. */
49
+ CURRENCY_MISMATCH: "ALDUS_CURRENCY_MISMATCH",
50
+ /** A monetary amount was not a well-formed decimal string. */
51
+ MONEY_MALFORMED: "ALDUS_MONEY_MALFORMED",
52
+ };
53
+ /** Construct an {@link AldusError} with a gate-engine code. */
54
+ export function gateEngineError(code, message, options) {
55
+ return new AldusError(code, message, options);
56
+ }
57
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,UAAU,EAAsB,MAAM,qBAAqB,CAAC;AAErE,6CAA6C;AAC7C,MAAM,CAAC,MAAM,oBAAoB,GAAG;IAClC,oDAAoD;IACpD,cAAc,EAAE,sBAAsB;IACtC,oFAAoF;IACpF,uBAAuB,EAAE,+BAA+B;IACxD;;;;;OAKG;IACH,qBAAqB,EAAE,6BAA6B;IACpD;;;;;;OAMG;IACH,wBAAwB,EAAE,gCAAgC;IAC1D,kFAAkF;IAClF,wBAAwB,EAAE,gCAAgC;IAC1D;;;;;OAKG;IACH,qBAAqB,EAAE,6BAA6B;IACpD;;;;OAIG;IACH,mBAAmB,EAAE,2BAA2B;IAChD,2FAA2F;IAC3F,oBAAoB,EAAE,4BAA4B;IAClD,6EAA6E;IAC7E,iBAAiB,EAAE,yBAAyB;IAC5C,8DAA8D;IAC9D,eAAe,EAAE,uBAAuB;CAChC,CAAC;AAKX,+DAA+D;AAC/D,MAAM,UAAU,eAAe,CAC7B,IAAyB,EACzB,OAAe,EACf,OAA4F;IAE5F,OAAO,IAAI,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `@aldus-runtime/gate-engine` — hash-bound human gates, cascading invalidation, and spend authorization.
3
+ *
4
+ * Implements architecture contract §22 **WP-05**, covering §12 (quality levels), §13 (human gates
5
+ * and freezes), and §19.3 (cost governance).
6
+ *
7
+ * What this package is for, in one sentence: making it impossible to spend money or publish
8
+ * anything on the strength of an approval that no longer describes what is about to happen.
9
+ *
10
+ * Three properties carry that weight:
11
+ *
12
+ * - **A decision binds to digests.** §3.6 requires human review to produce a durable record "tied
13
+ * to exact inputs", and §13.2 voids an authorization if any bound value changes.
14
+ * - **Invalidation is derived, never stored.** A gate's state is recomputed from its decision,
15
+ * its current inputs, and its dependencies. There is no "valid" flag for a stale approval to
16
+ * survive in, so §13.1's cascade cannot be half-applied.
17
+ * - **A grant's ceiling is itself bound.** §13.2 requires the operator to approve a maximum
18
+ * authorized cost, so the limits' digest sits among the decision's subject hashes and raising
19
+ * the ceiling voids the approval that permitted the spend.
20
+ *
21
+ * What it deliberately does not do: run any check that feeds a gate. §12's evaluators, §12.1's
22
+ * calibration metrics (WP-10), the TTS ledger (WP-07), and the release adapters (WP-12) are
23
+ * elsewhere. This package models the decisions; it does not make them.
24
+ *
25
+ * Contract §13 names four gates — Content Freeze, Performance Freeze, Human Ear, Final Release —
26
+ * and none is hardcoded. They are the definitions an adopter is most likely to write (§4.2, §4.3).
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ export { GATE_ENFORCEMENTS, GATE_LEVELS, GateRegistry, validateGateDefinition, type GateDefinition, type GateEnforcement, type GateLevel, type PromotionEvidence, type ResolvedGateDefinition, } from "./definition.js";
31
+ export { assertSubjectsCover, detectDrift, digestBytes, digestSubjectValue, toSubjectHashes, type GateSubject, type SubjectDrift, } from "./binding.js";
32
+ export { GATE_STATES, GateEngine, type AuthorizationGrant, type AuthorizationRefusal, type AuthorizationResult, type DecideInput, type GateEngineOptions, type GateState, type GateStatus, type SpendAuthorization, type SubjectsByGate, } from "./engine.js";
33
+ export { SPEND_LIMIT_SUBJECT_KEY, checkSpend, computeLedger, consumesBudget, costRecordDraw, grantLimitsDigest, type SpendCheck, type SpendGrant, type SpendLedger, type SpendRefusalReason, type SpendRequest, } from "./spend.js";
34
+ export { addMoney, assertMoney, compareMoney, formatMoney, isNegativeMoney, isPositiveMoney, subtractMoney, sumMoney, zeroMoney, } from "./money.js";
35
+ export { MemoryCostReader, MemoryGateDecisionStore, MemoryGateEventSink, type CostReader, type GateDecisionStore, type GateEventSink, } from "./ports.js";
36
+ export { GateEngineErrorCodes, gateEngineError, type GateEngineErrorCode } from "./errors.js";
37
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,sBAAsB,EACtB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,SAAS,EACd,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,GAC5B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,eAAe,EACf,KAAK,WAAW,EAChB,KAAK,YAAY,GAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,UAAU,EACV,KAAK,kBAAkB,EACvB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,WAAW,EAChB,KAAK,iBAAiB,EACtB,KAAK,SAAS,EACd,KAAK,UAAU,EACf,KAAK,kBAAkB,EACvB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,eAAe,EACf,eAAe,EACf,aAAa,EACb,QAAQ,EACR,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,EACnB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,aAAa,GACnB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAAE,KAAK,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ /**
2
+ * `@aldus-runtime/gate-engine` — hash-bound human gates, cascading invalidation, and spend authorization.
3
+ *
4
+ * Implements architecture contract §22 **WP-05**, covering §12 (quality levels), §13 (human gates
5
+ * and freezes), and §19.3 (cost governance).
6
+ *
7
+ * What this package is for, in one sentence: making it impossible to spend money or publish
8
+ * anything on the strength of an approval that no longer describes what is about to happen.
9
+ *
10
+ * Three properties carry that weight:
11
+ *
12
+ * - **A decision binds to digests.** §3.6 requires human review to produce a durable record "tied
13
+ * to exact inputs", and §13.2 voids an authorization if any bound value changes.
14
+ * - **Invalidation is derived, never stored.** A gate's state is recomputed from its decision,
15
+ * its current inputs, and its dependencies. There is no "valid" flag for a stale approval to
16
+ * survive in, so §13.1's cascade cannot be half-applied.
17
+ * - **A grant's ceiling is itself bound.** §13.2 requires the operator to approve a maximum
18
+ * authorized cost, so the limits' digest sits among the decision's subject hashes and raising
19
+ * the ceiling voids the approval that permitted the spend.
20
+ *
21
+ * What it deliberately does not do: run any check that feeds a gate. §12's evaluators, §12.1's
22
+ * calibration metrics (WP-10), the TTS ledger (WP-07), and the release adapters (WP-12) are
23
+ * elsewhere. This package models the decisions; it does not make them.
24
+ *
25
+ * Contract §13 names four gates — Content Freeze, Performance Freeze, Human Ear, Final Release —
26
+ * and none is hardcoded. They are the definitions an adopter is most likely to write (§4.2, §4.3).
27
+ *
28
+ * @packageDocumentation
29
+ */
30
+ export { GATE_ENFORCEMENTS, GATE_LEVELS, GateRegistry, validateGateDefinition, } from "./definition.js";
31
+ export { assertSubjectsCover, detectDrift, digestBytes, digestSubjectValue, toSubjectHashes, } from "./binding.js";
32
+ export { GATE_STATES, GateEngine, } from "./engine.js";
33
+ export { SPEND_LIMIT_SUBJECT_KEY, checkSpend, computeLedger, consumesBudget, costRecordDraw, grantLimitsDigest, } from "./spend.js";
34
+ export { addMoney, assertMoney, compareMoney, formatMoney, isNegativeMoney, isPositiveMoney, subtractMoney, sumMoney, zeroMoney, } from "./money.js";
35
+ export { MemoryCostReader, MemoryGateDecisionStore, MemoryGateEventSink, } from "./ports.js";
36
+ export { GateEngineErrorCodes, gateEngineError } from "./errors.js";
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EACL,iBAAiB,EACjB,WAAW,EACX,YAAY,EACZ,sBAAsB,GAMvB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,mBAAmB,EACnB,WAAW,EACX,WAAW,EACX,kBAAkB,EAClB,eAAe,GAGhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,WAAW,EACX,UAAU,GAUX,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,UAAU,EACV,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,GAMlB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,QAAQ,EACR,WAAW,EACX,YAAY,EACZ,WAAW,EACX,eAAe,EACf,eAAe,EACf,aAAa,EACb,QAAQ,EACR,SAAS,GACV,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,uBAAuB,EACvB,mBAAmB,GAIpB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,oBAAoB,EAAE,eAAe,EAA4B,MAAM,aAAa,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Exact decimal arithmetic on {@link Money}.
3
+ *
4
+ * Contract §19.3 requires per-request and per-run limits, actual cost recording, and
5
+ * stop-on-budget behaviour. All three are comparisons and sums over money, and Core deliberately
6
+ * models an amount as a decimal *string* because TTS costs are fractional-cent and IEEE-754
7
+ * accumulation silently corrupts the totals an operator authorises spend against.
8
+ *
9
+ * Honouring that decision means never converting an amount to `number`. Every operation here
10
+ * scales both operands to a common exponent and works in `bigint`, so a sum of ten thousand
11
+ * ten-thousandth-of-a-cent charges is exact rather than approximately exact.
12
+ */
13
+ import type { Money } from "@aldus-runtime/core";
14
+ /** A zero amount in the given currency. */
15
+ export declare function zeroMoney(currency: string): Money;
16
+ /**
17
+ * Sum two amounts exactly.
18
+ *
19
+ * @throws {AldusError} `ALDUS_CURRENCY_MISMATCH` if the currencies differ.
20
+ */
21
+ export declare function addMoney(a: Money, b: Money): Money;
22
+ /**
23
+ * Subtract `b` from `a` exactly. The result may be negative.
24
+ *
25
+ * @throws {AldusError} `ALDUS_CURRENCY_MISMATCH` if the currencies differ.
26
+ */
27
+ export declare function subtractMoney(a: Money, b: Money): Money;
28
+ /**
29
+ * Total a list of amounts exactly.
30
+ *
31
+ * @throws {AldusError} `ALDUS_CURRENCY_MISMATCH` if the amounts are not all one currency.
32
+ */
33
+ export declare function sumMoney(amounts: readonly Money[], currency: string): Money;
34
+ /**
35
+ * Compare two amounts: `-1` if `a < b`, `0` if equal, `1` if `a > b`.
36
+ *
37
+ * Numerically equal amounts written differently — `"1.5"` and `"1.50"` — compare equal, because
38
+ * trailing zeros are a presentation choice and treating them as a difference would make a budget
39
+ * check depend on how a provider happened to format its invoice.
40
+ *
41
+ * @throws {AldusError} `ALDUS_CURRENCY_MISMATCH` if the currencies differ.
42
+ */
43
+ export declare function compareMoney(a: Money, b: Money): -1 | 0 | 1;
44
+ /** True if the amount is greater than zero. */
45
+ export declare function isPositiveMoney(money: Money): boolean;
46
+ /** True if the amount is below zero. */
47
+ export declare function isNegativeMoney(money: Money): boolean;
48
+ /** Validate an amount, raising `ALDUS_MONEY_MALFORMED` if it is not an exact decimal. */
49
+ export declare function assertMoney(money: Money, context?: Record<string, unknown>): Money;
50
+ /** Render an amount for an operator-facing message. */
51
+ export declare function formatMoney(money: Money): string;
52
+ //# sourceMappingURL=money.d.ts.map