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.
Files changed (230) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +355 -0
  3. package/corpus/dev/README.md +36 -0
  4. package/corpus/dev/compile-seal-example/brief.json +1 -0
  5. package/corpus/dev/compile-seal-example/contract.json +1 -0
  6. package/corpus/dev/contracts/absent-collection-locations.json +1 -0
  7. package/corpus/dev/contracts/absent-sibling-groups.json +1 -0
  8. package/corpus/dev/contracts/absent-success-indicator.json +1 -0
  9. package/corpus/dev/contracts/empty-channel-roles.json +1 -0
  10. package/corpus/dev/contracts/empty-collection-locations.json +1 -0
  11. package/corpus/dev/contracts/empty-request-shapes.json +1 -0
  12. package/corpus/dev/contracts/empty-sibling-groups.json +1 -0
  13. package/corpus/dev/contracts/no-collection-quantifier.json +1 -0
  14. package/corpus/dev/contracts/no-operation-inventory.json +1 -0
  15. package/corpus/dev/contracts/no-read-back-relation.json +1 -0
  16. package/corpus/dev/contracts/no-state-change-marker.json +1 -0
  17. package/corpus/dev/contracts/no-type-violating-step.json +1 -0
  18. package/corpus/dev/contracts/per-key-split-oracles.json +1 -0
  19. package/corpus/dev/contracts/satisfied-declarations.json +1 -0
  20. package/corpus/dev/contracts/single-required-response-key.json +1 -0
  21. package/corpus/dev/contracts/split-indicator-oracle.json +1 -0
  22. package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -0
  23. package/corpus/dev/contracts/unnamed-reference-set.json +1 -0
  24. package/corpus/dev/contracts/wrong-cardinality-form.json +1 -0
  25. package/corpus/dev/index.json +1 -0
  26. package/dist/adapters/index.d.ts +12 -0
  27. package/dist/adapters/index.js +3 -0
  28. package/dist/adapters/local-corpus-adapter.d.ts +7 -0
  29. package/dist/adapters/local-corpus-adapter.js +78 -0
  30. package/dist/adapters/node-file-system-adapter.d.ts +7 -0
  31. package/dist/adapters/node-file-system-adapter.js +39 -0
  32. package/dist/adapters/port-boundary.d.ts +18 -0
  33. package/dist/adapters/port-boundary.js +83 -0
  34. package/dist/adapters/system-clock-adapter.d.ts +10 -0
  35. package/dist/adapters/system-clock-adapter.js +18 -0
  36. package/dist/application/compile.d.ts +4 -0
  37. package/dist/application/compile.js +24 -0
  38. package/dist/application/diagnostics.d.ts +22 -0
  39. package/dist/application/diagnostics.js +18 -0
  40. package/dist/application/index.d.ts +23 -0
  41. package/dist/application/index.js +17 -0
  42. package/dist/application/invoke-port.d.ts +2 -0
  43. package/dist/application/invoke-port.js +45 -0
  44. package/dist/application/preflight.d.ts +27 -0
  45. package/dist/application/preflight.js +131 -0
  46. package/dist/application/seal.d.ts +4 -0
  47. package/dist/application/seal.js +28 -0
  48. package/dist/application/serialize.d.ts +6 -0
  49. package/dist/application/serialize.js +9 -0
  50. package/dist/cli/arguments.d.ts +28 -0
  51. package/dist/cli/arguments.js +167 -0
  52. package/dist/cli/exit-codes.d.ts +36 -0
  53. package/dist/cli/exit-codes.js +39 -0
  54. package/dist/cli/main.d.ts +2 -0
  55. package/dist/cli/main.js +143 -0
  56. package/dist/cli/render.d.ts +26 -0
  57. package/dist/cli/render.js +53 -0
  58. package/dist/cli/run.d.ts +41 -0
  59. package/dist/cli/run.js +203 -0
  60. package/dist/core/canonical/canonicalize.d.ts +1 -0
  61. package/dist/core/canonical/canonicalize.js +113 -0
  62. package/dist/core/canonical/digest.d.ts +7 -0
  63. package/dist/core/canonical/digest.js +81 -0
  64. package/dist/core/canonical/scan-json.d.ts +1 -0
  65. package/dist/core/canonical/scan-json.js +298 -0
  66. package/dist/core/canonical/value-domain.d.ts +4 -0
  67. package/dist/core/canonical/value-domain.js +113 -0
  68. package/dist/core/compile/compile.d.ts +24 -0
  69. package/dist/core/compile/compile.js +67 -0
  70. package/dist/core/compile/declarations.d.ts +5 -0
  71. package/dist/core/compile/declarations.js +22 -0
  72. package/dist/core/compile/expression-legality.d.ts +14 -0
  73. package/dist/core/compile/expression-legality.js +346 -0
  74. package/dist/core/compile/forbidden-inputs.d.ts +5 -0
  75. package/dist/core/compile/forbidden-inputs.js +19 -0
  76. package/dist/core/compile/interface-inventory.d.ts +10 -0
  77. package/dist/core/compile/interface-inventory.js +55 -0
  78. package/dist/core/compile/oracle-alignment.d.ts +14 -0
  79. package/dist/core/compile/oracle-alignment.js +93 -0
  80. package/dist/core/compile/reachability.d.ts +17 -0
  81. package/dist/core/compile/reachability.js +177 -0
  82. package/dist/core/compile/rubrics.d.ts +55 -0
  83. package/dist/core/compile/rubrics.js +198 -0
  84. package/dist/core/compile/scripting-bound.d.ts +5 -0
  85. package/dist/core/compile/scripting-bound.js +151 -0
  86. package/dist/core/compile/sensitivity-witness.d.ts +73 -0
  87. package/dist/core/compile/sensitivity-witness.js +232 -0
  88. package/dist/core/compile/waivers.d.ts +3 -0
  89. package/dist/core/compile/waivers.js +21 -0
  90. package/dist/core/coverage/coverage.d.ts +18 -0
  91. package/dist/core/coverage/coverage.js +47 -0
  92. package/dist/core/coverage/relevance.d.ts +75 -0
  93. package/dist/core/coverage/relevance.js +186 -0
  94. package/dist/core/coverage/rules.d.ts +7 -0
  95. package/dist/core/coverage/rules.js +18 -0
  96. package/dist/core/coverage/satisfaction.d.ts +107 -0
  97. package/dist/core/coverage/satisfaction.js +415 -0
  98. package/dist/core/coverage/table.d.ts +26 -0
  99. package/dist/core/coverage/table.js +222 -0
  100. package/dist/core/declared-inputs.d.ts +16 -0
  101. package/dist/core/declared-inputs.js +18 -0
  102. package/dist/core/evaluate/evidence-resolution.d.ts +54 -0
  103. package/dist/core/evaluate/evidence-resolution.js +153 -0
  104. package/dist/core/evaluate/operators.d.ts +87 -0
  105. package/dist/core/evaluate/operators.js +350 -0
  106. package/dist/core/evaluate/resolution.d.ts +40 -0
  107. package/dist/core/evaluate/resolution.js +336 -0
  108. package/dist/core/evaluate/resolved-value.d.ts +9 -0
  109. package/dist/core/evaluate/resolved-value.js +6 -0
  110. package/dist/core/failure-codes.d.ts +18 -0
  111. package/dist/core/failure-codes.js +50 -0
  112. package/dist/core/lineage/chain.d.ts +49 -0
  113. package/dist/core/lineage/chain.js +210 -0
  114. package/dist/core/lineage/freeze.d.ts +12 -0
  115. package/dist/core/lineage/freeze.js +35 -0
  116. package/dist/core/lineage/stage-table.d.ts +43 -0
  117. package/dist/core/lineage/stage-table.js +106 -0
  118. package/dist/core/preflight/plan.d.ts +67 -0
  119. package/dist/core/preflight/plan.js +313 -0
  120. package/dist/core/preflight/projection.d.ts +40 -0
  121. package/dist/core/preflight/projection.js +96 -0
  122. package/dist/core/preflight/reduce.d.ts +8 -0
  123. package/dist/core/preflight/reduce.js +180 -0
  124. package/dist/core/preflight/witness-evidence.d.ts +42 -0
  125. package/dist/core/preflight/witness-evidence.js +88 -0
  126. package/dist/core/probe/target-policy.d.ts +63 -0
  127. package/dist/core/probe/target-policy.js +328 -0
  128. package/dist/core/schemas/artifact-reference.d.ts +19 -0
  129. package/dist/core/schemas/artifact-reference.js +30 -0
  130. package/dist/core/schemas/artifact.d.ts +1153 -0
  131. package/dist/core/schemas/artifact.js +84 -0
  132. package/dist/core/schemas/constraint-ledger.d.ts +57 -0
  133. package/dist/core/schemas/constraint-ledger.js +143 -0
  134. package/dist/core/schemas/eval-contract.d.ts +402 -0
  135. package/dist/core/schemas/eval-contract.js +147 -0
  136. package/dist/core/schemas/evaluator-configuration.d.ts +37 -0
  137. package/dist/core/schemas/evaluator-configuration.js +43 -0
  138. package/dist/core/schemas/evidence-artifact.d.ts +471 -0
  139. package/dist/core/schemas/evidence-artifact.js +232 -0
  140. package/dist/core/schemas/expression.d.ts +197 -0
  141. package/dist/core/schemas/expression.js +284 -0
  142. package/dist/core/schemas/faults.d.ts +9 -0
  143. package/dist/core/schemas/faults.js +32 -0
  144. package/dist/core/schemas/interface.d.ts +417 -0
  145. package/dist/core/schemas/interface.js +115 -0
  146. package/dist/core/schemas/isolation-manifest.d.ts +136 -0
  147. package/dist/core/schemas/isolation-manifest.js +92 -0
  148. package/dist/core/schemas/lineage.d.ts +21 -0
  149. package/dist/core/schemas/lineage.js +28 -0
  150. package/dist/core/schemas/oracle.d.ts +72 -0
  151. package/dist/core/schemas/oracle.js +35 -0
  152. package/dist/core/schemas/plan.d.ts +115 -0
  153. package/dist/core/schemas/plan.js +58 -0
  154. package/dist/core/schemas/pointer.d.ts +63 -0
  155. package/dist/core/schemas/pointer.js +94 -0
  156. package/dist/core/schemas/port-messages.d.ts +103 -0
  157. package/dist/core/schemas/port-messages.js +90 -0
  158. package/dist/core/schemas/preflight-verdict.d.ts +60 -0
  159. package/dist/core/schemas/preflight-verdict.js +38 -0
  160. package/dist/core/schemas/primitives.d.ts +78 -0
  161. package/dist/core/schemas/primitives.js +121 -0
  162. package/dist/core/schemas/private-artifact-manifest.d.ts +56 -0
  163. package/dist/core/schemas/private-artifact-manifest.js +46 -0
  164. package/dist/core/schemas/probe-body.d.ts +25 -0
  165. package/dist/core/schemas/probe-body.js +23 -0
  166. package/dist/core/schemas/probe-policy.d.ts +76 -0
  167. package/dist/core/schemas/probe-policy.js +33 -0
  168. package/dist/core/schemas/probe.d.ts +189 -0
  169. package/dist/core/schemas/probe.js +82 -0
  170. package/dist/core/schemas/publish.d.ts +30 -0
  171. package/dist/core/schemas/publish.js +135 -0
  172. package/dist/core/schemas/reference-set.d.ts +18 -0
  173. package/dist/core/schemas/reference-set.js +27 -0
  174. package/dist/core/schemas/rubric.d.ts +62 -0
  175. package/dist/core/schemas/rubric.js +68 -0
  176. package/dist/core/schemas/scoring-policy.d.ts +28 -0
  177. package/dist/core/schemas/scoring-policy.js +51 -0
  178. package/dist/core/schemas/sealed-evaluator-brief.d.ts +80 -0
  179. package/dist/core/schemas/sealed-evaluator-brief.js +59 -0
  180. package/dist/core/schemas/sealed-run-record.d.ts +382 -0
  181. package/dist/core/schemas/sealed-run-record.js +215 -0
  182. package/dist/core/schemas/sensitivity-witness.d.ts +138 -0
  183. package/dist/core/schemas/sensitivity-witness.js +86 -0
  184. package/dist/core/schemas/verdict.d.ts +25 -0
  185. package/dist/core/schemas/verdict.js +20 -0
  186. package/dist/core/schemas/waiver.d.ts +17 -0
  187. package/dist/core/schemas/waiver.js +24 -0
  188. package/dist/core/seal/derived-reference.d.ts +36 -0
  189. package/dist/core/seal/derived-reference.js +383 -0
  190. package/dist/core/seal/direction-prose.d.ts +13 -0
  191. package/dist/core/seal/direction-prose.js +120 -0
  192. package/dist/core/seal/plan-index.d.ts +63 -0
  193. package/dist/core/seal/plan-index.js +155 -0
  194. package/dist/core/seal/scripting-audit.d.ts +15 -0
  195. package/dist/core/seal/scripting-audit.js +51 -0
  196. package/dist/core/seal/seal.d.ts +18 -0
  197. package/dist/core/seal/seal.js +155 -0
  198. package/dist/core/stage-contracts.d.ts +30 -0
  199. package/dist/core/stage-contracts.js +1 -0
  200. package/dist/index.d.ts +15 -0
  201. package/dist/index.js +22 -0
  202. package/dist/ports/clock-port.d.ts +13 -0
  203. package/dist/ports/clock-port.js +7 -0
  204. package/dist/ports/corpus-port.d.ts +16 -0
  205. package/dist/ports/corpus-port.js +7 -0
  206. package/dist/ports/environment-probe-port.d.ts +76 -0
  207. package/dist/ports/environment-probe-port.js +32 -0
  208. package/dist/ports/file-system-port.d.ts +27 -0
  209. package/dist/ports/file-system-port.js +11 -0
  210. package/dist/ports/port.d.ts +33 -0
  211. package/dist/ports/port.js +14 -0
  212. package/dist/testing/conformance.d.ts +88 -0
  213. package/dist/testing/conformance.js +281 -0
  214. package/dist/testing/index.d.ts +26 -0
  215. package/dist/testing/index.js +18 -0
  216. package/dist/testing/probe-conformance.d.ts +45 -0
  217. package/dist/testing/probe-conformance.js +186 -0
  218. package/package.json +130 -0
  219. package/schemas/artifact-reference.schema.json +64 -0
  220. package/schemas/eval-contract.schema.json +2145 -0
  221. package/schemas/evaluator-configuration.schema.json +199 -0
  222. package/schemas/evidence-artifact.schema.json +1229 -0
  223. package/schemas/isolation-manifest.schema.json +394 -0
  224. package/schemas/preflight-verdict.schema.json +114 -0
  225. package/schemas/private-artifact-manifest.schema.json +103 -0
  226. package/schemas/probe.schema.json +1151 -0
  227. package/schemas/rubric.schema.json +148 -0
  228. package/schemas/scoring-policy.schema.json +90 -0
  229. package/schemas/sealed-evaluator-brief.schema.json +268 -0
  230. 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,6 @@
1
+ /**
2
+ * AD-26: a pointer that fails to resolve yields the symbol `ABSENT`, not
3
+ * `null` or a string literal, since `null` is itself a valid `JsonValue` and
4
+ * must stay distinguishable from a value that never resolved.
5
+ */
6
+ export const ABSENT = Symbol('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;