avouch 0.1.22

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.
Files changed (54) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +6 -0
  3. package/README.md +356 -0
  4. package/SPEC.md +379 -0
  5. package/dist/cli/avouch.d.ts +2 -0
  6. package/dist/cli/avouch.js +68 -0
  7. package/dist/cli/check.d.ts +86 -0
  8. package/dist/cli/check.js +333 -0
  9. package/dist/cli/facts.d.ts +3 -0
  10. package/dist/cli/facts.js +157 -0
  11. package/dist/cli/gate.d.ts +3 -0
  12. package/dist/cli/gate.js +149 -0
  13. package/dist/cli/init.d.ts +21 -0
  14. package/dist/cli/init.js +282 -0
  15. package/dist/cli/jsonl.d.ts +5 -0
  16. package/dist/cli/jsonl.js +20 -0
  17. package/dist/cli/query.d.ts +2 -0
  18. package/dist/cli/query.js +138 -0
  19. package/dist/cli/reconcile.d.ts +9 -0
  20. package/dist/cli/reconcile.js +131 -0
  21. package/dist/src/checker.d.ts +3 -0
  22. package/dist/src/checker.js +1136 -0
  23. package/dist/src/contract.d.ts +135 -0
  24. package/dist/src/contract.js +3 -0
  25. package/dist/src/coverage.d.ts +65 -0
  26. package/dist/src/coverage.js +237 -0
  27. package/dist/src/evaluate.d.ts +66 -0
  28. package/dist/src/evaluate.js +535 -0
  29. package/dist/src/expr.d.ts +209 -0
  30. package/dist/src/expr.js +497 -0
  31. package/dist/src/index.d.ts +11 -0
  32. package/dist/src/index.js +11 -0
  33. package/dist/src/ledger.d.ts +96 -0
  34. package/dist/src/ledger.js +241 -0
  35. package/dist/src/markdown.d.ts +66 -0
  36. package/dist/src/markdown.js +701 -0
  37. package/dist/src/patch.d.ts +6 -0
  38. package/dist/src/patch.js +37 -0
  39. package/dist/src/query.d.ts +333 -0
  40. package/dist/src/query.js +739 -0
  41. package/dist/src/reconcile.d.ts +61 -0
  42. package/dist/src/reconcile.js +151 -0
  43. package/dist/src/stage1-check.d.ts +31 -0
  44. package/dist/src/stage1-check.js +329 -0
  45. package/dist/src/touched.d.ts +1 -0
  46. package/dist/src/touched.js +9 -0
  47. package/dist/src/unknown.d.ts +10 -0
  48. package/dist/src/unknown.js +173 -0
  49. package/docs/adapter-guide.md +187 -0
  50. package/docs/markdown-convention.md +635 -0
  51. package/ontology.schema.json +1757 -0
  52. package/package.json +57 -0
  53. package/templates/agents-block.md +12 -0
  54. package/vocab.ttl +524 -0
