eval-quality 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/LICENSE +190 -0
- package/README.md +355 -0
- package/corpus/dev/README.md +36 -0
- package/corpus/dev/compile-seal-example/brief.json +1 -0
- package/corpus/dev/compile-seal-example/contract.json +1 -0
- package/corpus/dev/contracts/absent-collection-locations.json +1 -0
- package/corpus/dev/contracts/absent-sibling-groups.json +1 -0
- package/corpus/dev/contracts/absent-success-indicator.json +1 -0
- package/corpus/dev/contracts/empty-channel-roles.json +1 -0
- package/corpus/dev/contracts/empty-collection-locations.json +1 -0
- package/corpus/dev/contracts/empty-request-shapes.json +1 -0
- package/corpus/dev/contracts/empty-sibling-groups.json +1 -0
- package/corpus/dev/contracts/no-collection-quantifier.json +1 -0
- package/corpus/dev/contracts/no-operation-inventory.json +1 -0
- package/corpus/dev/contracts/no-read-back-relation.json +1 -0
- package/corpus/dev/contracts/no-state-change-marker.json +1 -0
- package/corpus/dev/contracts/no-type-violating-step.json +1 -0
- package/corpus/dev/contracts/per-key-split-oracles.json +1 -0
- package/corpus/dev/contracts/satisfied-declarations.json +1 -0
- package/corpus/dev/contracts/single-required-response-key.json +1 -0
- package/corpus/dev/contracts/split-indicator-oracle.json +1 -0
- package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -0
- package/corpus/dev/contracts/unnamed-reference-set.json +1 -0
- package/corpus/dev/contracts/wrong-cardinality-form.json +1 -0
- package/corpus/dev/index.json +1 -0
- package/dist/adapters/index.d.ts +12 -0
- package/dist/adapters/index.js +3 -0
- package/dist/adapters/local-corpus-adapter.d.ts +7 -0
- package/dist/adapters/local-corpus-adapter.js +78 -0
- package/dist/adapters/node-file-system-adapter.d.ts +7 -0
- package/dist/adapters/node-file-system-adapter.js +39 -0
- package/dist/adapters/port-boundary.d.ts +18 -0
- package/dist/adapters/port-boundary.js +83 -0
- package/dist/adapters/system-clock-adapter.d.ts +10 -0
- package/dist/adapters/system-clock-adapter.js +18 -0
- package/dist/application/compile.d.ts +4 -0
- package/dist/application/compile.js +24 -0
- package/dist/application/diagnostics.d.ts +22 -0
- package/dist/application/diagnostics.js +18 -0
- package/dist/application/index.d.ts +23 -0
- package/dist/application/index.js +17 -0
- package/dist/application/invoke-port.d.ts +2 -0
- package/dist/application/invoke-port.js +45 -0
- package/dist/application/preflight.d.ts +27 -0
- package/dist/application/preflight.js +131 -0
- package/dist/application/seal.d.ts +4 -0
- package/dist/application/seal.js +28 -0
- package/dist/application/serialize.d.ts +6 -0
- package/dist/application/serialize.js +9 -0
- package/dist/cli/arguments.d.ts +28 -0
- package/dist/cli/arguments.js +167 -0
- package/dist/cli/exit-codes.d.ts +36 -0
- package/dist/cli/exit-codes.js +39 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +143 -0
- package/dist/cli/render.d.ts +26 -0
- package/dist/cli/render.js +53 -0
- package/dist/cli/run.d.ts +41 -0
- package/dist/cli/run.js +203 -0
- package/dist/core/canonical/canonicalize.d.ts +1 -0
- package/dist/core/canonical/canonicalize.js +113 -0
- package/dist/core/canonical/digest.d.ts +7 -0
- package/dist/core/canonical/digest.js +81 -0
- package/dist/core/canonical/scan-json.d.ts +1 -0
- package/dist/core/canonical/scan-json.js +298 -0
- package/dist/core/canonical/value-domain.d.ts +4 -0
- package/dist/core/canonical/value-domain.js +113 -0
- package/dist/core/compile/compile.d.ts +24 -0
- package/dist/core/compile/compile.js +67 -0
- package/dist/core/compile/declarations.d.ts +5 -0
- package/dist/core/compile/declarations.js +22 -0
- package/dist/core/compile/expression-legality.d.ts +14 -0
- package/dist/core/compile/expression-legality.js +346 -0
- package/dist/core/compile/forbidden-inputs.d.ts +5 -0
- package/dist/core/compile/forbidden-inputs.js +19 -0
- package/dist/core/compile/interface-inventory.d.ts +10 -0
- package/dist/core/compile/interface-inventory.js +55 -0
- package/dist/core/compile/oracle-alignment.d.ts +14 -0
- package/dist/core/compile/oracle-alignment.js +93 -0
- package/dist/core/compile/reachability.d.ts +17 -0
- package/dist/core/compile/reachability.js +177 -0
- package/dist/core/compile/rubrics.d.ts +55 -0
- package/dist/core/compile/rubrics.js +198 -0
- package/dist/core/compile/scripting-bound.d.ts +5 -0
- package/dist/core/compile/scripting-bound.js +151 -0
- package/dist/core/compile/sensitivity-witness.d.ts +73 -0
- package/dist/core/compile/sensitivity-witness.js +232 -0
- package/dist/core/compile/waivers.d.ts +3 -0
- package/dist/core/compile/waivers.js +21 -0
- package/dist/core/coverage/coverage.d.ts +18 -0
- package/dist/core/coverage/coverage.js +47 -0
- package/dist/core/coverage/relevance.d.ts +75 -0
- package/dist/core/coverage/relevance.js +186 -0
- package/dist/core/coverage/rules.d.ts +7 -0
- package/dist/core/coverage/rules.js +18 -0
- package/dist/core/coverage/satisfaction.d.ts +107 -0
- package/dist/core/coverage/satisfaction.js +415 -0
- package/dist/core/coverage/table.d.ts +26 -0
- package/dist/core/coverage/table.js +222 -0
- package/dist/core/declared-inputs.d.ts +16 -0
- package/dist/core/declared-inputs.js +18 -0
- package/dist/core/evaluate/evidence-resolution.d.ts +54 -0
- package/dist/core/evaluate/evidence-resolution.js +153 -0
- package/dist/core/evaluate/operators.d.ts +87 -0
- package/dist/core/evaluate/operators.js +350 -0
- package/dist/core/evaluate/resolution.d.ts +40 -0
- package/dist/core/evaluate/resolution.js +336 -0
- package/dist/core/evaluate/resolved-value.d.ts +9 -0
- package/dist/core/evaluate/resolved-value.js +6 -0
- package/dist/core/failure-codes.d.ts +18 -0
- package/dist/core/failure-codes.js +50 -0
- package/dist/core/lineage/chain.d.ts +49 -0
- package/dist/core/lineage/chain.js +210 -0
- package/dist/core/lineage/freeze.d.ts +12 -0
- package/dist/core/lineage/freeze.js +35 -0
- package/dist/core/lineage/stage-table.d.ts +43 -0
- package/dist/core/lineage/stage-table.js +106 -0
- package/dist/core/preflight/plan.d.ts +67 -0
- package/dist/core/preflight/plan.js +313 -0
- package/dist/core/preflight/projection.d.ts +40 -0
- package/dist/core/preflight/projection.js +96 -0
- package/dist/core/preflight/reduce.d.ts +8 -0
- package/dist/core/preflight/reduce.js +180 -0
- package/dist/core/preflight/witness-evidence.d.ts +42 -0
- package/dist/core/preflight/witness-evidence.js +88 -0
- package/dist/core/probe/target-policy.d.ts +63 -0
- package/dist/core/probe/target-policy.js +328 -0
- package/dist/core/schemas/artifact-reference.d.ts +19 -0
- package/dist/core/schemas/artifact-reference.js +30 -0
- package/dist/core/schemas/artifact.d.ts +1153 -0
- package/dist/core/schemas/artifact.js +84 -0
- package/dist/core/schemas/constraint-ledger.d.ts +57 -0
- package/dist/core/schemas/constraint-ledger.js +143 -0
- package/dist/core/schemas/eval-contract.d.ts +402 -0
- package/dist/core/schemas/eval-contract.js +147 -0
- package/dist/core/schemas/evaluator-configuration.d.ts +37 -0
- package/dist/core/schemas/evaluator-configuration.js +43 -0
- package/dist/core/schemas/evidence-artifact.d.ts +471 -0
- package/dist/core/schemas/evidence-artifact.js +232 -0
- package/dist/core/schemas/expression.d.ts +197 -0
- package/dist/core/schemas/expression.js +284 -0
- package/dist/core/schemas/faults.d.ts +9 -0
- package/dist/core/schemas/faults.js +32 -0
- package/dist/core/schemas/interface.d.ts +417 -0
- package/dist/core/schemas/interface.js +115 -0
- package/dist/core/schemas/isolation-manifest.d.ts +136 -0
- package/dist/core/schemas/isolation-manifest.js +92 -0
- package/dist/core/schemas/lineage.d.ts +21 -0
- package/dist/core/schemas/lineage.js +28 -0
- package/dist/core/schemas/oracle.d.ts +72 -0
- package/dist/core/schemas/oracle.js +35 -0
- package/dist/core/schemas/plan.d.ts +115 -0
- package/dist/core/schemas/plan.js +58 -0
- package/dist/core/schemas/pointer.d.ts +63 -0
- package/dist/core/schemas/pointer.js +94 -0
- package/dist/core/schemas/port-messages.d.ts +103 -0
- package/dist/core/schemas/port-messages.js +90 -0
- package/dist/core/schemas/preflight-verdict.d.ts +60 -0
- package/dist/core/schemas/preflight-verdict.js +38 -0
- package/dist/core/schemas/primitives.d.ts +78 -0
- package/dist/core/schemas/primitives.js +121 -0
- package/dist/core/schemas/private-artifact-manifest.d.ts +56 -0
- package/dist/core/schemas/private-artifact-manifest.js +46 -0
- package/dist/core/schemas/probe-body.d.ts +25 -0
- package/dist/core/schemas/probe-body.js +23 -0
- package/dist/core/schemas/probe-policy.d.ts +76 -0
- package/dist/core/schemas/probe-policy.js +33 -0
- package/dist/core/schemas/probe.d.ts +189 -0
- package/dist/core/schemas/probe.js +82 -0
- package/dist/core/schemas/publish.d.ts +30 -0
- package/dist/core/schemas/publish.js +135 -0
- package/dist/core/schemas/reference-set.d.ts +18 -0
- package/dist/core/schemas/reference-set.js +27 -0
- package/dist/core/schemas/rubric.d.ts +62 -0
- package/dist/core/schemas/rubric.js +68 -0
- package/dist/core/schemas/scoring-policy.d.ts +28 -0
- package/dist/core/schemas/scoring-policy.js +51 -0
- package/dist/core/schemas/sealed-evaluator-brief.d.ts +80 -0
- package/dist/core/schemas/sealed-evaluator-brief.js +59 -0
- package/dist/core/schemas/sealed-run-record.d.ts +382 -0
- package/dist/core/schemas/sealed-run-record.js +215 -0
- package/dist/core/schemas/sensitivity-witness.d.ts +138 -0
- package/dist/core/schemas/sensitivity-witness.js +86 -0
- package/dist/core/schemas/verdict.d.ts +25 -0
- package/dist/core/schemas/verdict.js +20 -0
- package/dist/core/schemas/waiver.d.ts +17 -0
- package/dist/core/schemas/waiver.js +24 -0
- package/dist/core/seal/derived-reference.d.ts +36 -0
- package/dist/core/seal/derived-reference.js +383 -0
- package/dist/core/seal/direction-prose.d.ts +13 -0
- package/dist/core/seal/direction-prose.js +120 -0
- package/dist/core/seal/plan-index.d.ts +63 -0
- package/dist/core/seal/plan-index.js +155 -0
- package/dist/core/seal/scripting-audit.d.ts +15 -0
- package/dist/core/seal/scripting-audit.js +51 -0
- package/dist/core/seal/seal.d.ts +18 -0
- package/dist/core/seal/seal.js +155 -0
- package/dist/core/stage-contracts.d.ts +30 -0
- package/dist/core/stage-contracts.js +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +22 -0
- package/dist/ports/clock-port.d.ts +13 -0
- package/dist/ports/clock-port.js +7 -0
- package/dist/ports/corpus-port.d.ts +16 -0
- package/dist/ports/corpus-port.js +7 -0
- package/dist/ports/environment-probe-port.d.ts +76 -0
- package/dist/ports/environment-probe-port.js +32 -0
- package/dist/ports/file-system-port.d.ts +27 -0
- package/dist/ports/file-system-port.js +11 -0
- package/dist/ports/port.d.ts +33 -0
- package/dist/ports/port.js +14 -0
- package/dist/testing/conformance.d.ts +88 -0
- package/dist/testing/conformance.js +281 -0
- package/dist/testing/index.d.ts +26 -0
- package/dist/testing/index.js +18 -0
- package/dist/testing/probe-conformance.d.ts +45 -0
- package/dist/testing/probe-conformance.js +186 -0
- package/package.json +130 -0
- package/schemas/artifact-reference.schema.json +64 -0
- package/schemas/eval-contract.schema.json +2145 -0
- package/schemas/evaluator-configuration.schema.json +199 -0
- package/schemas/evidence-artifact.schema.json +1229 -0
- package/schemas/isolation-manifest.schema.json +394 -0
- package/schemas/preflight-verdict.schema.json +114 -0
- package/schemas/private-artifact-manifest.schema.json +103 -0
- package/schemas/probe.schema.json +1151 -0
- package/schemas/rubric.schema.json +148 -0
- package/schemas/scoring-policy.schema.json +90 -0
- package/schemas/sealed-evaluator-brief.schema.json +268 -0
- package/schemas/sealed-run-record.schema.json +875 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { absence, containment, countTolerance, coversByKey, deepEquality, equality, existence, ordering, regexMatch, setMembership, shape, } from './operators.js';
|
|
2
|
+
import { ABSENT } from './resolved-value.js';
|
|
3
|
+
/**
|
|
4
|
+
* AD-4's one closed introduction condition, checked per operand and applied
|
|
5
|
+
* uniformly: the resolved value and its operand are the only inputs.
|
|
6
|
+
*
|
|
7
|
+
* Two permanent consequences of that uniform reading, the same class as a
|
|
8
|
+
* `{ literal: [] }` operand tripping it: a `count-tolerance` node asserting
|
|
9
|
+
* `expected: 0` over a genuinely empty collection can never resolve `true`,
|
|
10
|
+
* because this interception fires first; and `existence` over a pointer
|
|
11
|
+
* resolving to a present-but-empty array resolves `insufficient-evidence`, not
|
|
12
|
+
* `true`, even though `existence` only asks about presence.
|
|
13
|
+
*/
|
|
14
|
+
function operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection) {
|
|
15
|
+
if (Array.isArray(resolved) && resolved.length === 0)
|
|
16
|
+
return true;
|
|
17
|
+
if (resolved !== ABSENT)
|
|
18
|
+
return false;
|
|
19
|
+
// Only a `{ pointer }` operand can carry a declared collection type.
|
|
20
|
+
// `{ literal }` never resolves ABSENT, and an ABSENT `{ referenceSet }`
|
|
21
|
+
// means `unresolved-reference-set` slipped past compilation, which this
|
|
22
|
+
// module assumes cannot happen.
|
|
23
|
+
return 'pointer' in operand && pointerDenotesCollection(operand.pointer);
|
|
24
|
+
}
|
|
25
|
+
function emptyCollectionResult() {
|
|
26
|
+
return {
|
|
27
|
+
resolution: 'insufficient-evidence',
|
|
28
|
+
introductionCondition: 'empty-collection',
|
|
29
|
+
children: [],
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function booleanResult(result) {
|
|
33
|
+
return {
|
|
34
|
+
resolution: result ? 'true' : 'false',
|
|
35
|
+
introductionCondition: null,
|
|
36
|
+
children: [],
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Checked across every operand before any operator runs, so the interception
|
|
40
|
+
// replaces a leaf's own two-valued answer outright.
|
|
41
|
+
function anyOperandEmpty(pairs, pointerDenotesCollection) {
|
|
42
|
+
return pairs.some(({ operand, resolved }) => operandDenotesEmptyCollection(resolved, operand, pointerDenotesCollection));
|
|
43
|
+
}
|
|
44
|
+
/** `not(insufficient-evidence)` is terminal under both polarities (AD-4). */
|
|
45
|
+
function notOf(child) {
|
|
46
|
+
if (child === 'insufficient-evidence')
|
|
47
|
+
return 'insufficient-evidence';
|
|
48
|
+
return child === 'true' ? 'false' : 'true';
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A genuine `false` stays decisive beside an `insufficient-evidence` sibling: a
|
|
52
|
+
* detected defect is information. `CONNECTIVE_MINIMUM_ARITY` (`expression.ts`)
|
|
53
|
+
* keeps the array from ever being empty, so the vacuous `true` never arises.
|
|
54
|
+
*/
|
|
55
|
+
function allOf(children) {
|
|
56
|
+
if (children.some((child) => child === 'false'))
|
|
57
|
+
return 'false';
|
|
58
|
+
if (children.some((child) => child === 'insufficient-evidence')) {
|
|
59
|
+
return 'insufficient-evidence';
|
|
60
|
+
}
|
|
61
|
+
return 'true';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Weaker than disjunction on purpose: a sibling resolving `true` never rescues
|
|
65
|
+
* one that examined nothing. Same arity floor as `allOf`.
|
|
66
|
+
*/
|
|
67
|
+
function anyOf(children) {
|
|
68
|
+
if (children.some((child) => child === 'insufficient-evidence')) {
|
|
69
|
+
return 'insufficient-evidence';
|
|
70
|
+
}
|
|
71
|
+
if (children.some((child) => child === 'true'))
|
|
72
|
+
return 'true';
|
|
73
|
+
return 'false';
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* A quantifier's `collection` field resolves through the same
|
|
77
|
+
* `resolveOperand`/`pointerDenotesCollection` pair as any other operand, with one
|
|
78
|
+
* exception: `ABSENT` here is unconditionally an empty collection and
|
|
79
|
+
* `pointerDenotesCollection` is never consulted. A `collection` field is a
|
|
80
|
+
* collection by definition, so there is nothing to disambiguate.
|
|
81
|
+
*/
|
|
82
|
+
function resolveQuantifier(op, collectionOperand, predicate, boundElement, ctx) {
|
|
83
|
+
// Resolved once, before the loop, against whatever `boundElement` was
|
|
84
|
+
// already in scope (an outer quantifier's element if nested, `ABSENT` at
|
|
85
|
+
// the root): never against an element this quantifier's own loop below
|
|
86
|
+
// has not bound yet.
|
|
87
|
+
const collection = ctx.resolveOperand(collectionOperand, boundElement, ctx.artifactPath);
|
|
88
|
+
if (collection === ABSENT ||
|
|
89
|
+
!Array.isArray(collection) ||
|
|
90
|
+
collection.length === 0) {
|
|
91
|
+
// One guard, three cases: ABSENT, a non-array type mismatch, and a
|
|
92
|
+
// genuinely empty array. All three collapse onto the same
|
|
93
|
+
// `empty-collection` value: the evidence artifact cannot tell which of the
|
|
94
|
+
// three fired. Separating them would need a second introduction-condition
|
|
95
|
+
// value, a schema change out of scope here.
|
|
96
|
+
return emptyCollectionResult();
|
|
97
|
+
}
|
|
98
|
+
const children = collection.map((element) => resolveNode(predicate, element, ctx));
|
|
99
|
+
const childResolutions = children.map((child) => child.resolution);
|
|
100
|
+
return {
|
|
101
|
+
resolution: op === 'for-all' ? allOf(childResolutions) : anyOf(childResolutions),
|
|
102
|
+
// Null even when the fold below reads insufficient-evidence. This node did
|
|
103
|
+
// not trip the empty-collection condition itself; a child did, and still
|
|
104
|
+
// carries it, reachable through `children`.
|
|
105
|
+
introductionCondition: null,
|
|
106
|
+
children,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
// Shared by the six single-operand leaves: resolve, intercept on the
|
|
110
|
+
// empty-collection condition, otherwise hand the value to the operator.
|
|
111
|
+
function resolveSingleOperand(operand, boundElement, ctx, evaluate) {
|
|
112
|
+
const resolved = ctx.resolveOperand(operand, boundElement, ctx.artifactPath);
|
|
113
|
+
if (anyOperandEmpty([{ operand, resolved }], ctx.pointerDenotesCollection)) {
|
|
114
|
+
return emptyCollectionResult();
|
|
115
|
+
}
|
|
116
|
+
return booleanResult(evaluate(resolved));
|
|
117
|
+
}
|
|
118
|
+
// Shared by `equality` and `deep-equality`, which differ only in the operator
|
|
119
|
+
// they call.
|
|
120
|
+
function resolveEqualityLike(operands, evaluate, boundElement, ctx) {
|
|
121
|
+
const [aOperand, bOperand] = operands;
|
|
122
|
+
const a = ctx.resolveOperand(aOperand, boundElement, ctx.artifactPath);
|
|
123
|
+
const b = ctx.resolveOperand(bOperand, boundElement, ctx.artifactPath);
|
|
124
|
+
if (anyOperandEmpty([
|
|
125
|
+
{ operand: aOperand, resolved: a },
|
|
126
|
+
{ operand: bOperand, resolved: b },
|
|
127
|
+
], ctx.pointerDenotesCollection)) {
|
|
128
|
+
return emptyCollectionResult();
|
|
129
|
+
}
|
|
130
|
+
return booleanResult(evaluate(a, b, ctx.artifactPath));
|
|
131
|
+
}
|
|
132
|
+
function resolveNotNode(expression, boundElement, ctx) {
|
|
133
|
+
const [child] = expression.operands;
|
|
134
|
+
const resolved = resolveNode(child, boundElement, ctx);
|
|
135
|
+
return {
|
|
136
|
+
resolution: notOf(resolved.resolution),
|
|
137
|
+
// A fold, not a firing. `resolved` still carries the condition if it is
|
|
138
|
+
// the one that tripped it.
|
|
139
|
+
introductionCondition: null,
|
|
140
|
+
children: [resolved],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
// Shared by `all` and `any`, which differ only in the fold they apply.
|
|
144
|
+
function resolveConnective(expression, fold, boundElement, ctx) {
|
|
145
|
+
// Total, never short-circuiting, over resolutions: every operand is
|
|
146
|
+
// recursed into before folding, whatever an earlier one resolved. Not
|
|
147
|
+
// total over faults (P5): `.map()` still stops at the first operand whose
|
|
148
|
+
// own resolution throws, so a later operand's fault is never reached.
|
|
149
|
+
const children = expression.operands.map((operand) => resolveNode(operand, boundElement, ctx));
|
|
150
|
+
return {
|
|
151
|
+
resolution: fold(children.map((child) => child.resolution)),
|
|
152
|
+
// A fold. The tripped child, if any, carries the condition itself, in
|
|
153
|
+
// `children`.
|
|
154
|
+
introductionCondition: null,
|
|
155
|
+
children,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
function resolveAllNode(expression, boundElement, ctx) {
|
|
159
|
+
return resolveConnective(expression, allOf, boundElement, ctx);
|
|
160
|
+
}
|
|
161
|
+
function resolveAnyNode(expression, boundElement, ctx) {
|
|
162
|
+
return resolveConnective(expression, anyOf, boundElement, ctx);
|
|
163
|
+
}
|
|
164
|
+
function resolveQuantifierNode(expression, boundElement, ctx) {
|
|
165
|
+
return resolveQuantifier(expression.op, expression.collection, expression.predicate, boundElement, ctx);
|
|
166
|
+
}
|
|
167
|
+
function resolveCoversByKeyNode(expression, boundElement, ctx) {
|
|
168
|
+
const [expectedOperand, actualOperand] = expression.operands;
|
|
169
|
+
const { expectedKey, actualKey } = expression;
|
|
170
|
+
const expectedResolved = ctx.resolveOperand(expectedOperand, boundElement, ctx.artifactPath);
|
|
171
|
+
const actualResolved = ctx.resolveOperand(actualOperand, boundElement, ctx.artifactPath);
|
|
172
|
+
// Checked first and unconditionally, before anything else looks at
|
|
173
|
+
// `actual`. A malformed `expected` is a resolver integration bug, never a
|
|
174
|
+
// data outcome, and must never be masked by whatever `actual` resolved to,
|
|
175
|
+
// including a benign empty collection.
|
|
176
|
+
if (expectedResolved !== ABSENT && !Array.isArray(expectedResolved)) {
|
|
177
|
+
throw new Error("covers-by-key's expected-operand guard: a reference-set operand " +
|
|
178
|
+
'must resolve to ABSENT or an array, which the schema guarantees. ' +
|
|
179
|
+
'The injected ResolveOperand returned something else — either a ' +
|
|
180
|
+
'resolver integration bug, or an unresolved reference set that ' +
|
|
181
|
+
'slipped past compilation (unresolved-reference-set); this guard ' +
|
|
182
|
+
'cannot tell the two apart.');
|
|
183
|
+
}
|
|
184
|
+
// Genuine emptiness (AD-4's "two empty collections" case, generalized to a
|
|
185
|
+
// single empty operand) applies only once both operands are confirmed
|
|
186
|
+
// ordinary, present collections. An ABSENT `actual` is AD-26's decisive
|
|
187
|
+
// `false` over a pointer that did not resolve; a non-array `actual` is an
|
|
188
|
+
// operand type `covers-by-key` does not accept, which AD-4 assigns to
|
|
189
|
+
// `malformed-operator-expression` and which resolves `false` here because
|
|
190
|
+
// no compile-time check covers this position. Both outrank emptiness.
|
|
191
|
+
// Delegating to `coversByKey` for those two cases keeps the check
|
|
192
|
+
// single-sourced: it already implements both as its own top guards.
|
|
193
|
+
const bothPresentArrays = expectedResolved !== ABSENT &&
|
|
194
|
+
actualResolved !== ABSENT &&
|
|
195
|
+
Array.isArray(actualResolved);
|
|
196
|
+
if (bothPresentArrays &&
|
|
197
|
+
(expectedResolved.length === 0 || actualResolved.length === 0)) {
|
|
198
|
+
return emptyCollectionResult();
|
|
199
|
+
}
|
|
200
|
+
return booleanResult(coversByKey(expectedResolved, actualResolved, expectedKey, actualKey, ctx.artifactPath));
|
|
201
|
+
}
|
|
202
|
+
function resolveEqualityNode(expression, boundElement, ctx) {
|
|
203
|
+
return resolveEqualityLike(expression.operands, equality, boundElement, ctx);
|
|
204
|
+
}
|
|
205
|
+
function resolveDeepEqualityNode(expression, boundElement, ctx) {
|
|
206
|
+
return resolveEqualityLike(expression.operands, deepEquality, boundElement, ctx);
|
|
207
|
+
}
|
|
208
|
+
function resolveContainmentNode(expression, boundElement, ctx) {
|
|
209
|
+
const [containerOperand, candidateOperand] = expression.operands;
|
|
210
|
+
const container = ctx.resolveOperand(containerOperand, boundElement, ctx.artifactPath);
|
|
211
|
+
const candidate = ctx.resolveOperand(candidateOperand, boundElement, ctx.artifactPath);
|
|
212
|
+
if (anyOperandEmpty([
|
|
213
|
+
{ operand: containerOperand, resolved: container },
|
|
214
|
+
{ operand: candidateOperand, resolved: candidate },
|
|
215
|
+
], ctx.pointerDenotesCollection)) {
|
|
216
|
+
return emptyCollectionResult();
|
|
217
|
+
}
|
|
218
|
+
// The array-narrowing guard applies only to a `{ referenceSet }` candidate.
|
|
219
|
+
// A `{ pointer }` or `{ literal }` candidate legally resolves to a scalar
|
|
220
|
+
// (the stdout/stderr substring shape), and `containment` already handles
|
|
221
|
+
// both shapes.
|
|
222
|
+
if ('referenceSet' in candidateOperand && !Array.isArray(candidate)) {
|
|
223
|
+
throw new Error("containment's referenceSet-candidate guard: a { referenceSet } " +
|
|
224
|
+
'operand must resolve to an array, which the schema guarantees. ' +
|
|
225
|
+
'The injected ResolveOperand returned something else — either a ' +
|
|
226
|
+
'resolver integration bug, or an unresolved reference set that ' +
|
|
227
|
+
'slipped past compilation (unresolved-reference-set); this guard ' +
|
|
228
|
+
'cannot tell the two apart.');
|
|
229
|
+
}
|
|
230
|
+
return booleanResult(containment(container, candidate, ctx.artifactPath));
|
|
231
|
+
}
|
|
232
|
+
function resolveExistenceNode(expression, boundElement, ctx) {
|
|
233
|
+
const [operand] = expression.operands;
|
|
234
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => existence(resolved, ctx.artifactPath));
|
|
235
|
+
}
|
|
236
|
+
function resolveAbsenceNode(expression, boundElement, ctx) {
|
|
237
|
+
const [operand] = expression.operands;
|
|
238
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => absence(resolved, ctx.artifactPath));
|
|
239
|
+
}
|
|
240
|
+
function resolveRegexNode(expression, boundElement, ctx) {
|
|
241
|
+
const [operand] = expression.operands;
|
|
242
|
+
const { pattern } = expression;
|
|
243
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => regexMatch(resolved, pattern, ctx.regexMatchStepBudget, ctx.artifactPath));
|
|
244
|
+
}
|
|
245
|
+
function resolveSetMembershipNode(expression, boundElement, ctx) {
|
|
246
|
+
const [valueOperand, setOperand] = expression.operands;
|
|
247
|
+
const value = ctx.resolveOperand(valueOperand, boundElement, ctx.artifactPath);
|
|
248
|
+
const resolvedSet = ctx.resolveOperand(setOperand, boundElement, ctx.artifactPath);
|
|
249
|
+
if (anyOperandEmpty([
|
|
250
|
+
{ operand: valueOperand, resolved: value },
|
|
251
|
+
{ operand: setOperand, resolved: resolvedSet },
|
|
252
|
+
], ctx.pointerDenotesCollection)) {
|
|
253
|
+
return emptyCollectionResult();
|
|
254
|
+
}
|
|
255
|
+
// The set position needs the `JsonValue[]` the schema already guarantees
|
|
256
|
+
// here. Narrow at runtime so a broken resolver fails loudly.
|
|
257
|
+
if (!Array.isArray(resolvedSet)) {
|
|
258
|
+
throw new Error("set-membership's set-operand guard: its SetOperand position must " +
|
|
259
|
+
'resolve to an array, which the schema guarantees. The injected ' +
|
|
260
|
+
'ResolveOperand returned something else — either a resolver ' +
|
|
261
|
+
'integration bug, or an unresolved reference set that slipped past ' +
|
|
262
|
+
'compilation (unresolved-reference-set); this guard cannot tell the ' +
|
|
263
|
+
'two apart.');
|
|
264
|
+
}
|
|
265
|
+
return booleanResult(setMembership(value, resolvedSet, ctx.artifactPath));
|
|
266
|
+
}
|
|
267
|
+
function resolveOrderingNode(expression, boundElement, ctx) {
|
|
268
|
+
const [operand] = expression.operands;
|
|
269
|
+
const { key, order } = expression;
|
|
270
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => ordering(resolved, key, order, ctx.artifactPath));
|
|
271
|
+
}
|
|
272
|
+
function resolveCountToleranceNode(expression, boundElement, ctx) {
|
|
273
|
+
const [operand] = expression.operands;
|
|
274
|
+
const { expected, tolerance, relative } = expression;
|
|
275
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => countTolerance(resolved, expected, tolerance, relative, ctx.artifactPath));
|
|
276
|
+
}
|
|
277
|
+
function resolveShapeNode(expression, boundElement, ctx) {
|
|
278
|
+
const [operand] = expression.operands;
|
|
279
|
+
const { descriptor } = expression;
|
|
280
|
+
return resolveSingleOperand(operand, boundElement, ctx, (resolved) => shape(resolved, descriptor, ctx.artifactPath));
|
|
281
|
+
}
|
|
282
|
+
// One entry per `Expression['op']`; the mapped type below fails to compile if
|
|
283
|
+
// an op is missing or misassigned, since each key demands the handler typed
|
|
284
|
+
// for exactly that variant. That check covers the closed union at compile
|
|
285
|
+
// time; it cannot see an out-of-union op arriving at runtime, which is what
|
|
286
|
+
// the guard in `resolveNode` is for.
|
|
287
|
+
const operatorHandlers = {
|
|
288
|
+
not: resolveNotNode,
|
|
289
|
+
all: resolveAllNode,
|
|
290
|
+
any: resolveAnyNode,
|
|
291
|
+
'for-all': resolveQuantifierNode,
|
|
292
|
+
'for-any': resolveQuantifierNode,
|
|
293
|
+
'covers-by-key': resolveCoversByKeyNode,
|
|
294
|
+
equality: resolveEqualityNode,
|
|
295
|
+
'deep-equality': resolveDeepEqualityNode,
|
|
296
|
+
containment: resolveContainmentNode,
|
|
297
|
+
existence: resolveExistenceNode,
|
|
298
|
+
absence: resolveAbsenceNode,
|
|
299
|
+
regex: resolveRegexNode,
|
|
300
|
+
'set-membership': resolveSetMembershipNode,
|
|
301
|
+
ordering: resolveOrderingNode,
|
|
302
|
+
'count-tolerance': resolveCountToleranceNode,
|
|
303
|
+
shape: resolveShapeNode,
|
|
304
|
+
};
|
|
305
|
+
/**
|
|
306
|
+
* The recursive worker behind `resolveCheck`. Every `RuntimeFault` a leaf
|
|
307
|
+
* operator throws (only `regexMatch`'s two, currently) propagates undecorated;
|
|
308
|
+
* no branch here catches one.
|
|
309
|
+
*/
|
|
310
|
+
function resolveNode(expression, boundElement, ctx) {
|
|
311
|
+
// `operatorHandlers` is keyed by the closed `Expression['op']` union, so
|
|
312
|
+
// TypeScript sees no `undefined` branch. That does not bind the runtime:
|
|
313
|
+
// an out-of-union `op` (unvalidated input, or a future schema version) is
|
|
314
|
+
// what this guard catches. `Object.hasOwn`, not a truthy check: a plain
|
|
315
|
+
// object literal inherits `Object.prototype`, so `op: 'constructor'` would
|
|
316
|
+
// otherwise resolve to `Object` itself and slip past silently.
|
|
317
|
+
if (!Object.hasOwn(operatorHandlers, expression.op)) {
|
|
318
|
+
throw new Error(`resolveNode: unrecognized expression.op ${JSON.stringify(expression.op)}`);
|
|
319
|
+
}
|
|
320
|
+
const handler = operatorHandlers[expression.op];
|
|
321
|
+
return handler(expression, boundElement, ctx);
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* The public entry point. Walks `expression` and produces one
|
|
325
|
+
* `CheckResolutionValue`, exactly what `Outcome.checkResolution` needs,
|
|
326
|
+
* unmodified. `boundElement` starts `ABSENT` at the root, where no quantifier
|
|
327
|
+
* has bound anything yet.
|
|
328
|
+
*/
|
|
329
|
+
export function resolveCheck(expression, resolveOperand, pointerDenotesCollection, regexMatchStepBudget, artifactPath) {
|
|
330
|
+
return resolveNode(expression, ABSENT, {
|
|
331
|
+
resolveOperand,
|
|
332
|
+
pointerDenotesCollection,
|
|
333
|
+
regexMatchStepBudget,
|
|
334
|
+
artifactPath,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** the value domain every AD-4 operator resolves over. */
|
|
2
|
+
import type { JsonValue } from '../schemas/primitives.ts';
|
|
3
|
+
/**
|
|
4
|
+
* AD-26: a pointer that fails to resolve yields the symbol `ABSENT`, not
|
|
5
|
+
* `null` or a string literal, since `null` is itself a valid `JsonValue` and
|
|
6
|
+
* must stay distinguishable from a value that never resolved.
|
|
7
|
+
*/
|
|
8
|
+
export declare const ABSENT: unique symbol;
|
|
9
|
+
export type ResolvedValue = JsonValue | typeof ABSENT;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/** AD-5's twenty-one compile-time failure codes, as data, in the table's order. */
|
|
2
|
+
export declare const FAILURE_CODES: readonly ['missing-requirement-linkage', 'no-observable-success-criterion', 'unreachable-check-evidence', 'malformed-operator-expression', 'quantifier-over-non-collection', 'quantifier-nesting-exceeded', 'unresolved-reference-set', 'duplicate-operation-signature', 'undeclared-mandatory-input', 'oracle-missing-channel', 'direction-check-misaligned', 'unsupported-interface-kind', 'nested-temporal-clause', 'plan-exceeds-scripting-bound', 'rubric-scores-reasoning-prose', 'rubric-unanchored', 'rubric-evidence-unreachable', 'forbidden-input-floor-incomplete', 'scoped-reference-resolves-forbidden', 'waiver-incomplete', 'brief-exceeds-scripting-bound'];
|
|
3
|
+
export type FailureCode = (typeof FAILURE_CODES)[number];
|
|
4
|
+
/**
|
|
5
|
+
* AD-5's compile-time failure registry: thrown when a structural error fails
|
|
6
|
+
* compilation (or, for this code, the post-generation brief audit), since no
|
|
7
|
+
* artifact is emitted. Mirrors AD-28's `RuntimeFault` shape (`code`,
|
|
8
|
+
* `artifactPath`) without subclassing it, per the Consistency Conventions'
|
|
9
|
+
* Errors row keeping the two registries disjoint. Lives beside `FAILURE_CODES`
|
|
10
|
+
* because every code in that registry is thrown through it.
|
|
11
|
+
*/
|
|
12
|
+
export declare class StructuralFailure extends Error {
|
|
13
|
+
readonly code: FailureCode;
|
|
14
|
+
readonly artifactPath: string;
|
|
15
|
+
constructor(code: FailureCode, artifactPath: string, detail: string, options?: {
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** AD-5's twenty-one compile-time failure codes, as data, in the table's order. */
|
|
2
|
+
// This lives in `core/`, since it's a plain data tuple and `core/schemas/`
|
|
3
|
+
// is reserved for Zod definitions. The compiler that emits these codes lives
|
|
4
|
+
// in `core/compile/`, one directory over.
|
|
5
|
+
//
|
|
6
|
+
// `scripts/check-ad5-registry.ts` checks this tuple against AD-5's own
|
|
7
|
+
// registry table under `npm run validate`; the tuple's own invariants
|
|
8
|
+
// (twenty-one members, unique, kebab-case) are locked in
|
|
9
|
+
// tests/schemas/failure-codes.test.ts.
|
|
10
|
+
export const FAILURE_CODES = [
|
|
11
|
+
'missing-requirement-linkage',
|
|
12
|
+
'no-observable-success-criterion',
|
|
13
|
+
'unreachable-check-evidence',
|
|
14
|
+
'malformed-operator-expression',
|
|
15
|
+
'quantifier-over-non-collection',
|
|
16
|
+
'quantifier-nesting-exceeded',
|
|
17
|
+
'unresolved-reference-set',
|
|
18
|
+
'duplicate-operation-signature',
|
|
19
|
+
'undeclared-mandatory-input',
|
|
20
|
+
'oracle-missing-channel',
|
|
21
|
+
'direction-check-misaligned',
|
|
22
|
+
'unsupported-interface-kind',
|
|
23
|
+
'nested-temporal-clause',
|
|
24
|
+
'plan-exceeds-scripting-bound',
|
|
25
|
+
'rubric-scores-reasoning-prose',
|
|
26
|
+
'rubric-unanchored',
|
|
27
|
+
'rubric-evidence-unreachable',
|
|
28
|
+
'forbidden-input-floor-incomplete',
|
|
29
|
+
'scoped-reference-resolves-forbidden',
|
|
30
|
+
'waiver-incomplete',
|
|
31
|
+
'brief-exceeds-scripting-bound',
|
|
32
|
+
];
|
|
33
|
+
/**
|
|
34
|
+
* AD-5's compile-time failure registry: thrown when a structural error fails
|
|
35
|
+
* compilation (or, for this code, the post-generation brief audit), since no
|
|
36
|
+
* artifact is emitted. Mirrors AD-28's `RuntimeFault` shape (`code`,
|
|
37
|
+
* `artifactPath`) without subclassing it, per the Consistency Conventions'
|
|
38
|
+
* Errors row keeping the two registries disjoint. Lives beside `FAILURE_CODES`
|
|
39
|
+
* because every code in that registry is thrown through it.
|
|
40
|
+
*/
|
|
41
|
+
export class StructuralFailure extends Error {
|
|
42
|
+
code;
|
|
43
|
+
artifactPath;
|
|
44
|
+
constructor(code, artifactPath, detail, options) {
|
|
45
|
+
super(`${code} in ${artifactPath}: ${detail}`, options);
|
|
46
|
+
this.name = 'StructuralFailure';
|
|
47
|
+
this.code = code;
|
|
48
|
+
this.artifactPath = artifactPath;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { LineageChain } from '../schemas/evidence-artifact.ts';
|
|
2
|
+
export declare const LINEAGE_DEFECT_CODES: readonly ['lineage-root-invalid', 'lineage-duplicate-artifact', 'lineage-no-root', 'lineage-multiple-roots', 'lineage-parent-absent', 'lineage-revision-not-successor', 'lineage-revision-conflict', 'lineage-length-inconsistent', 'lineage-remediation-cap-exceeded'];
|
|
3
|
+
export type LineageDefectCode = (typeof LINEAGE_DEFECT_CODES)[number];
|
|
4
|
+
/** The three fields `lineage.ts` spreads, as the shape this module reads. */
|
|
5
|
+
export type LineageFields = {
|
|
6
|
+
readonly schemaVersion: number;
|
|
7
|
+
readonly parentDigest: string | null;
|
|
8
|
+
readonly revisionCount: number;
|
|
9
|
+
};
|
|
10
|
+
export type LineageFinding = {
|
|
11
|
+
readonly code: LineageDefectCode;
|
|
12
|
+
readonly artifactPath: string;
|
|
13
|
+
readonly detail: string;
|
|
14
|
+
};
|
|
15
|
+
export type LineageChainOptions = {
|
|
16
|
+
readonly artifactPath: string;
|
|
17
|
+
/** AD-11: a member outside this version throws `schema-version-mismatch`. */
|
|
18
|
+
readonly acceptedSchemaVersion: number;
|
|
19
|
+
/** AD-12's declared revision; the chain's length must be one greater. */
|
|
20
|
+
readonly declaredRevisionCount: number;
|
|
21
|
+
/** AD-12's remediation cap from the scoring policy, or null when none is presented. */
|
|
22
|
+
readonly remediationCap: number | null;
|
|
23
|
+
};
|
|
24
|
+
export type LineageChainReport = {
|
|
25
|
+
readonly artifactPath: string;
|
|
26
|
+
readonly findings: readonly LineageFinding[];
|
|
27
|
+
readonly checks: LineageChain;
|
|
28
|
+
readonly passed: boolean;
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Which of AD-12's three booleans each code clears. A `Record` over the code
|
|
32
|
+
* union, so a tenth code is a compile error here before it can reach a report
|
|
33
|
+
* with no boolean to affect. The cap maps to null; AD-12 records it in
|
|
34
|
+
* `Remediation.cap`, beside `lineageChain`.
|
|
35
|
+
*/
|
|
36
|
+
export declare const CHECK_PROJECTION: Record<LineageDefectCode, keyof LineageChain | null>;
|
|
37
|
+
/**
|
|
38
|
+
* Validates one presented chain of revisions of a single artifact type
|
|
39
|
+
* (AD-12, AD-29). Every defect is collected, so a chain with three problems
|
|
40
|
+
* reports three findings.
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateLineageChain<T extends LineageFields>(artifacts: readonly T[], options: LineageChainOptions): LineageChainReport;
|
|
43
|
+
/**
|
|
44
|
+
* Mints the next revision of `parent`: a new artifact carrying the parent's
|
|
45
|
+
* digest and a revision count one greater (AD-29). The clone is load-bearing.
|
|
46
|
+
* `{ ...body }` shares its subtrees with the caller and `freezeArtifact`
|
|
47
|
+
* freezes in place, so without it this call would freeze the caller's `body`.
|
|
48
|
+
*/
|
|
49
|
+
export declare function reviseArtifact<T extends LineageFields>(parent: T, body: Omit<T, 'parentDigest' | 'revisionCount'>, artifactPath: string): T;
|