eval-quality 0.1.0 → 0.3.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 (179) hide show
  1. package/README.md +142 -67
  2. package/corpus/dev/README.md +20 -13
  3. package/corpus/dev/compile-seal-example/brief.json +1 -1
  4. package/corpus/dev/compile-seal-example/contract.json +1 -1
  5. package/corpus/dev/contracts/absent-collection-locations.json +1 -1
  6. package/corpus/dev/contracts/absent-sibling-groups.json +1 -1
  7. package/corpus/dev/contracts/absent-success-indicator.json +1 -1
  8. package/corpus/dev/contracts/empty-channel-roles.json +1 -1
  9. package/corpus/dev/contracts/empty-collection-locations.json +1 -1
  10. package/corpus/dev/contracts/empty-request-shapes.json +1 -1
  11. package/corpus/dev/contracts/empty-sibling-groups.json +1 -1
  12. package/corpus/dev/contracts/fragment-selection.json +1 -0
  13. package/corpus/dev/contracts/no-collection-quantifier.json +1 -1
  14. package/corpus/dev/contracts/no-operation-inventory.json +1 -1
  15. package/corpus/dev/contracts/no-read-back-relation.json +1 -1
  16. package/corpus/dev/contracts/no-state-change-marker.json +1 -1
  17. package/corpus/dev/contracts/no-type-violating-step.json +1 -1
  18. package/corpus/dev/contracts/per-key-split-oracles.json +1 -1
  19. package/corpus/dev/contracts/review-corpus.json +1 -0
  20. package/corpus/dev/contracts/satisfied-declarations.json +1 -1
  21. package/corpus/dev/contracts/single-required-response-key.json +1 -1
  22. package/corpus/dev/contracts/split-indicator-oracle.json +1 -1
  23. package/corpus/dev/contracts/unaddressed-parameter-sibling.json +1 -1
  24. package/corpus/dev/contracts/unnamed-reference-set.json +1 -1
  25. package/corpus/dev/contracts/wrong-cardinality-form.json +1 -1
  26. package/corpus/dev/index.json +1 -1
  27. package/dist/application/index.d.ts +2 -0
  28. package/dist/application/index.js +1 -0
  29. package/dist/application/score.d.ts +48 -0
  30. package/dist/application/score.js +186 -0
  31. package/dist/cli/arguments.d.ts +7 -3
  32. package/dist/cli/arguments.js +73 -5
  33. package/dist/cli/exit-codes.d.ts +6 -2
  34. package/dist/cli/exit-codes.js +8 -9
  35. package/dist/cli/main.js +9 -0
  36. package/dist/cli/render.d.ts +3 -6
  37. package/dist/cli/render.js +57 -5
  38. package/dist/cli/run.d.ts +13 -3
  39. package/dist/cli/run.js +136 -16
  40. package/dist/core/canonical/canonicalize.d.ts +12 -0
  41. package/dist/core/canonical/canonicalize.js +18 -4
  42. package/dist/core/compile/bindings.d.ts +89 -0
  43. package/dist/core/compile/bindings.js +334 -0
  44. package/dist/core/compile/compile.d.ts +17 -0
  45. package/dist/core/compile/compile.js +37 -1
  46. package/dist/core/compile/excluded-content.d.ts +11 -0
  47. package/dist/core/compile/excluded-content.js +42 -0
  48. package/dist/core/compile/expression-legality.d.ts +49 -0
  49. package/dist/core/compile/expression-legality.js +158 -38
  50. package/dist/core/compile/forbidden-inputs.d.ts +8 -1
  51. package/dist/core/compile/forbidden-inputs.js +16 -3
  52. package/dist/core/compile/interface-inventory.d.ts +57 -2
  53. package/dist/core/compile/interface-inventory.js +119 -13
  54. package/dist/core/compile/reachability.d.ts +28 -1
  55. package/dist/core/compile/reachability.js +167 -35
  56. package/dist/core/compile/schema-version.d.ts +2 -0
  57. package/dist/core/compile/schema-version.js +25 -0
  58. package/dist/core/compile/sensitivity-witness.d.ts +31 -12
  59. package/dist/core/compile/sensitivity-witness.js +110 -23
  60. package/dist/core/compile/step-reference.d.ts +2 -0
  61. package/dist/core/compile/step-reference.js +49 -0
  62. package/dist/core/coverage/operations.d.ts +62 -0
  63. package/dist/core/coverage/operations.js +57 -0
  64. package/dist/core/coverage/relevance.d.ts +4 -2
  65. package/dist/core/coverage/relevance.js +22 -23
  66. package/dist/core/coverage/satisfaction.d.ts +2 -2
  67. package/dist/core/coverage/satisfaction.js +73 -48
  68. package/dist/core/declared-inputs.d.ts +83 -4
  69. package/dist/core/declared-inputs.js +105 -8
  70. package/dist/core/emit/emit.d.ts +9 -0
  71. package/dist/core/emit/emit.js +214 -0
  72. package/dist/core/emit/private-artifact-digest.d.ts +11 -0
  73. package/dist/core/emit/private-artifact-digest.js +31 -0
  74. package/dist/core/evaluate/evidence-resolution.d.ts +10 -3
  75. package/dist/core/evaluate/evidence-resolution.js +74 -11
  76. package/dist/core/evaluate/operators.d.ts +22 -0
  77. package/dist/core/evaluate/operators.js +17 -1
  78. package/dist/core/evaluate/resolution.d.ts +14 -3
  79. package/dist/core/evaluate/resolution.js +63 -3
  80. package/dist/core/excluded-content.d.ts +65 -0
  81. package/dist/core/excluded-content.js +113 -0
  82. package/dist/core/failure-codes.d.ts +2 -2
  83. package/dist/core/failure-codes.js +7 -2
  84. package/dist/core/ingest/conditions.d.ts +201 -0
  85. package/dist/core/ingest/conditions.js +48 -0
  86. package/dist/core/ingest/index.d.ts +13 -0
  87. package/dist/core/ingest/index.js +11 -0
  88. package/dist/core/ingest/ingest.d.ts +68 -0
  89. package/dist/core/ingest/ingest.js +318 -0
  90. package/dist/core/lineage/stage-table.d.ts +21 -4
  91. package/dist/core/lineage/stage-table.js +35 -3
  92. package/dist/core/preflight/plan.d.ts +7 -5
  93. package/dist/core/preflight/plan.js +75 -32
  94. package/dist/core/preflight/projection.d.ts +6 -3
  95. package/dist/core/preflight/projection.js +22 -2
  96. package/dist/core/preflight/reduce.js +47 -7
  97. package/dist/core/preflight/witness-evidence.d.ts +5 -5
  98. package/dist/core/preflight/witness-evidence.js +73 -20
  99. package/dist/core/schemas/artifact.d.ts +1049 -30
  100. package/dist/core/schemas/constraint-ledger.js +51 -0
  101. package/dist/core/schemas/defect-signature.d.ts +695 -0
  102. package/dist/core/schemas/defect-signature.js +175 -0
  103. package/dist/core/schemas/eval-contract.d.ts +550 -12
  104. package/dist/core/schemas/eval-contract.js +32 -2
  105. package/dist/core/schemas/evidence-artifact.d.ts +109 -19
  106. package/dist/core/schemas/evidence-artifact.js +64 -8
  107. package/dist/core/schemas/interface.d.ts +741 -13
  108. package/dist/core/schemas/interface.js +120 -7
  109. package/dist/core/schemas/isolation-manifest.js +16 -9
  110. package/dist/core/schemas/plan.d.ts +329 -3
  111. package/dist/core/schemas/plan.js +89 -3
  112. package/dist/core/schemas/pointer.d.ts +47 -1
  113. package/dist/core/schemas/pointer.js +89 -8
  114. package/dist/core/schemas/port-messages.d.ts +194 -10
  115. package/dist/core/schemas/port-messages.js +73 -6
  116. package/dist/core/schemas/probe-body.d.ts +18 -0
  117. package/dist/core/schemas/probe-body.js +13 -0
  118. package/dist/core/schemas/probe-qualification.d.ts +168 -0
  119. package/dist/core/schemas/probe-qualification.js +106 -0
  120. package/dist/core/schemas/probe.d.ts +431 -0
  121. package/dist/core/schemas/probe.js +6 -2
  122. package/dist/core/schemas/scoring-policy.d.ts +1 -0
  123. package/dist/core/schemas/scoring-policy.js +6 -1
  124. package/dist/core/schemas/sealed-evaluator-brief.d.ts +1 -0
  125. package/dist/core/schemas/sealed-evaluator-brief.js +4 -1
  126. package/dist/core/schemas/sealed-run-record.d.ts +161 -12
  127. package/dist/core/schemas/sealed-run-record.js +92 -15
  128. package/dist/core/schemas/sensitivity-witness.d.ts +108 -6
  129. package/dist/core/schemas/sensitivity-witness.js +61 -5
  130. package/dist/core/score/binding-order.d.ts +31 -0
  131. package/dist/core/score/binding-order.js +88 -0
  132. package/dist/core/score/bindings.d.ts +93 -0
  133. package/dist/core/score/bindings.js +309 -0
  134. package/dist/core/score/ladder-table.d.ts +23 -0
  135. package/dist/core/score/ladder-table.js +109 -0
  136. package/dist/core/score/ladder.d.ts +229 -0
  137. package/dist/core/score/ladder.js +464 -0
  138. package/dist/core/score/mode-agreement.d.ts +30 -0
  139. package/dist/core/score/mode-agreement.js +16 -0
  140. package/dist/core/score/outcome-table.d.ts +17 -0
  141. package/dist/core/score/outcome-table.js +172 -0
  142. package/dist/core/score/outcome.d.ts +466 -0
  143. package/dist/core/score/outcome.js +562 -0
  144. package/dist/core/score/qualification.d.ts +83 -0
  145. package/dist/core/score/qualification.js +645 -0
  146. package/dist/core/score/quotation.d.ts +56 -0
  147. package/dist/core/score/quotation.js +226 -0
  148. package/dist/core/score/reduce-trials.d.ts +80 -0
  149. package/dist/core/score/reduce-trials.js +90 -0
  150. package/dist/core/score/score.d.ts +72 -0
  151. package/dist/core/score/score.js +607 -0
  152. package/dist/core/score/selection.d.ts +80 -0
  153. package/dist/core/score/selection.js +98 -0
  154. package/dist/core/score/strength.d.ts +58 -0
  155. package/dist/core/score/strength.js +227 -0
  156. package/dist/core/score/witness.d.ts +138 -0
  157. package/dist/core/score/witness.js +320 -0
  158. package/dist/core/seal/derived-reference.d.ts +2 -9
  159. package/dist/core/seal/derived-reference.js +293 -46
  160. package/dist/core/seal/plan-index.d.ts +18 -3
  161. package/dist/core/seal/plan-index.js +44 -8
  162. package/dist/core/seal/seal.js +8 -3
  163. package/dist/core/stage-contracts.d.ts +62 -2
  164. package/dist/index.d.ts +1 -1
  165. package/dist/index.js +1 -1
  166. package/dist/ports/environment-probe-port.d.ts +60 -4
  167. package/dist/testing/index.d.ts +2 -0
  168. package/dist/testing/index.js +1 -0
  169. package/dist/testing/probe-conformance.d.ts +29 -0
  170. package/dist/testing/probe-conformance.js +34 -5
  171. package/package.json +12 -3
  172. package/schemas/eval-contract.schema.json +1787 -797
  173. package/schemas/evidence-artifact.schema.json +126 -9
  174. package/schemas/isolation-manifest.schema.json +17 -10
  175. package/schemas/probe.schema.json +636 -5
  176. package/schemas/rubric.schema.json +1 -1
  177. package/schemas/scoring-policy.schema.json +8 -1
  178. package/schemas/sealed-evaluator-brief.schema.json +11 -1
  179. package/schemas/sealed-run-record.schema.json +314 -40