@@ -0,0 +1,135 @@
1
+ export type RuleId = "R1" | "R2" | "R3" | "R4" | "R5" | "R6" | "R7" | "R8" | "R9" | "R10" | "R11" | "R12";
2
+ export declare const RULES: readonly RuleId[];
3
+ /** A name as a whole identifier: not preceded or followed by a letter, a digit or `_`. The empty string matches nothing. */
4
+ export declare const IDENT: (s: string) => RegExp;
5
+ export interface Source {
6
+ anchor: string;
7
+ locator?: string;
8
+ }
9
+ export interface Fact<T> {
10
+ value: T;
11
+ source: Source;
12
+ }
13
+ /** Adapter finding (parse error, duplicate row, harness error). Reported and waived exactly like a violation. */
14
+ export interface Diagnostic {
15
+ rule: RuleId;
16
+ kind: string;
17
+ key: string;
18
+ msg: string;
19
+ }
20
+ export interface Violation {
21
+ rule: string;
22
+ kind: string;
23
+ key: string;
24
+ msg: string;
25
+ }
26
+ export interface Report {
27
+ violations: Violation[];
28
+ waived: Violation[];
29
+ info: string[];
30
+ }
31
+ export type SectionLookup = {
32
+ title: string;
33
+ text: string;
34
+ } | "missing" | "ambiguous";
35
+ /** nodes = all states; initial = targets of initial pseudo-edges (exactly one expected, checked by R3); edges = "FROM->TO". */
36
+ export interface StateMachineFact {
37
+ nodes: string[];
38
+ initial: string[];
39
+ edges: string[];
40
+ }
41
+ /** Stable row identity supplied by the adapter (for example a SQLite rowid); never a key column that an update may change. */
42
+ export type RowId = string | number;
43
+ /** Net committed change of one store, row by row: an insert keeps its after image, an update its changed columns, a delete its before image. */
44
+ export interface StoreDelta {
45
+ inserted: {
46
+ row: RowId;
47
+ after: Record<string, unknown>;
48
+ }[];
49
+ updated: {
50
+ row: RowId;
51
+ changes: Record<string, {
52
+ before: unknown;
53
+ after: unknown;
54
+ }>;
55
+ }[];
56
+ deleted: {
57
+ row: RowId;
58
+ before: Record<string, unknown>;
59
+ }[];
60
+ }
61
+ /** Inputs the evaluation of one invocation depends on. Recorded only; no rule reads them yet. */
62
+ export interface EvaluationInputs {
63
+ bindings: unknown;
64
+ actor: unknown;
65
+ scope: unknown;
66
+ now: unknown;
67
+ }
68
+ /** Committed net change of one invocation: changed stores and the event types written. */
69
+ export interface InvocationDelta {
70
+ stores: Record<string, StoreDelta>;
71
+ events: string[];
72
+ }
73
+ /**
74
+ * One invocation of an operation, with an id that the harness generates. Plain records and arrays (no Map or Set), so the
75
+ * evidence serializes as JSON. Aggregates (changed stores, committed count) are derived by the checker, never stored.
76
+ * `delta` is non-null iff the outcome is committed (a rejected record has `delta: null`, not an omitted key); the checker
77
+ * reports any record that breaks this type (SPEC section 4) as an R7 info diagnostic and never throws on it.
78
+ */
79
+ export type Invocation = {
80
+ id: string;
81
+ rolledBackAttempts: number;
82
+ snapshot: string | null;
83
+ inputs: EvaluationInputs;
84
+ disposition?: string;
85
+ } & ({
86
+ outcome: "committed";
87
+ delta: InvocationDelta;
88
+ } | {
89
+ outcome: "rejected";
90
+ delta: null;
91
+ });
92
+ export interface PermissionRow {
93
+ keys: Set<string>;
94
+ text: string;
95
+ }
96
+ export interface Permissions {
97
+ catalog: Fact<Set<string>>;
98
+ rows: Fact<Map<string, PermissionRow>>;
99
+ quoteNamesKey(quote: string, key: string): boolean;
100
+ }
101
+ export type RuleSwitch = true | {
102
+ disabled: string;
103
+ };
104
+ export interface FormatConfig {
105
+ actionIdPattern: RegExp;
106
+ linkFieldPattern: RegExp;
107
+ stateProperty: string;
108
+ crossContextMechanisms: string[];
109
+ requiredSource: Partial<Record<RuleId, string>>;
110
+ scenarioCoverage?: "printed" | "enforced";
111
+ rules: Record<RuleId, RuleSwitch>;
112
+ idempotencyDeclarationRequired?: boolean;
113
+ evidenceRequired?: boolean;
114
+ }
115
+ export interface SourceFacts {
116
+ config: FormatConfig;
117
+ diagnostics: Diagnostic[];
118
+ info: string[];
119
+ section(anchor: string): SectionLookup;
120
+ containsTerm(term: string): boolean;
121
+ fieldList(anchor: string, objectType: string): Fact<Map<string, {
122
+ nullable: boolean;
123
+ }>> | null;
124
+ storeCatalog?: Fact<Set<string>>;
125
+ stateMachines?: Map<string, Fact<StateMachineFact | null>>;
126
+ transitionTriggers?: Map<string, Map<string, Fact<Set<string>>>>;
127
+ contextMembers?: Map<string, Fact<Set<string>>>;
128
+ eventCatalog?: Fact<Set<string>>;
129
+ scenarios?: {
130
+ ids: Set<string>;
131
+ cites: Map<string, Set<string>>;
132
+ };
133
+ permissions?: Permissions;
134
+ evidence?: Map<string, Invocation[]>;
135
+ }
@@ -0,0 +1,3 @@
1
+ export const RULES = ["R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8", "R9", "R10", "R11", "R12"];
2
+ /** A name as a whole identifier: not preceded or followed by a letter, a digit or `_`. The empty string matches nothing. */
3
+ export const IDENT = (s) => (s === "" ? /(?!)/ : new RegExp(`(?<![A-Za-z0-9_])${s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_])`));
@@ -0,0 +1,65 @@
1
+ import type { ReconcileRow } from "./reconcile.js";
2
+ /** One coverage unit: a row of a decision table (index from 0) or its otherwise. Key text: `<action>:rows.<i>` / `<action>:otherwise`. */
3
+ export type CoverageUnit = {
4
+ action: string;
5
+ row: number | "otherwise";
6
+ };
7
+ /** A unit with the `when` and `result` of its row, so that an entry for a row that moved is stale. */
8
+ export type CoverageDebt = CoverageUnit & {
9
+ when: {
10
+ passes: string;
11
+ } | {
12
+ fails: string;
13
+ } | null;
14
+ result: string;
15
+ };
16
+ /** A condition that a decision row names and that is declared without an expression. */
17
+ export type CoverageGap = {
18
+ action: string;
19
+ condition: string;
20
+ kind: "unspecified";
21
+ };
22
+ export type CoverageExemption = CoverageGap & {
23
+ reason?: string;
24
+ };
25
+ export type CoverageBaseline = {
26
+ avouchCoverage: 1;
27
+ uncovered: CoverageDebt[];
28
+ exempt: CoverageExemption[];
29
+ };
30
+ export type CoverageStale = {
31
+ list: "uncovered";
32
+ action: string;
33
+ row: number | "otherwise";
34
+ why: "duplicate" | "no_such_row" | "moved" | "verified" | "blocked";
35
+ } | {
36
+ list: "exempt";
37
+ action: string;
38
+ condition: string;
39
+ why: "duplicate" | "not_a_gap";
40
+ };
41
+ export type CoverageFailure = {
42
+ id: string;
43
+ action: string;
44
+ why: "mismatch" | "action_unknown" | "row_unknown" | "no_row_matched" | "no_decision_table" | "permission_condition" | "indeterminate" | "runtime_missing" | "runtime_unmapped" | "expectation_unmapped" | "snapshot_missing" | "evaluator_error";
45
+ };
46
+ export interface CoverageResult {
47
+ ok: boolean;
48
+ units: number;
49
+ verified: CoverageUnit[];
50
+ baselined: CoverageUnit[];
51
+ blocked: (CoverageUnit & {
52
+ condition: string;
53
+ })[];
54
+ noTable: string[];
55
+ unasserted: {
56
+ id: string;
57
+ action: string;
58
+ }[];
59
+ uncovered: CoverageDebt[];
60
+ unexempted: CoverageGap[];
61
+ unreasoned: CoverageGap[];
62
+ stale: CoverageStale[];
63
+ failed: CoverageFailure[];
64
+ }
65
+ export declare function verifyCoverage(ont: unknown, rows: readonly ReconcileRow[], baseline: CoverageBaseline | null): CoverageResult;
@@ -0,0 +1,237 @@
1
+ // Decision-row coverage of reconcile rows (`avouch gate verify`). A rule of the tool, not of the format. A pure function:
2
+ // it reads only its arguments, and the order of each output list is fixed.
3
+ import { isObj } from "./expr.js";
4
+ import { queryAction } from "./query.js";
5
+ // Which reasons of cannot_reconcile fail the gate. A replay is not a decision; a case without an expectation states
6
+ // nothing to compare. An indeterminate case has an expectation that the action can give (reconcile steps 4 and 7 come
7
+ // before step 8), and decide does not check it: it fails unless its detail is in DECLARED.
8
+ const FAILS = {
9
+ envelope: false,
10
+ expectation_missing: false,
11
+ indeterminate: true,
12
+ runtime_missing: true,
13
+ runtime_unmapped: true,
14
+ expectation_unmapped: true,
15
+ snapshot_missing: true,
16
+ evaluator_error: true,
17
+ };
18
+ // INDETERMINATE details that do not fail: the details of a gap that an ontology can declare, a condition or a derived
19
+ // property without an expression (a baseline gap, or R12 condition_unspecified), and permission: unknown (R10
20
+ // permission_unknown). The detail of a row is not checked against the ontology.
21
+ const DECLARED = ["unspecified", "permission_unknown"];
22
+ // INDETERMINATE details that fail under their own name; another detail, or none, fails as "indeterminate".
23
+ const NAMED = ["no_row_matched", "no_decision_table", "permission_condition"];
24
+ const sortStr = (a, b) => (a < b ? -1 : a > b ? 1 : 0);
25
+ const show = (x) => JSON.stringify(x) ?? String(x);
26
+ const isRow = (x) => x === "otherwise" || (typeof x === "number" && Number.isSafeInteger(x) && x >= 0);
27
+ const sameWhen = (a, b) => JSON.stringify(a) === JSON.stringify(b);
28
+ function checkKeys(o, path, required, allowed) {
29
+ for (const k of required)
30
+ if (!Object.hasOwn(o, k))
31
+ throw new Error(`baseline: ${path}${k}: missing`);
32
+ for (const k of Object.keys(o))
33
+ if (!allowed.includes(k))
34
+ throw new Error(`baseline: ${path}${k}: unknown key`);
35
+ }
36
+ function checkRows(rows) {
37
+ if (!Array.isArray(rows))
38
+ throw new Error("rows: not an array");
39
+ rows.forEach((x, i) => {
40
+ const at = `rows[${i}]:`;
41
+ if (!isObj(x))
42
+ throw new Error(`${at} not an object`);
43
+ if (typeof x.id !== "string")
44
+ throw new Error(`${at} id is not a string`);
45
+ if (typeof x.action !== "string")
46
+ throw new Error(`${at} action is not a string`);
47
+ if (x.status === "match" || x.status === "mismatch") {
48
+ if (x.row !== undefined && !isRow(x.row))
49
+ throw new Error(`${at} row ${show(x.row)} is not a row index or "otherwise"`);
50
+ }
51
+ else if (x.status === "cannot_reconcile") {
52
+ if (typeof x.reason !== "string" || !Object.hasOwn(FAILS, x.reason))
53
+ throw new Error(`${at} reason ${show(x.reason)} is not a reason of cannot_reconcile`);
54
+ }
55
+ else
56
+ throw new Error(`${at} status ${show(x.status)} is not match, mismatch or cannot_reconcile`);
57
+ });
58
+ }
59
+ function checkBaseline(b) {
60
+ if (!isObj(b))
61
+ throw new Error("baseline: not an object");
62
+ checkKeys(b, "", ["avouchCoverage", "uncovered", "exempt"], ["avouchCoverage", "uncovered", "exempt"]);
63
+ if (b.avouchCoverage !== 1)
64
+ throw new Error(`baseline: avouchCoverage: ${show(b.avouchCoverage)} is not 1`);
65
+ if (!Array.isArray(b.uncovered))
66
+ throw new Error("baseline: uncovered: not an array");
67
+ if (!Array.isArray(b.exempt))
68
+ throw new Error("baseline: exempt: not an array");
69
+ b.uncovered.forEach((x, i) => {
70
+ const at = `uncovered[${i}]`;
71
+ if (!isObj(x))
72
+ throw new Error(`baseline: ${at}: not an object`);
73
+ checkKeys(x, `${at}.`, ["action", "row", "when", "result"], ["action", "row", "when", "result"]);
74
+ if (typeof x.action !== "string")
75
+ throw new Error(`baseline: ${at}.action: not a string`);
76
+ if (!isRow(x.row))
77
+ throw new Error(`baseline: ${at}.row: ${show(x.row)} is not a row index or "otherwise"`);
78
+ const w = x.when;
79
+ const when = w === null || (isObj(w) && Object.keys(w).length === 1 && (typeof w.passes === "string" || typeof w.fails === "string"));
80
+ if (!when)
81
+ throw new Error(`baseline: ${at}.when: not null, {passes: <condition>} or {fails: <condition>}`);
82
+ if (typeof x.result !== "string")
83
+ throw new Error(`baseline: ${at}.result: not a string`);
84
+ });
85
+ b.exempt.forEach((x, i) => {
86
+ const at = `exempt[${i}]`;
87
+ if (!isObj(x))
88
+ throw new Error(`baseline: ${at}: not an object`);
89
+ checkKeys(x, `${at}.`, ["action", "condition", "kind"], ["action", "condition", "kind", "reason"]);
90
+ if (typeof x.action !== "string")
91
+ throw new Error(`baseline: ${at}.action: not a string`);
92
+ if (typeof x.condition !== "string")
93
+ throw new Error(`baseline: ${at}.condition: not a string`);
94
+ if (x.kind !== "unspecified")
95
+ throw new Error(`baseline: ${at}.kind: ${show(x.kind)} is not supported by this version`);
96
+ if (x.reason !== undefined && typeof x.reason !== "string")
97
+ throw new Error(`baseline: ${at}.reason: not a string`);
98
+ });
99
+ }
100
+ // Coverage of the decision rows of every action of the ontology by the match rows. baseline null = both lists empty.
101
+ // Throws for an ontology without an actionTypes object, or rows or a baseline with a wrong shape (the message starts
102
+ // with "ontology:", "rows" or "baseline:").
103
+ export function verifyCoverage(ont, rows, baseline) {
104
+ if (!isObj(ont) || !isObj(ont.actionTypes))
105
+ throw new Error("ontology: actionTypes is not an object");
106
+ const actionTypes = ont.actionTypes;
107
+ checkRows(rows);
108
+ if (baseline !== null)
109
+ checkBaseline(baseline);
110
+ const base = baseline ?? { avouchCoverage: 1, uncovered: [], exempt: [] };
111
+ // 1. Units and gaps. A gap: a condition that a row names (as its own or as an earlier row's) and that has no expression.
112
+ const units = [];
113
+ const unitOf = new Map();
114
+ const gaps = [];
115
+ const noTable = [];
116
+ const key = (action, x) => JSON.stringify([action, x]);
117
+ for (const action of Object.keys(actionTypes).sort(sortStr)) {
118
+ const q = queryAction(ont, action);
119
+ if (q === null)
120
+ continue;
121
+ if (q.decision === null) {
122
+ noTable.push(action);
123
+ continue;
124
+ }
125
+ const isGap = (c) => q.conditions.find((x) => x.id === c)?.expr === null;
126
+ const named = new Set();
127
+ for (const r of q.decision.rows) {
128
+ const u = { action, q: r, gap: r.requires.find((x) => isGap(x.condition))?.condition ?? null };
129
+ units.push(u);
130
+ unitOf.set(key(action, r.row), u);
131
+ for (const x of r.requires)
132
+ if (isGap(x.condition))
133
+ named.add(x.condition);
134
+ }
135
+ for (const condition of [...named].sort(sortStr))
136
+ gaps.push({ action, condition, kind: "unspecified" });
137
+ }
138
+ const gapKeys = new Set(gaps.map((g) => key(g.action, g.condition)));
139
+ // 2. Case rows, in input order.
140
+ const verified = new Set();
141
+ const failed = [];
142
+ const unasserted = [];
143
+ for (const r of rows) {
144
+ const head = { id: r.id, action: r.action };
145
+ if (!Object.hasOwn(actionTypes, r.action))
146
+ failed.push({ ...head, why: "action_unknown" });
147
+ else if (r.status === "mismatch")
148
+ failed.push({ ...head, why: "mismatch" });
149
+ else if (r.status === "cannot_reconcile") {
150
+ if (r.reason === "indeterminate") {
151
+ if (!DECLARED.includes(r.detail))
152
+ failed.push({ ...head, why: NAMED.includes(r.detail) ? r.detail : "indeterminate" });
153
+ }
154
+ else if (FAILS[r.reason])
155
+ failed.push({ ...head, why: r.reason });
156
+ else if (r.reason === "expectation_missing")
157
+ unasserted.push(head);
158
+ }
159
+ else if (r.row !== undefined) {
160
+ // a match on a row: the unit must exist and give the same result
161
+ const u = unitOf.get(key(r.action, r.row));
162
+ const result = isObj(r.result) ? r.result.result : undefined;
163
+ if (u === undefined || u.q.result !== result)
164
+ failed.push({ ...head, why: "row_unknown" });
165
+ else
166
+ verified.add(u);
167
+ }
168
+ }
169
+ // 3. Exemptions, in baseline order.
170
+ const stale = [];
171
+ const exempted = new Set();
172
+ const unreasonedKeys = new Set();
173
+ const seenExempt = new Set();
174
+ for (const e of base.exempt) {
175
+ const k = key(e.action, e.condition);
176
+ const out = { list: "exempt", action: e.action, condition: e.condition };
177
+ if (seenExempt.has(k))
178
+ stale.push({ ...out, why: "duplicate" });
179
+ else if (!gapKeys.has(k))
180
+ stale.push({ ...out, why: "not_a_gap" });
181
+ else {
182
+ exempted.add(k);
183
+ if (e.reason === undefined || e.reason.trim() === "")
184
+ unreasonedKeys.add(k);
185
+ }
186
+ seenExempt.add(k);
187
+ }
188
+ // 4. Uncovered entries, in baseline order. The baseline must have no dead entries.
189
+ const owed = new Set();
190
+ const seenDebt = new Set();
191
+ for (const d of base.uncovered) {
192
+ const k = key(d.action, d.row);
193
+ const u = unitOf.get(k);
194
+ const out = { list: "uncovered", action: d.action, row: d.row };
195
+ if (seenDebt.has(k))
196
+ stale.push({ ...out, why: "duplicate" });
197
+ else if (u === undefined)
198
+ stale.push({ ...out, why: "no_such_row" });
199
+ else if (!sameWhen(d.when, u.q.when) || d.result !== u.q.result)
200
+ stale.push({ ...out, why: "moved" });
201
+ else if (verified.has(u))
202
+ stale.push({ ...out, why: "verified" });
203
+ else if (u.gap !== null)
204
+ stale.push({ ...out, why: "blocked" });
205
+ else
206
+ owed.add(u);
207
+ seenDebt.add(k);
208
+ }
209
+ // 5. Each unit, in unit order.
210
+ const result = {
211
+ ok: false,
212
+ units: units.length,
213
+ verified: [],
214
+ baselined: [],
215
+ blocked: [],
216
+ noTable,
217
+ unasserted,
218
+ uncovered: [],
219
+ unexempted: gaps.filter((g) => !exempted.has(key(g.action, g.condition))),
220
+ unreasoned: gaps.filter((g) => unreasonedKeys.has(key(g.action, g.condition))),
221
+ stale,
222
+ failed,
223
+ };
224
+ for (const u of units) {
225
+ const id = { action: u.action, row: u.q.row };
226
+ if (verified.has(u))
227
+ result.verified.push(id);
228
+ else if (u.gap !== null)
229
+ result.blocked.push({ ...id, condition: u.gap });
230
+ else if (owed.has(u))
231
+ result.baselined.push(id);
232
+ else
233
+ result.uncovered.push({ ...id, when: u.q.when, result: u.q.result });
234
+ }
235
+ result.ok = [result.uncovered, result.unexempted, result.unreasoned, result.stale, result.failed].every((x) => x.length === 0);
236
+ return result;
237
+ }
@@ -0,0 +1,66 @@
1
+ import { type DispositionClass, type Expr, type Stage1Ontology } from "./expr.js";
2
+ export type Value = string | number | boolean | null | readonly (string | number | boolean)[];
3
+ export type Row = Readonly<Record<string, Value>>;
4
+ export interface Snapshot {
5
+ objects: Record<string, readonly Row[]>;
6
+ links?: Record<string, readonly Row[]>;
7
+ }
8
+ export type Actor = {
9
+ id: string;
10
+ keys: readonly string[];
11
+ kind: string;
12
+ };
13
+ export type Bindings = Readonly<Record<string, Value | undefined>>;
14
+ export interface Env {
15
+ action: string;
16
+ snapshot: Snapshot;
17
+ scope: string | null;
18
+ bindings: Bindings;
19
+ actor: Actor;
20
+ now: number;
21
+ }
22
+ export type DecideResult = {
23
+ layer: "invocation";
24
+ result: "INVALID_BINDING";
25
+ param: string;
26
+ reason: "missing" | "unknown_param" | "type" | "not_found";
27
+ } | {
28
+ layer: "invocation";
29
+ result: "SCOPE_DENIED";
30
+ param: string;
31
+ } | {
32
+ layer: "invocation";
33
+ result: "PERMISSION_DENIED";
34
+ } | {
35
+ layer: "domain";
36
+ result: string;
37
+ class: DispositionClass;
38
+ row: number | "otherwise";
39
+ next?: unknown;
40
+ } | {
41
+ layer: "indeterminate";
42
+ reason: "unspecified";
43
+ row: number;
44
+ condition: string;
45
+ derived?: string;
46
+ } | {
47
+ layer: "indeterminate";
48
+ reason: "no_row_matched" | "no_decision_table" | "permission_condition" | "permission_unknown";
49
+ };
50
+ export { INVOCATION_RESULTS } from "./expr.js";
51
+ export declare class UndefinedDerived extends Error {
52
+ readonly derived: string;
53
+ constructor(derived: string);
54
+ }
55
+ type TV = "T" | "F" | "U";
56
+ export declare function evalValue(ont: Stage1Ontology, expr: Expr, env: Env): Value;
57
+ export declare function evalPredicate(ont: Stage1Ontology, expr: Expr, env: Env): TV;
58
+ export declare function decide(ont: Stage1Ontology, actionId: string, snapshot: Snapshot, scope: string | null, bindings: Bindings, actor: Actor, now: number): DecideResult;
59
+ export type ExternalResult = {
60
+ result: string;
61
+ param?: string;
62
+ reason?: "missing" | "unknown_param" | "type" | "not_found";
63
+ };
64
+ export declare function externalResult(r: Exclude<DecideResult, {
65
+ layer: "indeterminate";
66
+ }>): ExternalResult;