@githonllc/avouch 0.1.5 → 0.1.6
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/SPEC.md +3 -3
- package/dist/src/expr.js +1 -0
- package/dist/src/reconcile.d.ts +1 -0
- package/dist/src/reconcile.js +18 -7
- package/package.json +1 -1
package/SPEC.md
CHANGED
|
@@ -308,13 +308,13 @@ Types:
|
|
|
308
308
|
| 3 | `expected` is `null` | `cannot_reconcile`, `expectation_missing` |
|
|
309
309
|
| 4 | `snapshot` is `null` | `cannot_reconcile`, `snapshot_missing` |
|
|
310
310
|
| 5 | `decide(ont, action, snapshot, scope, bindings, actor, now)` throws | `cannot_reconcile`, `evaluator_error`; `detail` is the error message |
|
|
311
|
-
| 6 | `reported.result` is neither an invocation result (`INVOCATION_RESULTS`) nor the `result` of a decision row or of `otherwise` of this action | `cannot_reconcile`, `runtime_unmapped`; `detail` is `reported.result` |
|
|
311
|
+
| 6 | `reported.result` is `SCOPE_DENIED`, or is neither an invocation result (`INVOCATION_RESULTS`) nor the `result` of a decision row or of `otherwise` of this action | `cannot_reconcile`, `runtime_unmapped`; `detail` is `reported.result` |
|
|
312
312
|
| 7 | the result is INDETERMINATE | `cannot_reconcile`, `indeterminate`; `detail` is its reason |
|
|
313
313
|
| 8 | otherwise: `evaluator` is `externalResult` of the result without `param` (a domain result is `{result}`; `INVALID_BINDING` keeps `reason`) | `match` with `result = evaluator` when the three results are pairwise equal; else `mismatch` |
|
|
314
314
|
|
|
315
315
|
Two `Reported` values are equal when `result` is equal and `reason` is equal (both absent counts as equal).
|
|
316
316
|
|
|
317
|
-
A missing runtime result is never filled in from the decision table; neither is a missing expectation. The evaluator result is computed only to compare. Envelope results are not reconciled: the evaluator does not implement the envelope, and a replay does not recompute domain conditions, so a replay is not a new decision. An action without a decision table gives no domain result, so every reported result other than
|
|
317
|
+
A missing runtime result is never filled in from the decision table; neither is a missing expectation. The evaluator result is computed only to compare. Envelope results are not reconciled: the evaluator does not implement the envelope, and a replay does not recompute domain conditions, so a replay is not a new decision. An action without a decision table gives no domain result, so every reported result other than `INVALID_BINDING` and `PERMISSION_DENIED` is unmapped for it (envelope results stop at step 1).
|
|
318
318
|
|
|
319
319
|
Once an invocation is inside the domain path of an action with a decision table, the runtime must report the domain result explicitly. A coarse signal, such as the status of a receipt, must not stand in for it: a coarse value can share a name with the expected result and give a false match. `runtime_missing` (nothing reported) and `runtime_unmapped` (a value the action cannot give) record the two ways this evidence fails.
|
|
320
320
|
|
|
@@ -330,7 +330,7 @@ The physical type mapping is checked here: the profile supplies `decode`, which
|
|
|
330
330
|
|
|
331
331
|
### Existence is not revealed
|
|
332
332
|
|
|
333
|
-
`SCOPE_DENIED` is internal. `externalResult` maps it and `INVALID_BINDING` `not_found` to one value, `{result: INVALID_BINDING, param, reason: not_found}`, so a caller cannot learn that an id exists in another tenant. Other reasons, such as `type`, are reported as they are.
|
|
333
|
+
`SCOPE_DENIED` is internal. `externalResult` maps it and `INVALID_BINDING` `not_found` to one value, `{result: INVALID_BINDING, param, reason: not_found}`, so a caller cannot learn that an id exists in another tenant. Other reasons, such as `type`, are reported as they are. A runtime must not report `SCOPE_DENIED`; `reconcile` gives `runtime_unmapped` for it.
|
|
334
334
|
|
|
335
335
|
### Known limit
|
|
336
336
|
|
package/dist/src/expr.js
CHANGED
|
@@ -9,6 +9,7 @@ export const EXPR_NODES = [
|
|
|
9
9
|
export const DISPOSITION_CLASSES = ["rejected", "recorded", "pending", "applied"];
|
|
10
10
|
// The fixed invocation result set of the format. The first two belong to the idempotency envelope. A disposition id may
|
|
11
11
|
// not be one of these names.
|
|
12
|
+
// Every invocation-layer result, SCOPE_DENIED included. SCOPE_DENIED is internal and never reported (SPEC section 9).
|
|
12
13
|
export const INVOCATION_RESULTS = ["REPLAYED", "IDEMPOTENCY_CONFLICT", "INVALID_BINDING", "SCOPE_DENIED", "PERMISSION_DENIED"];
|
|
13
14
|
export class ExprError extends Error {
|
|
14
15
|
path;
|
package/dist/src/reconcile.d.ts
CHANGED
|
@@ -50,4 +50,5 @@ export declare function projectSnapshot(ont: Stage1Ontology, stores: Readonly<Re
|
|
|
50
50
|
snapshot: Snapshot;
|
|
51
51
|
defects: TypeDefect[];
|
|
52
52
|
};
|
|
53
|
+
export declare function domainResults(ont: Stage1Ontology, actionId: string): readonly string[];
|
|
53
54
|
export declare function reconcile(ont: Stage1Ontology, cases: readonly RuntimeCase[]): ReconcileRow[];
|
package/dist/src/reconcile.js
CHANGED
|
@@ -35,12 +35,21 @@ export function projectSnapshot(ont, stores, decode) {
|
|
|
35
35
|
const same = (a, b) => a.result === b.result && a.reason === b.reason;
|
|
36
36
|
const ENVELOPE = ["REPLAYED", "IDEMPOTENCY_CONFLICT"];
|
|
37
37
|
const INVOCATION = INVOCATION_RESULTS;
|
|
38
|
-
// The results
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
38
|
+
// The domain results of an action: each distinct result of its decision rows, in row order, then of otherwise.
|
|
39
|
+
// An action without a decision table gives none. Names only; the class is ont.dispositions[name].class. For an ontology
|
|
40
|
+
// that passes the check, every name is a key of dispositions and none is an invocation result (R1).
|
|
41
|
+
export function domainResults(ont, actionId) {
|
|
42
|
+
const action = own(ont.actionTypes, actionId);
|
|
43
|
+
if (action === undefined)
|
|
44
|
+
throw new Error(`unknown action ${actionId}`);
|
|
45
|
+
const names = [];
|
|
46
|
+
for (const r of action.decision?.rows ?? [])
|
|
47
|
+
if (!names.includes(r.result))
|
|
48
|
+
names.push(r.result);
|
|
49
|
+
const o = action.decision?.otherwise;
|
|
50
|
+
if (o !== undefined && !names.includes(o.result))
|
|
51
|
+
names.push(o.result);
|
|
52
|
+
return names;
|
|
44
53
|
}
|
|
45
54
|
// One row per case, in input order. The evaluator result is computed only to compare: a missing runtime result or
|
|
46
55
|
// expectation is reported as such, never filled in from the decision table. Envelope results are not decisions (the
|
|
@@ -67,7 +76,9 @@ export function reconcile(ont, cases) {
|
|
|
67
76
|
catch (e) {
|
|
68
77
|
return cannot("evaluator_error", e instanceof Error ? e.message : String(e));
|
|
69
78
|
}
|
|
70
|
-
|
|
79
|
+
// SCOPE_DENIED is internal (SPEC section 9, Existence is not revealed): never a reportable result.
|
|
80
|
+
const reportable = reported.result !== "SCOPE_DENIED" && (INVOCATION.includes(reported.result) || domainResults(ont, c.action).includes(reported.result));
|
|
81
|
+
if (!reportable)
|
|
71
82
|
return cannot("runtime_unmapped", reported.result);
|
|
72
83
|
if (d.layer === "indeterminate")
|
|
73
84
|
return cannot("indeterminate", d.reason);
|