@@ -3,8 +3,9 @@
3
3
  * that turns an `Expression` into one `CheckResolutionValue`. Leaf operators,
4
4
  * `covers-by-key` included, live in `operators.ts`. Operand resolution, every
5
5
  * pointer form including the bound-element `@/` form, is injected;
6
- * `evidence-resolution.ts` supplies it, and `ResolveOperand` and
7
- * `PointerDenotesCollection` are the consumer-side contract it satisfies.
6
+ * `evidence-resolution.ts` supplies it, and `ResolveOperand`,
7
+ * `PointerDenotesCollection`, and `ReferenceSetKeys` are the consumer-side
8
+ * contract it satisfies.
8
9
  */
9
10
  import type { CheckResolutionValue } from '../schemas/evidence-artifact.ts';
10
11
  import type { Expression, Operand } from '../schemas/expression.ts';
@@ -31,10 +32,20 @@ export type ResolveOperand = (operand: Operand, boundElement: ResolvedValue, art
31
32
  * soft-delete agreement pair (AD-4's own worked example).
32
33
  */
33
34
  export type PointerDenotesCollection = (pointer: string) => boolean;
35
+ /**
36
+ * The `keys` each declared reference set names, by identifier.
37
+ * `set-membership`'s set position reads the single declared key off each
38
+ * member (`reference-set.ts`), and the members map the injected
39
+ * `ResolveOperand` closes over has already discarded the keys, so they travel
40
+ * separately. Plain data, because `PreflightPlan` carries this through to
41
+ * its reducer and is compared by value, which a closure fails.
42
+ * `evidence-resolution.ts` builds it from a contract.
43
+ */
44
+ export type ReferenceSetKeys = Readonly<Record<string, readonly string[]>>;
34
45
  /**
35
46
  * The public entry point. Walks `expression` and produces one
36
47
  * `CheckResolutionValue`, exactly what `Outcome.checkResolution` needs,
37
48
  * unmodified. `boundElement` starts `ABSENT` at the root, where no quantifier
38
49
  * has bound anything yet.
39
50
  */
40
- export declare function resolveCheck(expression: Expression, resolveOperand: ResolveOperand, pointerDenotesCollection: PointerDenotesCollection, regexMatchStepBudget: number, artifactPath: string): CheckResolutionValue;
51
+ export declare function resolveCheck(expression: Expression, resolveOperand: ResolveOperand, pointerDenotesCollection: PointerDenotesCollection, referenceSetKeys: ReferenceSetKeys, regexMatchStepBudget: number, artifactPath: string): CheckResolutionValue;
@@ -1,4 +1,4 @@
1
- import { absence, containment, countTolerance, coversByKey, deepEquality, equality, existence, ordering, regexMatch, setMembership, shape, } from './operators.js';
1
+ import { absence, containment, countTolerance, coversByKey, deepEquality, equality, existence, keyValueOf, ordering, regexMatch, setMembership, shape, } from './operators.js';
2
2
  import { ABSENT } from './resolved-value.js';
3
3
  /**
4
4
  * AD-4's one closed introduction condition, checked per operand and applied
@@ -242,10 +242,69 @@ function resolveRegexNode(expression, boundElement, ctx) {
242
242
  const { pattern } = expression;
243
243
  return resolveSingleOperand(operand, boundElement, ctx, (resolved) => regexMatch(resolved, pattern, ctx.regexMatchStepBudget, ctx.artifactPath));
244
244
  }
245
+ /**
246
+ * Reads the single declared key off each member of a `{ referenceSet }` set
247
+ * operand. `reference-set.ts` declares members as objects so one declaration
248
+ * serves both operators, and states that "a `set-membership` operand against
249
+ * the same set reads the single named key". This is where that reading
250
+ * happens, and without it AD-20 rule 6's injection form can never answer
251
+ * `true`: the value operand resolves to a scalar, and `setMembership` compares
252
+ * whole members by digest, so a scalar against an object is always `false`.
253
+ *
254
+ * Scoped to this one operator position on purpose. `covers-by-key` projects
255
+ * both sides itself on `expectedKey` and `actualKey`, and `containment`
256
+ * matches whole members, so both want the members unprojected. The injected
257
+ * `ResolveOperand` is handed one operand at a time and cannot see which
258
+ * operator position it is filling, which is why the projection lives at the
259
+ * resolution site.
260
+ *
261
+ * The option turned down: an explicit `memberKey` field on `set-membership`
262
+ * mirroring `covers-by-key`'s `expectedKey`. It is symmetric with its sibling
263
+ * and it carries a multi-key set, at the price of a grammar change to a
264
+ * published artifact, an eval-contract `schemaVersion` bump, and
265
+ * published-schema drift, for a case no shipped contract has. Revisit it the
266
+ * first time a real contract wants a multi-key set in this position.
267
+ *
268
+ * Both throws are unreachable for a compiled contract:
269
+ * `checkOperandLegality` rejects a multi-key reference set in this position,
270
+ * and a member missing the declared key, under
271
+ * `malformed-operator-expression`. They throw, because a set this function
272
+ * cannot project carries no membership answer to give.
273
+ */
274
+ function projectSetOperand(resolvedSet, setOperand, ctx) {
275
+ if (!('referenceSet' in setOperand))
276
+ return resolvedSet;
277
+ const { referenceSet } = setOperand;
278
+ // `Object.hasOwn`, the same prototype-chain guard `makeResolveOperand`
279
+ // applies to its own maps: `Identifier`'s charset admits `constructor`.
280
+ const keys = Object.hasOwn(ctx.referenceSetKeys, referenceSet)
281
+ ? ctx.referenceSetKeys[referenceSet]
282
+ : undefined;
283
+ // No declared keys means the contract declares no such reference set,
284
+ // which compilation rejects under `unresolved-reference-set`; a non-array
285
+ // is the array guard below reporting whatever the resolver returned. Both
286
+ // pass through so exactly one guard speaks for each.
287
+ if (keys === undefined || !Array.isArray(resolvedSet))
288
+ return resolvedSet;
289
+ if (keys.length !== 1) {
290
+ throw new Error(`set-membership's set-operand projection: referenceSet "${referenceSet}" declares ${keys.length} keys, and this position reads exactly one. A multi-key set here is malformed-operator-expression, which compilation rejects.`);
291
+ }
292
+ const key = keys[0];
293
+ return resolvedSet.map((member) => {
294
+ const keyValue = keyValueOf(member, key);
295
+ if (keyValue === ABSENT) {
296
+ throw new Error(`set-membership's set-operand projection: a member of referenceSet "${referenceSet}" carries no own property "${key}", the key it declares. That is malformed-operator-expression, which compilation rejects.`);
297
+ }
298
+ return keyValue;
299
+ });
300
+ }
245
301
  function resolveSetMembershipNode(expression, boundElement, ctx) {
246
302
  const [valueOperand, setOperand] = expression.operands;
247
303
  const value = ctx.resolveOperand(valueOperand, boundElement, ctx.artifactPath);
248
- const resolvedSet = ctx.resolveOperand(setOperand, boundElement, ctx.artifactPath);
304
+ // Projected before the empty-collection interception and the array guard
305
+ // below, so both read the one value the operator will see. Projection
306
+ // preserves length and array-ness, so neither answer moves.
307
+ const resolvedSet = projectSetOperand(ctx.resolveOperand(setOperand, boundElement, ctx.artifactPath), setOperand, ctx);
249
308
  if (anyOperandEmpty([
250
309
  { operand: valueOperand, resolved: value },
251
310
  { operand: setOperand, resolved: resolvedSet },
@@ -326,10 +385,11 @@ function resolveNode(expression, boundElement, ctx) {
326
385
  * unmodified. `boundElement` starts `ABSENT` at the root, where no quantifier
327
386
  * has bound anything yet.
328
387
  */
329
- export function resolveCheck(expression, resolveOperand, pointerDenotesCollection, regexMatchStepBudget, artifactPath) {
388
+ export function resolveCheck(expression, resolveOperand, pointerDenotesCollection, referenceSetKeys, regexMatchStepBudget, artifactPath) {
330
389
  return resolveNode(expression, ABSENT, {
331
390
  resolveOperand,
332
391
  pointerDenotesCollection,
392
+ referenceSetKeys,
333
393
  regexMatchStepBudget,
334
394
  artifactPath,
335
395
  });
@@ -0,0 +1,65 @@
1
+ /**
2
+ * AD-18's excluded categories, as patterns that read a value rather than a name.
3
+ *
4
+ * AD-18 excludes "credentials, tokens, real names, email addresses, account
5
+ * identifiers, and transaction content" from every artifact this package
6
+ * produces or publishes, and binds "published examples and test fixtures as
7
+ * strictly as real runs". Until this module the whole prohibition was carried by
8
+ * one test that scans `corpus/dev`, so a contract carrying a live-shaped token
9
+ * compiled clean and the token travelled into the sealed brief.
10
+ *
11
+ * Only the value-shaped half of that scan ships here, and the split is the point
12
+ * rather than an economy. AD-18's own reading of the rule for the header channel
13
+ * is that a declaration "names the header and its type and never carries a
14
+ * credential value", so a name is what an author is supposed to write: an
15
+ * operation declaring a `password` body key or an `Authorization` header is a
16
+ * contract for an authentication API, and a gate matching `\bpassword\b` would
17
+ * reject exactly the systems this package exists to evaluate. The patterns below
18
+ * therefore match only strings whose shape is decidable without knowing what
19
+ * field they sit in: a PEM header, a token with an issuer prefix, an address, a
20
+ * checkable account number, a card-shaped digit run. The name-shaped patterns
21
+ * stay in `tests/architecture/dev-corpus.test.ts`, where review rather than a
22
+ * compiler decides.
23
+ *
24
+ * Each pattern carries a string it must fire on, and a test asserts every one
25
+ * still does. A pattern that has rotted into a regex matching nothing is worse
26
+ * than no pattern: it reports clean forever.
27
+ */
28
+ /** AD-18's six categories, in the rule's own order. */
29
+ export declare const EXCLUDED_CATEGORIES: readonly ['credentials', 'tokens', 'real names', 'email addresses', 'account identifiers', 'transaction content'];
30
+ export type ExcludedCategory = (typeof EXCLUDED_CATEGORIES)[number];
31
+ export type ExcludedContentPattern = {
32
+ readonly category: ExcludedCategory;
33
+ readonly regex: RegExp;
34
+ /** a string the pattern must match, so a rotted pattern fails its own test. */
35
+ readonly fires: string;
36
+ };
37
+ /**
38
+ * `real names` has no member here and never will: a personal name has no surface
39
+ * form, so every pattern that reaches one reads the field that carries it, which
40
+ * is a name-shaped test. The category stays in the tuple above because AD-18
41
+ * names six and dropping one from the vocabulary would make the gate look total.
42
+ */
43
+ export declare const EXCLUDED_VALUE_PATTERNS: readonly ExcludedContentPattern[];
44
+ export type ExcludedContentHit = {
45
+ readonly category: ExcludedCategory;
46
+ /** the artifact-rooted path of the string that matched. */
47
+ readonly path: string;
48
+ /** the matched run, which is short by construction and is what a caller redacts. */
49
+ readonly match: string;
50
+ };
51
+ /**
52
+ * Every string anywhere inside one value, against every value-shaped pattern.
53
+ *
54
+ * Object keys are walked as paths and never scanned as content: a key is a name
55
+ * by construction, which is the same line the pattern set is drawn on. The walk
56
+ * is total over JSON and stops at nothing, so a token nested under a body
57
+ * example twelve levels down is found; the first matching category per string
58
+ * wins, since one string reported six times says nothing more than once.
59
+ *
60
+ * Order is the walk's order, which for an object is its own key order. A caller
61
+ * that needs a stable report over two spellings of the same object sorts what
62
+ * comes back; the compile gate below reads only the first hit and never the
63
+ * order.
64
+ */
65
+ export declare function scanExcludedContent(value: unknown, path: string): readonly ExcludedContentHit[];
@@ -0,0 +1,113 @@
1
+ /**
2
+ * AD-18's excluded categories, as patterns that read a value rather than a name.
3
+ *
4
+ * AD-18 excludes "credentials, tokens, real names, email addresses, account
5
+ * identifiers, and transaction content" from every artifact this package
6
+ * produces or publishes, and binds "published examples and test fixtures as
7
+ * strictly as real runs". Until this module the whole prohibition was carried by
8
+ * one test that scans `corpus/dev`, so a contract carrying a live-shaped token
9
+ * compiled clean and the token travelled into the sealed brief.
10
+ *
11
+ * Only the value-shaped half of that scan ships here, and the split is the point
12
+ * rather than an economy. AD-18's own reading of the rule for the header channel
13
+ * is that a declaration "names the header and its type and never carries a
14
+ * credential value", so a name is what an author is supposed to write: an
15
+ * operation declaring a `password` body key or an `Authorization` header is a
16
+ * contract for an authentication API, and a gate matching `\bpassword\b` would
17
+ * reject exactly the systems this package exists to evaluate. The patterns below
18
+ * therefore match only strings whose shape is decidable without knowing what
19
+ * field they sit in: a PEM header, a token with an issuer prefix, an address, a
20
+ * checkable account number, a card-shaped digit run. The name-shaped patterns
21
+ * stay in `tests/architecture/dev-corpus.test.ts`, where review rather than a
22
+ * compiler decides.
23
+ *
24
+ * Each pattern carries a string it must fire on, and a test asserts every one
25
+ * still does. A pattern that has rotted into a regex matching nothing is worse
26
+ * than no pattern: it reports clean forever.
27
+ */
28
+ /** AD-18's six categories, in the rule's own order. */
29
+ export const EXCLUDED_CATEGORIES = [
30
+ 'credentials',
31
+ 'tokens',
32
+ 'real names',
33
+ 'email addresses',
34
+ 'account identifiers',
35
+ 'transaction content',
36
+ ];
37
+ /**
38
+ * `real names` has no member here and never will: a personal name has no surface
39
+ * form, so every pattern that reaches one reads the field that carries it, which
40
+ * is a name-shaped test. The category stays in the tuple above because AD-18
41
+ * names six and dropping one from the vocabulary would make the gate look total.
42
+ */
43
+ export const EXCLUDED_VALUE_PATTERNS = [
44
+ {
45
+ category: 'credentials',
46
+ regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----/,
47
+ fires: '-----BEGIN RSA PRIVATE KEY-----',
48
+ },
49
+ {
50
+ category: 'tokens',
51
+ regex: /(?<![A-Za-z])(?:[Bb]earer\s+[\w.~+/-]{16,}|eyJ[\w-]{8,}\.[\w-]{8,}|sk-[A-Za-z0-9]{16,}|gh[pousr]_[A-Za-z0-9]{16,}|AKIA[0-9A-Z]{16})/,
52
+ fires: 'Authorization: Bearer abcdefghijklmnopqrstuv',
53
+ },
54
+ {
55
+ category: 'email addresses',
56
+ regex: /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/,
57
+ fires: '{"contact":"ada@example.com"}',
58
+ },
59
+ {
60
+ // Case-sensitive: an IBAN is upper case, and a case-insensitive form
61
+ // matches a run inside a lower-case hex digest.
62
+ category: 'account identifiers',
63
+ regex: /\b[A-Z]{2}\d{2}[A-Z0-9]{11,30}\b|\b\d{3}-\d{2}-\d{4}\b/,
64
+ fires: 'GB29NWBK60161331926819',
65
+ },
66
+ {
67
+ category: 'transaction content',
68
+ regex: /(?<![0-9A-Za-z])\d{4}(?:[ -]\d{4}){3}(?![0-9A-Za-z])/,
69
+ fires: 'charged 4111 1111 1111 1111',
70
+ },
71
+ ];
72
+ const step = (path, key) => typeof key === 'number' ? `${path}[${key}]` : `${path}.${key}`;
73
+ /**
74
+ * Every string anywhere inside one value, against every value-shaped pattern.
75
+ *
76
+ * Object keys are walked as paths and never scanned as content: a key is a name
77
+ * by construction, which is the same line the pattern set is drawn on. The walk
78
+ * is total over JSON and stops at nothing, so a token nested under a body
79
+ * example twelve levels down is found; the first matching category per string
80
+ * wins, since one string reported six times says nothing more than once.
81
+ *
82
+ * Order is the walk's order, which for an object is its own key order. A caller
83
+ * that needs a stable report over two spellings of the same object sorts what
84
+ * comes back; the compile gate below reads only the first hit and never the
85
+ * order.
86
+ */
87
+ export function scanExcludedContent(value, path) {
88
+ const hits = [];
89
+ const visit = (node, at) => {
90
+ if (typeof node === 'string') {
91
+ for (const { category, regex } of EXCLUDED_VALUE_PATTERNS) {
92
+ const match = regex.exec(node);
93
+ if (match === null)
94
+ continue;
95
+ hits.push({ category, path: at, match: match[0] });
96
+ return;
97
+ }
98
+ return;
99
+ }
100
+ if (Array.isArray(node)) {
101
+ node.forEach((element, index) => {
102
+ visit(element, step(at, index));
103
+ });
104
+ return;
105
+ }
106
+ if (node === null || typeof node !== 'object')
107
+ return;
108
+ for (const [key, child] of Object.entries(node))
109
+ visit(child, step(at, key));
110
+ };
111
+ visit(value, path);
112
+ return hits;
113
+ }
@@ -1,5 +1,5 @@
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'];
1
+ /** AD-5's twenty-six 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', 'binding-cycle', 'captured-channel-undeclared', 'rubric-scores-reasoning-prose', 'rubric-unanchored', 'rubric-evidence-unreachable', 'forbidden-input-floor-incomplete', 'scoped-reference-resolves-forbidden', 'waiver-incomplete', 'brief-exceeds-scripting-bound', 'unresolved-artifact-reference', 'irreducible-step-reference', 'excluded-content-in-declaration'];
3
3
  export type FailureCode = (typeof FAILURE_CODES)[number];
4
4
  /**
5
5
  * AD-5's compile-time failure registry: thrown when a structural error fails
@@ -1,11 +1,11 @@
1
- /** AD-5's twenty-one compile-time failure codes, as data, in the table's order. */
1
+ /** AD-5's twenty-six compile-time failure codes, as data, in the table's order. */
2
2
  // This lives in `core/`, since it's a plain data tuple and `core/schemas/`
3
3
  // is reserved for Zod definitions. The compiler that emits these codes lives
4
4
  // in `core/compile/`, one directory over.
5
5
  //
6
6
  // `scripts/check-ad5-registry.ts` checks this tuple against AD-5's own
7
7
  // registry table under `npm run validate`; the tuple's own invariants
8
- // (twenty-one members, unique, kebab-case) are locked in
8
+ // (twenty-six members, unique, kebab-case) are locked in
9
9
  // tests/schemas/failure-codes.test.ts.
10
10
  export const FAILURE_CODES = [
11
11
  'missing-requirement-linkage',
@@ -22,6 +22,8 @@ export const FAILURE_CODES = [
22
22
  'unsupported-interface-kind',
23
23
  'nested-temporal-clause',
24
24
  'plan-exceeds-scripting-bound',
25
+ 'binding-cycle',
26
+ 'captured-channel-undeclared',
25
27
  'rubric-scores-reasoning-prose',
26
28
  'rubric-unanchored',
27
29
  'rubric-evidence-unreachable',
@@ -29,6 +31,9 @@ export const FAILURE_CODES = [
29
31
  'scoped-reference-resolves-forbidden',
30
32
  'waiver-incomplete',
31
33
  'brief-exceeds-scripting-bound',
34
+ 'unresolved-artifact-reference',
35
+ 'irreducible-step-reference',
36
+ 'excluded-content-in-declaration',
32
37
  ];
33
38
  /**
34
39
  * AD-5's compile-time failure registry: thrown when a structural error fails
@@ -0,0 +1,201 @@
1
+ /**
2
+ * The eleven conditions `core/ingest` detects, and the ladder input each one
3
+ * feeds.
4
+ *
5
+ * Each condition is a cross-artifact rule a shipped schema names `core/ingest`
6
+ * as the enforcement point for, and none of them is a parse failure: the
7
+ * artifacts arrive already parsed, and a rule two conforming artifacts break
8
+ * between them is a finding about the run rather than a rejection of its bytes.
9
+ *
10
+ * The kinds are a runtime tuple first and the union draws from it, the idiom
11
+ * `RUNTIME_FAULT_CODES`, `FORBIDDEN_INPUT_FLOOR`, and `ORACLE_DISPOSITIONS` are
12
+ * written in. A union's `kind` literals are erased at compile time, so a drift
13
+ * check over a bare union could only compare the mapping below against itself or
14
+ * against a hand-written second list, which is the drift it exists to catch.
15
+ *
16
+ * `ladder.ts` is imported type-only on purpose. `isolatedModules` without
17
+ * `verbatimModuleSyntax` compiles a value import used only in type position
18
+ * silently, which would put the whole ladder module on this module's runtime
19
+ * load path; `preflight/reduce.ts` imports `ReduceStage` the same way.
20
+ */
21
+ import type { ForbiddenInput } from '../schemas/eval-contract.ts';
22
+ import type { EvidenceIntegrityInputs, OutcomeStateInputs } from '../score/ladder.ts';
23
+ import type { UnwitnessedQuotation } from '../score/quotation.ts';
24
+ /**
25
+ * The eleven kinds, in the order `ingest` records them. The two isolation-family
26
+ * kinds and the two evaluator-configuration ones are sequenced here; the order
27
+ * of the values inside one `isolation-manifest-violation` is the manifest's own
28
+ * array order and belongs to the stage, not to this tuple.
29
+ */
30
+ export declare const INGEST_CONDITION_KINDS: readonly ['duplicate-record-identifier', 'dangling-citation', 'dangling-disposition-citation', 'unwitnessed-quotation', 'isolation-manifest-absent', 'isolation-manifest-violation', 'forbidden-input-not-withheld', 'cross-artifact-disagreement', 'evaluator-configuration-absent', 'evaluator-configuration-digest-mismatch', 'judge-result-unscored'];
31
+ export type IngestConditionKind = (typeof INGEST_CONDITION_KINDS)[number];
32
+ /**
33
+ * The three fields AD-32 requires the sealed run record and the isolation
34
+ * manifest to agree on, in the order a disagreement is reported.
35
+ *
36
+ * `conditionArm` sits on both artifacts and is deliberately excluded: both
37
+ * schemas describe it as "an opaque caller label with no product semantics, per
38
+ * AD-24", so making a mismatch invalidate would mint a normative rule with no
39
+ * AD behind it and would be the one invalidating condition in the design with
40
+ * nothing downstream reading it.
41
+ */
42
+ export declare const AGREEMENT_FIELDS: readonly ['runId', 'contractDigest', 'evaluatorConfigurationDigest'];
43
+ export type AgreementField = (typeof AGREEMENT_FIELDS)[number];
44
+ /**
45
+ * An identifier the record uses twice where everything downstream addresses by
46
+ * it once.
47
+ *
48
+ * `SealedRunRecord` refines exactly one array for uniqueness, and it refines it
49
+ * on `sequence` rather than on an identifier, so all three of `observationId`,
50
+ * `findingId`, and `oracleId` may repeat in a record that parses. Nothing
51
+ * downstream can then tell the pair apart: `verdictBasis` names a finding by its
52
+ * identifier, `FindingConfidence` is keyed by it, `resolveOutcome` reads one
53
+ * disposition per oracle, and `auditQuotation` indexes observations into a `Map`
54
+ * keyed on `observationId`, which is last-write-wins. Recorded rather than
55
+ * repaired, because choosing which of two entries an identifier means is not a
56
+ * choice this stage can make.
57
+ *
58
+ * The observation case is the one with teeth. A repeated `observationId` decides
59
+ * which body a quotation is checked against, so it changes whether a defect
60
+ * finding's evidence is witnessed at all, which is the AD-32
61
+ * declared-versus-observed property this stage exists to check. The other two
62
+ * change an order; this one changes an answer.
63
+ *
64
+ * It is also what makes the product's `findings` and `dispositions` order total,
65
+ * and `unwitnessedQuotations` a function of the record's content, in every
66
+ * record that does not carry this condition.
67
+ */
68
+ export type DuplicateRecordIdentifier = {
69
+ readonly kind: Extract<IngestConditionKind, 'duplicate-record-identifier'>;
70
+ readonly subject: 'observation' | 'finding' | 'oracle-disposition' | 'judge-result';
71
+ readonly identifier: string;
72
+ readonly occurrences: number;
73
+ };
74
+ /**
75
+ * A finding citing an observation identifier the record does not declare. One
76
+ * per finding, carrying every identifier that resolved to nothing, so a
77
+ * consumer never has to re-derive which half of the citation was bad.
78
+ */
79
+ export type DanglingCitation = {
80
+ readonly kind: Extract<IngestConditionKind, 'dangling-citation'>;
81
+ readonly findingId: string;
82
+ readonly unresolvedObservationIds: readonly string[];
83
+ };
84
+ /**
85
+ * A disposition citing an observation identifier the record does not declare.
86
+ *
87
+ * The record's second citation site, and the one nothing else reads. AD-33 wants
88
+ * "every disposition citing supporting observations, and an unsupported
89
+ * disposition invalidating cross-artifact agreement rather than being believed";
90
+ * `resolveOutcome`'s `unsupported-disposition` covers the empty list only, so a
91
+ * `violated` disposition corroborated by a citation to nothing would otherwise
92
+ * be believed. Ingest is the only stage holding the dispositions and the
93
+ * observations at once.
94
+ */
95
+ export type DanglingDispositionCitation = {
96
+ readonly kind: Extract<IngestConditionKind, 'dangling-disposition-citation'>;
97
+ readonly oracleId: string;
98
+ readonly unresolvedObservationIds: readonly string[];
99
+ };
100
+ /**
101
+ * The quotation audit's own return value, unchanged. `auditQuotation` already
102
+ * shapes the payload its consumer declares, so re-projecting it here would be a
103
+ * second spelling of one result.
104
+ */
105
+ export type UnwitnessedQuotationCondition = {
106
+ readonly kind: Extract<IngestConditionKind, 'unwitnessed-quotation'>;
107
+ readonly quotations: readonly UnwitnessedQuotation[];
108
+ };
109
+ /**
110
+ * AD-16's absent manifest. No payload: there is no artifact to describe, which
111
+ * is why no schema can carry this rule and why the manifest's own `.meta` hands
112
+ * the violating case to `core/ingest`.
113
+ */
114
+ export type IsolationManifestAbsent = {
115
+ readonly kind: Extract<IngestConditionKind, 'isolation-manifest-absent'>;
116
+ };
117
+ /**
118
+ * A declared violation, an observed mount, network target, or tool call outside
119
+ * its allowlist, or any combination. One condition rather than four, each
120
+ * exceeded allowlist carrying its own offending values in the manifest's array
121
+ * order, so a consumer reads values rather than parsing them back out of a
122
+ * joined summary.
123
+ */
124
+ export type IsolationManifestViolation = {
125
+ readonly kind: Extract<IngestConditionKind, 'isolation-manifest-violation'>;
126
+ readonly violation: string | null;
127
+ readonly mountsOutsideAllowlist: readonly string[];
128
+ readonly networkTargetsOutsideAllowlist: readonly string[];
129
+ readonly toolCallsOutsideAllowlist: readonly string[];
130
+ };
131
+ /**
132
+ * AD-16's first clause: a prohibited input the manifest accounts for with
133
+ * `withheld: false`. Named in `FORBIDDEN_INPUT_FLOOR` order, since that list is
134
+ * the floor's one home and the manifest's key order is the caller's.
135
+ */
136
+ export type ForbiddenInputNotWithheld = {
137
+ readonly kind: Extract<IngestConditionKind, 'forbidden-input-not-withheld'>;
138
+ readonly inputs: readonly ForbiddenInput[];
139
+ };
140
+ /**
141
+ * One field the two artifacts disagree on, carrying both values. Per field
142
+ * rather than per record: a run whose two artifacts disagree about which
143
+ * configuration produced it is a different finding from one that disagrees
144
+ * about which run it is.
145
+ */
146
+ export type CrossArtifactDisagreement = {
147
+ readonly kind: Extract<IngestConditionKind, 'cross-artifact-disagreement'>;
148
+ readonly field: AgreementField;
149
+ readonly recordValue: string;
150
+ readonly manifestValue: string;
151
+ };
152
+ /**
153
+ * AD-24's absent evaluator configuration. No payload, for the same reason
154
+ * `IsolationManifestAbsent` carries none: there is no artifact to describe.
155
+ */
156
+ export type EvaluatorConfigurationAbsent = {
157
+ readonly kind: Extract<IngestConditionKind, 'evaluator-configuration-absent'>;
158
+ };
159
+ /**
160
+ * The digest the record declares for the evaluator configuration against the
161
+ * one that artifact actually produces.
162
+ *
163
+ * The operand is the record's declaration. A disagreement between the record's
164
+ * and the manifest's declarations is already `cross-artifact-disagreement`, and
165
+ * what this condition adds is the half AD-32 calls the trust boundary: both
166
+ * declarations are caller-attested, and only the recomputation reads the
167
+ * artifact.
168
+ */
169
+ export type EvaluatorConfigurationDigestMismatch = {
170
+ readonly kind: Extract<IngestConditionKind, 'evaluator-configuration-digest-mismatch'>;
171
+ readonly declaredDigest: string;
172
+ readonly computedDigest: string;
173
+ };
174
+ /**
175
+ * AD-17's record-decidable half: a judge result carrying `score: null`. The
176
+ * other half, that the criterion is one the cited rubric declares, needs the
177
+ * rubric, which no stage row declares as an input.
178
+ */
179
+ export type JudgeResultUnscored = {
180
+ readonly kind: Extract<IngestConditionKind, 'judge-result-unscored'>;
181
+ readonly rubricId: string;
182
+ readonly criterionId: string;
183
+ };
184
+ export type IngestCondition = DuplicateRecordIdentifier | DanglingCitation | DanglingDispositionCitation | UnwitnessedQuotationCondition | IsolationManifestAbsent | IsolationManifestViolation | ForbiddenInputNotWithheld | CrossArtifactDisagreement | EvaluatorConfigurationAbsent | EvaluatorConfigurationDigestMismatch | JudgeResultUnscored;
185
+ /**
186
+ * The ladder field a condition feeds. Every one of the eleven kinds now
187
+ * carries a rung, so the union below names all ten `EvidenceIntegrityInputs`
188
+ * fields that carry an ingest condition and `null` is dropped entirely: a
189
+ * kind with no rung is a compile error now, not a documented gap.
190
+ *
191
+ * Ten pairs built with `Extract` rather than a bare `keyof` product: the
192
+ * product would also pull in fields like `internallyInconsistent`, whose row
193
+ * is FAIL and whose guard reads "internally inconsistent under AD-17" rather
194
+ * than reading an `IngestCondition`, and a bare key would lose which of the
195
+ * two input types a member came from. Renaming a field in `ladder.ts`
196
+ * collapses its branch here to `never`, so the mapping below stops compiling
197
+ * instead of pointing at a field that no longer exists.
198
+ */
199
+ export type LadderTarget = Extract<keyof EvidenceIntegrityInputs, 'isolationViolation'> | Extract<keyof OutcomeStateInputs, 'unwitnessedQuotations'> | Extract<keyof EvidenceIntegrityInputs, 'duplicateRecordIdentifiers'> | Extract<keyof EvidenceIntegrityInputs, 'danglingCitations'> | Extract<keyof EvidenceIntegrityInputs, 'danglingDispositionCitations'> | Extract<keyof EvidenceIntegrityInputs, 'forbiddenInputsNotWithheld'> | Extract<keyof EvidenceIntegrityInputs, 'crossArtifactDisagreements'> | Extract<keyof EvidenceIntegrityInputs, 'evaluatorConfigurationAbsent'> | Extract<keyof EvidenceIntegrityInputs, 'evaluatorConfigurationDigestMismatches'> | Extract<keyof EvidenceIntegrityInputs, 'judgeResultsUnscored'>;
200
+ /** Total over the kinds tuple: a new kind fails to compile until it declares its rung. */
201
+ export declare const LADDER_TARGETS: Record<IngestConditionKind, LadderTarget>;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The eleven kinds, in the order `ingest` records them. The two isolation-family
3
+ * kinds and the two evaluator-configuration ones are sequenced here; the order
4
+ * of the values inside one `isolation-manifest-violation` is the manifest's own
5
+ * array order and belongs to the stage, not to this tuple.
6
+ */
7
+ export const INGEST_CONDITION_KINDS = [
8
+ 'duplicate-record-identifier',
9
+ 'dangling-citation',
10
+ 'dangling-disposition-citation',
11
+ 'unwitnessed-quotation',
12
+ 'isolation-manifest-absent',
13
+ 'isolation-manifest-violation',
14
+ 'forbidden-input-not-withheld',
15
+ 'cross-artifact-disagreement',
16
+ 'evaluator-configuration-absent',
17
+ 'evaluator-configuration-digest-mismatch',
18
+ 'judge-result-unscored',
19
+ ];
20
+ /**
21
+ * The three fields AD-32 requires the sealed run record and the isolation
22
+ * manifest to agree on, in the order a disagreement is reported.
23
+ *
24
+ * `conditionArm` sits on both artifacts and is deliberately excluded: both
25
+ * schemas describe it as "an opaque caller label with no product semantics, per
26
+ * AD-24", so making a mismatch invalidate would mint a normative rule with no
27
+ * AD behind it and would be the one invalidating condition in the design with
28
+ * nothing downstream reading it.
29
+ */
30
+ export const AGREEMENT_FIELDS = [
31
+ 'runId',
32
+ 'contractDigest',
33
+ 'evaluatorConfigurationDigest',
34
+ ];
35
+ /** Total over the kinds tuple: a new kind fails to compile until it declares its rung. */
36
+ export const LADDER_TARGETS = {
37
+ 'duplicate-record-identifier': 'duplicateRecordIdentifiers',
38
+ 'dangling-citation': 'danglingCitations',
39
+ 'dangling-disposition-citation': 'danglingDispositionCitations',
40
+ 'unwitnessed-quotation': 'unwitnessedQuotations',
41
+ 'isolation-manifest-absent': 'isolationViolation',
42
+ 'isolation-manifest-violation': 'isolationViolation',
43
+ 'forbidden-input-not-withheld': 'forbiddenInputsNotWithheld',
44
+ 'cross-artifact-disagreement': 'crossArtifactDisagreements',
45
+ 'evaluator-configuration-absent': 'evaluatorConfigurationAbsent',
46
+ 'evaluator-configuration-digest-mismatch': 'evaluatorConfigurationDigestMismatches',
47
+ 'judge-result-unscored': 'judgeResultsUnscored',
48
+ };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The ingest stage's surface for the rest of `core/`: the stage itself, the
3
+ * product it owns, the condition vocabulary, and the ladder input each
4
+ * condition feeds.
5
+ *
6
+ * Nothing here reaches `src/index.ts` or `src/application/index.ts`.
7
+ * `validated-observations` is an internal stage product AD-24 exempts from
8
+ * publication, so it has no schema, no registry entry, and no published type.
9
+ */
10
+ export type { AgreementField, CrossArtifactDisagreement, DanglingCitation, DanglingDispositionCitation, DuplicateRecordIdentifier, EvaluatorConfigurationAbsent, EvaluatorConfigurationDigestMismatch, ForbiddenInputNotWithheld, IngestCondition, IngestConditionKind, IsolationManifestAbsent, IsolationManifestViolation, JudgeResultUnscored, LadderTarget, UnwitnessedQuotationCondition, } from './conditions.ts';
11
+ export { AGREEMENT_FIELDS, INGEST_CONDITION_KINDS, LADDER_TARGETS, } from './conditions.ts';
12
+ export type { ValidatedObservations } from './ingest.ts';
13
+ export { ingest } from './ingest.ts';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The ingest stage's surface for the rest of `core/`: the stage itself, the
3
+ * product it owns, the condition vocabulary, and the ladder input each
4
+ * condition feeds.
5
+ *
6
+ * Nothing here reaches `src/index.ts` or `src/application/index.ts`.
7
+ * `validated-observations` is an internal stage product AD-24 exempts from
8
+ * publication, so it has no schema, no registry entry, and no published type.
9
+ */
10
+ export { AGREEMENT_FIELDS, INGEST_CONDITION_KINDS, LADDER_TARGETS, } from './conditions.js';
11
+ export { ingest } from './ingest.js';