@orkestrel/qualifier 0.0.11 → 0.0.13

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.
@@ -3,17 +3,17 @@ let _orkestrel_contract = require("@orkestrel/contract");
3
3
  let _orkestrel_reason = require("@orkestrel/reason");
4
4
  let _orkestrel_emitter = require("@orkestrel/emitter");
5
5
  //#region src/core/constants.ts
6
- /** Default definition validation policy for `createQualifier` / `Qualifier.qualify`. */
6
+ /** Holds the default definition validation policy for `createQualifier` / `Qualifier.qualify`. */
7
7
  var DEFAULT_QUALIFIER_VALIDATE = true;
8
- /** The reserved internal projection namespace a pass's working projection is written under. */
8
+ /** Names the reserved internal projection namespace a pass's working projection is written under. */
9
9
  var QUALIFICATION_KEY = "qualification";
10
- /** Eligibility severity order — most to least severe. */
10
+ /** Lists the eligibility severities in order — most to least severe. */
11
11
  var ELIGIBILITY_PRECEDENCE = Object.freeze([
12
12
  "ineligible",
13
13
  "referral",
14
14
  "eligible"
15
15
  ]);
16
- /** The eligibility impact of each {@link QualificationEffect}; `condition` remains eligible. */
16
+ /** Maps each {@link QualificationEffect} to its eligibility impact; `condition` remains eligible. */
17
17
  var EFFECT_ELIGIBILITIES = Object.freeze({
18
18
  restriction: "ineligible",
19
19
  referral: "referral",
@@ -22,14 +22,19 @@ var EFFECT_ELIGIBILITIES = Object.freeze({
22
22
  //#endregion
23
23
  //#region src/core/errors.ts
24
24
  /**
25
- * A coded programmer error thrown by the qualifier layer.
25
+ * Represents a coded programmer error thrown by the qualifier layer.
26
26
  *
27
27
  * @remarks
28
- * `DEFINITION` — a qualification definition failed semantic validation.
29
- * `MISMATCH` — a subject is not a record or already carries the reserved
30
- * `qualification` key. `DESTROYED` — use of a destroyed qualifier.
31
- * `ENGINE` — the underlying reason engine threw while running a pass (the
32
- * original throw is preserved as `context.cause`).
28
+ * `DEFINITION` — a qualification definition failed semantic validation; when
29
+ * `qualify`'s own validation raises it, `context.definition` names the
30
+ * definition, and when an engine throw maps to it, `context.pass` names the pass
31
+ * and `context.cause` preserves the original throw. `MISMATCH` — a subject is
32
+ * not a record or already carries the reserved `qualification` key.
33
+ * `DESTROYED` — use of a destroyed qualifier; an engine-mapped one carries
34
+ * `context.pass` and `context.cause`. `ENGINE` — the underlying reason engine
35
+ * threw while running a pass; `context.pass` names the pass and `context.cause`
36
+ * preserves the original throw. `context` is absent for a throw that carries no
37
+ * payload.
33
38
  */
34
39
  var QualifierError = class extends Error {
35
40
  code;
@@ -41,31 +46,47 @@ var QualifierError = class extends Error {
41
46
  this.context = context;
42
47
  }
43
48
  };
44
- /** Narrow a caught value to a {@link QualifierError}. */
49
+ /**
50
+ * Narrows a caught value to a {@link QualifierError}.
51
+ *
52
+ * @param value - The caught value to test
53
+ * @returns True if `value` is a {@link QualifierError} instance; false otherwise
54
+ */
45
55
  function isQualifierError(value) {
46
56
  return value instanceof QualifierError;
47
57
  }
48
58
  //#endregion
49
59
  //#region src/core/validators.ts
50
- /** Determine whether a value is an {@link Eligibility} literal. */
60
+ /**
61
+ * Determines whether a value is an {@link Eligibility} literal.
62
+ *
63
+ * @param value - The value to test
64
+ * @returns True if `value` is one of the eligibility literals; false otherwise
65
+ */
51
66
  var isEligibility = (0, _orkestrel_contract.literalOf)("eligible", "ineligible", "referral");
52
- /** Determine whether a value is a {@link QualificationEffect} literal. */
67
+ /**
68
+ * Determines whether a value is a {@link QualificationEffect} literal.
69
+ *
70
+ * @param value - The value to test
71
+ * @returns True if `value` is one of the effect literals; false otherwise
72
+ */
53
73
  var isQualificationEffect = (0, _orkestrel_contract.literalOf)("restriction", "referral", "condition");
54
74
  /**
55
- * Determine whether a value is an open string-keyed record of {@link Eligibility} values.
75
+ * Determines whether a value is an open string-keyed record of {@link Eligibility} values.
56
76
  *
57
77
  * @remarks
58
78
  * Every own string-named property is checked, including non-enumerable properties. Inherited and
59
79
  * symbol-named members are left unchecked because they are outside the record this guard certifies.
60
80
  *
61
81
  * @param value - The value to test
62
- * @returns `true` when `value` is a non-array object whose own string-named values are eligibilities
82
+ * @returns True if `value` is a non-array object whose own string-named values are
83
+ * eligibilities; false otherwise
63
84
  */
64
85
  function isEligibilityRecord(value) {
65
86
  return (0, _orkestrel_contract.whereOf)((0, _orkestrel_contract.objectOf)({}), (record) => Object.getOwnPropertyNames(record).every((key) => isEligibility(Reflect.get(record, key))))(value);
66
87
  }
67
88
  /**
68
- * Determine whether a value is an open result-side {@link Premise}.
89
+ * Determines whether a value is an open result-side {@link Premise}.
69
90
  *
70
91
  * @remarks
71
92
  * `expected` is unchecked and may be absent or contain any value because its published type is
@@ -73,7 +94,7 @@ function isEligibilityRecord(value) {
73
94
  * Unknown members are admitted.
74
95
  *
75
96
  * @param value - The value to test
76
- * @returns `true` when every checked premise member follows its published type
97
+ * @returns True if every checked premise member follows its published type; false otherwise
77
98
  */
78
99
  function isPremise(value) {
79
100
  return (0, _orkestrel_contract.objectOf)({
@@ -91,14 +112,14 @@ function isPremise(value) {
91
112
  ])(value);
92
113
  }
93
114
  /**
94
- * Determine whether a value is an open result-side {@link Finding}.
115
+ * Determines whether a value is an open result-side {@link Finding}.
95
116
  *
96
117
  * @remarks
97
118
  * Unknown members are admitted. Optional `scope` and `message` members may be absent or
98
119
  * `undefined`; a present defined value must be a string.
99
120
  *
100
121
  * @param value - The value to test
101
- * @returns `true` when every published finding member is valid
122
+ * @returns True if every published finding member is valid; false otherwise
102
123
  */
103
124
  function isFinding(value) {
104
125
  return (0, _orkestrel_contract.objectOf)({
@@ -113,14 +134,14 @@ function isFinding(value) {
113
134
  }, ["scope", "message"])(value);
114
135
  }
115
136
  /**
116
- * Determine whether a value is an open result-side {@link Derivation}.
137
+ * Determines whether a value is an open result-side {@link Derivation}.
117
138
  *
118
139
  * @remarks
119
140
  * Unknown members are admitted. `value` follows the published `number` type, including `NaN` and
120
141
  * infinities.
121
142
  *
122
143
  * @param value - The value to test
123
- * @returns `true` when every published derivation member is valid
144
+ * @returns True if every published derivation member is valid; false otherwise
124
145
  */
125
146
  function isDerivation(value) {
126
147
  return (0, _orkestrel_contract.objectOf)({
@@ -132,14 +153,14 @@ function isDerivation(value) {
132
153
  })(value);
133
154
  }
134
155
  /**
135
- * Determine whether a value is an open {@link QualificationResult} returned by a qualifier.
156
+ * Determines whether a value is an open {@link QualificationResult} returned by a qualifier.
136
157
  *
137
158
  * @remarks
138
159
  * This guard is result-postured for values returned through a borrowed qualifier interface. It
139
160
  * admits unknown members and class instances while checking the complete published result closure.
140
161
  *
141
162
  * @param value - The value to test
142
- * @returns `true` when every published qualification-result member is valid
163
+ * @returns True if every published qualification-result member is valid; false otherwise
143
164
  */
144
165
  function isQualificationResult(value) {
145
166
  return (0, _orkestrel_contract.objectOf)({
@@ -155,23 +176,20 @@ function isQualificationResult(value) {
155
176
  })(value);
156
177
  }
157
178
  /**
158
- * Determine whether a value is an open {@link QualificationValidationResult} returned by a qualifier.
159
- *
160
- * @remarks
161
- * `QualificationValidationResult` is an alias of reasons' validation-result type, so this IS
162
- * reasons' published open result guard by delegation — the alias tracks reasons' shape
163
- * automatically, and a hand-written member list here would silently stop checking a member a
164
- * later reason release adds.
179
+ * Determines whether a value is a {@link QualificationPass} (a quantitative or logical definition).
165
180
  *
166
181
  * @param value - The value to test
167
- * @returns `true` when every published qualification-validation-result member is valid
182
+ * @returns True if `value` is a complete reason quantitative or logical definition; false otherwise
168
183
  */
169
- var isQualificationValidationResult = _orkestrel_reason.isReasonValidationResult;
170
- /** Determine whether a value is a {@link QualificationPass} (a quantitative or logical definition). */
171
184
  function isQualificationPass(value) {
172
185
  return (0, _orkestrel_reason.isQuantitativeDefinition)(value) || (0, _orkestrel_reason.isLogicalDefinition)(value);
173
186
  }
174
- /** Determine whether a value is an exact {@link Ruling} record. */
187
+ /**
188
+ * Determines whether a value is an exact {@link Ruling} record.
189
+ *
190
+ * @param value - The value to test
191
+ * @returns True if `value` carries every ruling member and no unknown key; false otherwise
192
+ */
175
193
  function isRuling(value) {
176
194
  return (0, _orkestrel_contract.recordOf)({
177
195
  id: _orkestrel_contract.isString,
@@ -182,7 +200,12 @@ function isRuling(value) {
182
200
  message: _orkestrel_contract.isString
183
201
  }, ["scope", "message"])(value);
184
202
  }
185
- /** Determine whether a value is an exact {@link QualificationDefinition} record. */
203
+ /**
204
+ * Determines whether a value is an exact {@link QualificationDefinition} record.
205
+ *
206
+ * @param value - The value to test
207
+ * @returns True if `value` carries every definition member and no unknown key; false otherwise
208
+ */
186
209
  function isQualificationDefinition(value) {
187
210
  return (0, _orkestrel_contract.recordOf)({
188
211
  id: _orkestrel_contract.isString,
@@ -200,7 +223,7 @@ function isQualificationDefinition(value) {
200
223
  //#endregion
201
224
  //#region src/core/helpers.ts
202
225
  /**
203
- * Interpolate `{{dotted.path}}` tokens in a message template against a subject.
226
+ * Interpolates `{{dotted.path}}` tokens in a message template against a subject.
204
227
  *
205
228
  * @remarks
206
229
  * Each token is split on `.` into a `FieldPath` array and resolved with
@@ -230,19 +253,19 @@ function interpolateMessage(template, subject) {
230
253
  });
231
254
  }
232
255
  /**
233
- * Describe a {@link Premise} comparison as a display-neutral verb phrase.
256
+ * Renders a {@link Premise} comparison as a display-neutral verb phrase.
234
257
  *
235
258
  * @param comparison - The comparison to describe
236
259
  * @returns A display-neutral phrase
237
260
  *
238
261
  * @example
239
262
  * ```ts
240
- * import { describeComparison } from '@orkestrel/qualifier'
263
+ * import { renderComparison } from '@orkestrel/qualifier'
241
264
  *
242
- * describeComparison('above') // 'is more than'
265
+ * renderComparison('above') // 'is more than'
243
266
  * ```
244
267
  */
245
- function describeComparison(comparison) {
268
+ function renderComparison(comparison) {
246
269
  switch (comparison) {
247
270
  case "equals": return "is";
248
271
  case "not": return "is not";
@@ -257,7 +280,7 @@ function describeComparison(comparison) {
257
280
  }
258
281
  }
259
282
  /**
260
- * Render a structured or scalar expected value display-neutrally.
283
+ * Renders a structured or scalar expected value display-neutrally.
261
284
  *
262
285
  * @remarks
263
286
  * An array renders as its elements, each `String`-coerced, joined with `', '`.
@@ -272,14 +295,14 @@ function describeComparison(comparison) {
272
295
  *
273
296
  * @example
274
297
  * ```ts
275
- * import { describeValue } from '@orkestrel/qualifier'
298
+ * import { renderValue } from '@orkestrel/qualifier'
276
299
  *
277
- * describeValue([18, 25, 40]) // '18, 25, 40'
278
- * describeValue({ minimum: 18, maximum: 65 }) // '18 and 65'
279
- * describeValue(42) // '42'
300
+ * renderValue([18, 25, 40]) // '18, 25, 40'
301
+ * renderValue({ minimum: 18, maximum: 65 }) // '18 and 65'
302
+ * renderValue(42) // '42'
280
303
  * ```
281
304
  */
282
- function describeValue(value) {
305
+ function renderValue(value) {
283
306
  if (Array.isArray(value)) return value.map((entry) => String(entry)).join(", ");
284
307
  if ((0, _orkestrel_reason.isBounds)(value)) {
285
308
  const sides = [];
@@ -290,7 +313,7 @@ function describeValue(value) {
290
313
  return String(value);
291
314
  }
292
315
  /**
293
- * Render one {@link Premise} into a display-neutral sentence.
316
+ * Renders one {@link Premise} into a display-neutral sentence.
294
317
  *
295
318
  * @remarks
296
319
  * The checked form renders only when `field` and `comparison` are BOTH
@@ -303,25 +326,25 @@ function describeValue(value) {
303
326
  *
304
327
  * @example
305
328
  * ```ts
306
- * import { describePremise } from '@orkestrel/qualifier'
329
+ * import { renderPremise } from '@orkestrel/qualifier'
307
330
  *
308
- * describePremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
331
+ * renderPremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
309
332
  * // 'age is more than 18 → met'
310
333
  *
311
- * describePremise({ description: 'Applicant is enrolled', met: true })
334
+ * renderPremise({ description: 'Applicant is enrolled', met: true })
312
335
  * // 'Applicant is enrolled → met'
313
336
  * ```
314
337
  */
315
- function describePremise(entry, labels) {
338
+ function renderPremise(entry, labels) {
316
339
  const status = entry.met === void 0 ? "unknown" : entry.met ? "met" : "not met";
317
340
  if (entry.field === void 0 || entry.comparison === void 0) return `${entry.description ?? "Premise"} → ${status}`;
318
341
  const field = (0, _orkestrel_reason.formatField)(entry.field);
319
342
  const label = labels?.[field] ?? entry.label ?? field;
320
- const expected = entry.expected === void 0 ? "" : ` ${describeValue(entry.expected)}`;
321
- return `${label} ${describeComparison(entry.comparison)}${expected} → ${status}`;
343
+ const expected = entry.expected === void 0 ? "" : ` ${renderValue(entry.expected)}`;
344
+ return `${label} ${renderComparison(entry.comparison)}${expected} → ${status}`;
322
345
  }
323
346
  /**
324
- * Build a {@link Premise} from an authored {@link Check} and its evaluated
347
+ * Builds a {@link Premise} from an authored {@link Check} and its evaluated
325
348
  * `CheckResult`.
326
349
  *
327
350
  * @param check - The authored check
@@ -331,15 +354,15 @@ function describePremise(entry, labels) {
331
354
  *
332
355
  * @example
333
356
  * ```ts
334
- * import { premiseCheck } from '@orkestrel/qualifier'
335
- * import { check } from '@orkestrel/reason'
357
+ * import { checkToPremise } from '@orkestrel/qualifier'
358
+ * import { createCheck } from '@orkestrel/reason'
336
359
  *
337
- * const authored = check('age', 'above', 18)
338
- * premiseCheck(authored, { field: 'age', actual: 25, met: true })
360
+ * const authored = createCheck('age', 'above', 18)
361
+ * checkToPremise(authored, { field: 'age', actual: 25, met: true })
339
362
  * // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
340
363
  * ```
341
364
  */
342
- function premiseCheck(check, result, labels) {
365
+ function checkToPremise(check, result, labels) {
343
366
  const field = (0, _orkestrel_reason.formatField)(check.field);
344
367
  return {
345
368
  field: check.field,
@@ -351,13 +374,13 @@ function premiseCheck(check, result, labels) {
351
374
  };
352
375
  }
353
376
  /**
354
- * Build rich premises for one fired {@link Rule} by walking its premise atoms
377
+ * Builds rich premises for one fired {@link Rule} by walking its premise atoms
355
378
  * and re-evaluating each against the working subject.
356
379
  *
357
380
  * @remarks
358
381
  * A reason rule result carries only booleans, so this is the qualifier's own
359
382
  * premise-audit projection: each authored premise expression is flattened to its
360
- * atom leaves via `extractAtoms`, and each leaf's `Check` is re-evaluated through
383
+ * atom leaves through `extractAtoms`, and each leaf's `Check` is re-evaluated through
361
384
  * the injected `evaluator`. A membership check (`any` / `none`) over an EMPTY
362
385
  * array value is content-free and is skipped.
363
386
  *
@@ -369,29 +392,29 @@ function premiseCheck(check, result, labels) {
369
392
  *
370
393
  * @example
371
394
  * ```ts
372
- * import { logicalPremises } from '@orkestrel/qualifier'
373
- * import { atom, createEvaluator, logicalDefinition, rule } from '@orkestrel/reason'
395
+ * import { ruleToPremises } from '@orkestrel/qualifier'
396
+ * import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
374
397
  *
375
- * const gates = logicalDefinition('gates', 'Gates', [
376
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
398
+ * const gates = createLogicalDefinition('gates', 'Gates', [
399
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
377
400
  * ])
378
401
  * const evaluator = createEvaluator()
379
- * const premises = logicalPremises(gates.rules[0], { licensed: false }, evaluator)
402
+ * const premises = ruleToPremises(gates.rules[0], { licensed: false }, evaluator)
380
403
  *
381
404
  * premises[0]?.met // true
382
405
  * ```
383
406
  */
384
- function logicalPremises(rule, working, evaluator, labels) {
407
+ function ruleToPremises(rule, working, evaluator, labels) {
385
408
  const output = [];
386
409
  for (const premise of rule.premises) for (const atom of (0, _orkestrel_reason.extractAtoms)(premise)) {
387
410
  const { check } = atom;
388
411
  if ((check.operator === "any" || check.operator === "none") && Array.isArray(check.value) && check.value.length === 0) continue;
389
- output.push(premiseCheck(check, evaluator.evaluate(check, working), labels));
412
+ output.push(checkToPremise(check, evaluator.evaluate(check, working), labels));
390
413
  }
391
414
  return output;
392
415
  }
393
416
  /**
394
- * Locate an authored {@link Rule} by id.
417
+ * Locates an authored {@link Rule} by id.
395
418
  *
396
419
  * @param definition - The logical definition to search
397
420
  * @param id - The rule id
@@ -400,10 +423,10 @@ function logicalPremises(rule, working, evaluator, labels) {
400
423
  * @example
401
424
  * ```ts
402
425
  * import { findRule } from '@orkestrel/qualifier'
403
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
426
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
404
427
  *
405
- * const gates = logicalDefinition('gates', 'Gates', [
406
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
428
+ * const gates = createLogicalDefinition('gates', 'Gates', [
429
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
407
430
  * ])
408
431
  *
409
432
  * findRule(gates, 'licensed')?.id // 'licensed'
@@ -413,7 +436,7 @@ function findRule(definition, id) {
413
436
  return definition.rules.find((rule) => rule.id === id);
414
437
  }
415
438
  /**
416
- * Project one reason result into the internal qualification namespace.
439
+ * Projects one reason result into the internal qualification namespace.
417
440
  *
418
441
  * @remarks
419
442
  * A quantitative result contributes its numeric `value`. A logical result
@@ -428,10 +451,10 @@ function findRule(definition, id) {
428
451
  * @example
429
452
  * ```ts
430
453
  * import { reasonResultToProjection } from '@orkestrel/qualifier'
431
- * import { factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
454
+ * import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
432
455
  *
433
- * const cap = quantitativeDefinition('cap', 'TIV cap', [
434
- * factorGroup('limit', 'sum', [staticFactor('base', 500_000)]),
456
+ * const cap = createQuantitativeDefinition('cap', 'TIV cap', [
457
+ * createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
435
458
  * ])
436
459
  * const result = {
437
460
  * reasoning: 'quantitative' as const,
@@ -459,7 +482,7 @@ function reasonResultToProjection(definition, result) {
459
482
  return projection;
460
483
  }
461
484
  /**
462
- * Project a quantitative result into a {@link Derivation} audit record.
485
+ * Projects a quantitative result into a {@link Derivation} audit record.
463
486
  *
464
487
  * @param id - The pass id
465
488
  * @param result - The evaluated quantitative result
@@ -484,7 +507,7 @@ function quantitativeResultToDerivation(id, result) {
484
507
  };
485
508
  }
486
509
  /**
487
- * Wrap a {@link QualificationContext} under {@link QUALIFICATION_KEY}.
510
+ * Wraps a {@link QualificationContext} under {@link QUALIFICATION_KEY}.
488
511
  *
489
512
  * @param context - The accumulated projection context
490
513
  * @returns A record carrying the context under the reserved key
@@ -500,7 +523,7 @@ function qualificationToRecord(context) {
500
523
  return { [QUALIFICATION_KEY]: context };
501
524
  }
502
525
  /**
503
- * Copy-on-write merge one pass projection into the context.
526
+ * Merges one pass projection into the context, copy-on-write.
504
527
  *
505
528
  * @param context - The current projection context
506
529
  * @param id - The pass id to project under
@@ -522,7 +545,7 @@ function mergeQualificationContext(context, id, projection) {
522
545
  };
523
546
  }
524
547
  /**
525
- * Join a ruling, its logical rule result, the pass, the pre-projection subject,
548
+ * Joins a ruling, its logical rule result, the pass, the pre-projection subject,
526
549
  * and an evaluator into a {@link Finding}.
527
550
  *
528
551
  * @remarks
@@ -542,22 +565,22 @@ function mergeQualificationContext(context, id, projection) {
542
565
  * @example
543
566
  * ```ts
544
567
  * import { rulingToFinding } from '@orkestrel/qualifier'
545
- * import { atom, createEvaluator, logicalDefinition, rule } from '@orkestrel/reason'
568
+ * import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
546
569
  *
547
- * const gates = logicalDefinition('gates', 'Gates', [
548
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
570
+ * const gates = createLogicalDefinition('gates', 'Gates', [
571
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
549
572
  * ])
550
573
  * const ruling = { id: 'license', pass: 'gates', rule: 'licensed', effect: 'restriction' as const }
551
- * const result = { reasoning: 'logical' as const, conclusion: true, rules: [{ id: 'licensed', applied: true, premises: [true], conclusion: true }], count: 1, success: true, trace: [], errors: [] }
574
+ * const result = { reasoning: 'logical' as const, conclusion: true, rules: [{ id: 'licensed', applied: true, premises: [true] }], count: 1, success: true, trace: [], errors: [] }
552
575
  * const finding = rulingToFinding(ruling, gates, result, { licensed: false }, createEvaluator())
553
576
  *
554
577
  * finding.applied // true
555
578
  * ```
556
579
  */
557
580
  function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
558
- const applied = result.rules.find((item) => item.id === ruling.rule)?.applied ?? false;
581
+ const applied = result.rules.find((resolved) => resolved.id === ruling.rule)?.applied ?? false;
559
582
  const rule = pass.reasoning === "logical" ? findRule(pass, ruling.rule) : void 0;
560
- const premises = rule === void 0 ? [] : logicalPremises(rule, subject, evaluator, labels);
583
+ const premises = rule === void 0 ? [] : ruleToPremises(rule, subject, evaluator, labels);
561
584
  return {
562
585
  id: ruling.id,
563
586
  pass: ruling.pass,
@@ -570,7 +593,7 @@ function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
570
593
  };
571
594
  }
572
595
  /**
573
- * Derive global eligibility from applied, unscoped findings.
596
+ * Derives global eligibility from applied, unscoped findings.
574
597
  *
575
598
  * @remarks
576
599
  * Only findings that both applied and carry no `scope` shape global eligibility.
@@ -578,7 +601,8 @@ function rulingToFinding(ruling, pass, result, subject, evaluator, labels) {
578
601
  * `failed`.
579
602
  *
580
603
  * @param findings - The resolved findings
581
- * @param failed - Whether an operational pass failure occurred
604
+ * @param failed - If `true`, adds a synthetic `referral` impact so an operational failure
605
+ * fails closed; if `false`, derives eligibility from the findings alone. Default: `false`
582
606
  * @returns The most severe global eligibility
583
607
  *
584
608
  * @example
@@ -595,7 +619,7 @@ function deriveFindingEligibility(findings, failed = false) {
595
619
  return combineEligibilities(eligibilities);
596
620
  }
597
621
  /**
598
- * Return the most severe {@link Eligibility} in a list.
622
+ * Returns the most severe {@link Eligibility} in a list.
599
623
  *
600
624
  * @param eligibilities - The eligibilities to combine
601
625
  * @returns The most severe eligibility, or `'eligible'` for an empty list
@@ -612,7 +636,7 @@ function combineEligibilities(eligibilities) {
612
636
  return "eligible";
613
637
  }
614
638
  /**
615
- * Derive one eligibility per finding scope.
639
+ * Derives one eligibility per finding scope.
616
640
  *
617
641
  * @remarks
618
642
  * Only applied, scoped findings contribute. Each named scope combines its own
@@ -642,29 +666,29 @@ function deriveScopeEligibilities(findings) {
642
666
  return scopes;
643
667
  }
644
668
  /**
645
- * Find rulings whose pass or rule does not exist, whose pass is not logical, or
646
- * a pass id shadowing the reserved {@link QUALIFICATION_KEY}.
669
+ * Describes each ruling whose pass or rule does not exist or whose pass is not
670
+ * logical, and each pass id shadowing the reserved {@link QUALIFICATION_KEY}.
647
671
  *
648
672
  * @param definition - The qualification definition to check
649
673
  * @returns A fresh list of reference-error messages
650
674
  *
651
675
  * @example
652
676
  * ```ts
653
- * import { findMissingReferences, qualificationDefinition, rulingDefinition } from '@orkestrel/qualifier'
654
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
677
+ * import { createQualificationDefinition, createRuling, describeMissingReferences } from '@orkestrel/qualifier'
678
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
655
679
  *
656
- * const gates = logicalDefinition('gates', 'Gates', [
657
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
680
+ * const gates = createLogicalDefinition('gates', 'Gates', [
681
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
658
682
  * ])
659
- * const definition = qualificationDefinition('standard', 'Standard', [gates], {
660
- * rulings: [rulingDefinition('license', 'gates', 'absent', 'restriction')],
683
+ * const definition = createQualificationDefinition('standard', 'Standard', [gates], {
684
+ * rulings: [createRuling('license', 'gates', 'absent', 'restriction')],
661
685
  * })
662
686
  *
663
- * findMissingReferences(definition)
687
+ * describeMissingReferences(definition)
664
688
  * // ["Ruling 'license' references missing rule 'absent' in pass 'gates'"]
665
689
  * ```
666
690
  */
667
- function findMissingReferences(definition) {
691
+ function describeMissingReferences(definition) {
668
692
  const errors = [];
669
693
  const passes = new Map(definition.passes.map((pass) => [pass.id, pass]));
670
694
  for (const pass of definition.passes) if (pass.id === "qualification") errors.push(`Pass id must not equal reserved key '${QUALIFICATION_KEY}'`);
@@ -683,10 +707,10 @@ function findMissingReferences(definition) {
683
707
  return errors;
684
708
  }
685
709
  /**
686
- * Determine whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
710
+ * Determines whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
687
711
  *
688
712
  * @param subject - The subject to check
689
- * @returns `true` when the subject owns the reserved key
713
+ * @returns True if the subject owns the reserved key; false otherwise
690
714
  *
691
715
  * @example
692
716
  * ```ts
@@ -699,7 +723,7 @@ function hasReservedKey(subject) {
699
723
  return Object.hasOwn(subject, QUALIFICATION_KEY);
700
724
  }
701
725
  /**
702
- * Assert a value is a valid qualification {@link Subject}, narrowing it in place.
726
+ * Asserts a value is a valid qualification {@link Subject}, narrowing it in place.
703
727
  *
704
728
  * @param value - The candidate subject to validate
705
729
  * @throws {@link QualifierError} `'MISMATCH'` when the value is not a record, or
@@ -717,12 +741,12 @@ function assertSubject(value) {
717
741
  if (hasReservedKey(value)) throw new QualifierError("MISMATCH", `Qualification subject must not contain reserved key '${QUALIFICATION_KEY}'`);
718
742
  }
719
743
  /**
720
- * Map an engine throw caught while running one pass to a typed {@link QualifierError}.
744
+ * Maps an engine throw caught while running one pass to a typed {@link QualifierError}.
721
745
  *
722
746
  * @remarks
723
747
  * A reason `ReasonError('INVALID')` maps to `DEFINITION` (the engine rejected the
724
748
  * pass as an invalid definition); `ReasonError('DESTROYED')` maps to `DESTROYED`;
725
- * every other `ReasonError` code (e.g. `MISSING`) and every non-`ReasonError` throw
749
+ * every other `ReasonError` code (for example `MISSING`) and every non-`ReasonError` throw
726
750
  * (including a `bail: true` reasoner rethrow) maps to `ENGINE`. The original throw
727
751
  * is preserved as `context.cause`.
728
752
  *
@@ -759,7 +783,7 @@ function mapEngineError(error, pass) {
759
783
  });
760
784
  }
761
785
  /**
762
- * Find logical passes carrying no rulings.
786
+ * Describes each logical pass carrying no rulings.
763
787
  *
764
788
  * @remarks
765
789
  * A logical pass with no matching ruling can never contribute a finding, so its
@@ -771,18 +795,18 @@ function mapEngineError(error, pass) {
771
795
  *
772
796
  * @example
773
797
  * ```ts
774
- * import { findEmptyLogicalPasses, qualificationDefinition } from '@orkestrel/qualifier'
775
- * import { atom, logicalDefinition, rule } from '@orkestrel/reason'
798
+ * import { createQualificationDefinition, describeEmptyLogicalPasses } from '@orkestrel/qualifier'
799
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
776
800
  *
777
- * const gates = logicalDefinition('gates', 'Gates', [
778
- * rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
801
+ * const gates = createLogicalDefinition('gates', 'Gates', [
802
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
779
803
  * ])
780
- * const definition = qualificationDefinition('standard', 'Standard', [gates])
804
+ * const definition = createQualificationDefinition('standard', 'Standard', [gates])
781
805
  *
782
- * findEmptyLogicalPasses(definition) // ["Logical pass 'gates' has no rulings"]
806
+ * describeEmptyLogicalPasses(definition) // ["Logical pass 'gates' has no rulings"]
783
807
  * ```
784
808
  */
785
- function findEmptyLogicalPasses(definition) {
809
+ function describeEmptyLogicalPasses(definition) {
786
810
  const warnings = [];
787
811
  for (const pass of definition.passes) {
788
812
  if (pass.reasoning !== "logical") continue;
@@ -791,7 +815,7 @@ function findEmptyLogicalPasses(definition) {
791
815
  return warnings;
792
816
  }
793
817
  /**
794
- * Find quantitative passes never read by a later pass.
818
+ * Describes each quantitative pass never read by a later pass.
795
819
  *
796
820
  * @remarks
797
821
  * A quantitative pass's projected value lives under `qualification.<id>` — if no
@@ -805,18 +829,18 @@ function findEmptyLogicalPasses(definition) {
805
829
  *
806
830
  * @example
807
831
  * ```ts
808
- * import { findUnreadDerivations, qualificationDefinition } from '@orkestrel/qualifier'
809
- * import { factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
832
+ * import { createQualificationDefinition, describeUnreadDerivations } from '@orkestrel/qualifier'
833
+ * import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
810
834
  *
811
- * const cap = quantitativeDefinition('cap', 'TIV cap', [
812
- * factorGroup('limit', 'sum', [staticFactor('base', 500_000)]),
835
+ * const cap = createQuantitativeDefinition('cap', 'TIV cap', [
836
+ * createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
813
837
  * ])
814
- * const definition = qualificationDefinition('standard', 'Standard', [cap])
838
+ * const definition = createQualificationDefinition('standard', 'Standard', [cap])
815
839
  *
816
- * findUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
840
+ * describeUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
817
841
  * ```
818
842
  */
819
- function findUnreadDerivations(definition) {
843
+ function describeUnreadDerivations(definition) {
820
844
  const warnings = [];
821
845
  definition.passes.forEach((pass, index) => {
822
846
  if (pass.reasoning !== "quantitative") return;
@@ -833,69 +857,10 @@ function findUnreadDerivations(definition) {
833
857
  });
834
858
  return warnings;
835
859
  }
836
- /**
837
- * Build a {@link QualificationDefinition}.
838
- *
839
- * @remarks
840
- * Returns a fresh top-level definition, omitting absent optional keys;
841
- * `passes` and `rulings` arrays are copied, and record `metadata` is
842
- * shallow-copied so nested values are not deep-cloned.
843
- *
844
- * @param id - The definition id
845
- * @param name - The display name
846
- * @param passes - The ordered qualification passes
847
- * @param input - Optional description, rulings, and metadata
848
- * @returns A fresh qualification definition
849
- *
850
- * @example
851
- * ```ts
852
- * import { qualificationDefinition } from '@orkestrel/qualifier'
853
- *
854
- * qualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
855
- * ```
856
- */
857
- function qualificationDefinition(id, name, passes, input) {
858
- return {
859
- id,
860
- name,
861
- passes: [...passes],
862
- ...input?.description === void 0 ? {} : { description: input.description },
863
- ...input?.rulings === void 0 ? {} : { rulings: [...input.rulings] },
864
- ...input?.metadata === void 0 ? {} : { metadata: { ...input.metadata } }
865
- };
866
- }
867
- /**
868
- * Build a {@link Ruling} — one authored consequence for one rule in one pass.
869
- *
870
- * @param id - The ruling id
871
- * @param pass - The logical pass id the rule lives in
872
- * @param rule - The rule id whose firing this ruling reacts to
873
- * @param effect - The eligibility impact when the rule fires
874
- * @param input - Optional selection scope and message template
875
- * @returns A fresh ruling
876
- *
877
- * @example
878
- * ```ts
879
- * import { rulingDefinition } from '@orkestrel/qualifier'
880
- *
881
- * rulingDefinition('license', 'gates', 'licensed', 'restriction', { message: 'A license is required' })
882
- * ```
883
- */
884
- function rulingDefinition(id, pass, rule, effect, input) {
885
- return {
886
- id,
887
- pass,
888
- rule,
889
- effect,
890
- ...input?.scope === void 0 ? {} : { scope: input.scope },
891
- ...input?.message === void 0 ? {} : { message: input.message }
892
- };
893
- }
894
860
  //#endregion
895
861
  //#region src/core/Qualifier.ts
896
862
  /**
897
- * A qualifier — runs ordered passes over one reason engine and returns
898
- * eligibility.
863
+ * Runs ordered passes over one reason engine and returns eligibility.
899
864
  *
900
865
  * @remarks
901
866
  * The engine is OWNED when self-created (destroyed on `destroy()`) and borrowed
@@ -925,17 +890,92 @@ var Qualifier = class {
925
890
  this.#validate = options?.validate ?? true;
926
891
  this.#labels = options?.labels;
927
892
  }
893
+ /**
894
+ * Holds the typed observation surface carrying `derive`, `finding`, `qualify`, and
895
+ * `destroy`.
896
+ *
897
+ * @returns The emitter this qualifier owns
898
+ *
899
+ * @example
900
+ * ```ts
901
+ * import { Qualifier } from '@orkestrel/qualifier'
902
+ *
903
+ * const qualifier = new Qualifier()
904
+ * qualifier.emitter.on('qualify', (result) => console.log(result.eligibility))
905
+ * qualifier.destroy()
906
+ * ```
907
+ */
928
908
  get emitter() {
929
909
  return this.#emitter;
930
910
  }
911
+ /**
912
+ * Qualifies one subject against one authored definition.
913
+ *
914
+ * @remarks
915
+ * Semantic validation runs first when the `validate` option is on, so an invalid
916
+ * definition throws before any pass runs. Passes run in authored order over a
917
+ * copy-on-write working subject, and the caller's `subject` is never mutated. An
918
+ * unscoped restriction stops the remaining passes because no later finding can be
919
+ * more severe.
920
+ *
921
+ * @param subject - The record to qualify
922
+ * @param definition - The authored qualification definition
923
+ * @returns A fresh, frozen qualification result
924
+ * @throws {@link QualifierError} `'DEFINITION'` when validation is enabled and the
925
+ * definition fails semantic validation, or when the engine rejects a pass as an invalid
926
+ * definition; `'MISMATCH'` when the subject is not a record or already carries the
927
+ * reserved `qualification` key; `'DESTROYED'` after `destroy`, or when the engine
928
+ * reports itself destroyed mid-pass; `'ENGINE'` for every other engine throw
929
+ *
930
+ * @example
931
+ * ```ts
932
+ * import { createQualificationDefinition, createRuling, Qualifier } from '@orkestrel/qualifier'
933
+ * import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
934
+ *
935
+ * const gates = createLogicalDefinition('gates', 'Eligibility gates', [
936
+ * createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
937
+ * ])
938
+ * const definition = createQualificationDefinition('standard', 'Standard eligibility', [gates], {
939
+ * rulings: [createRuling('license', 'gates', 'licensed', 'restriction')],
940
+ * })
941
+ *
942
+ * const qualifier = new Qualifier()
943
+ * qualifier.qualify({ id: 'risk-1', licensed: false }, definition).eligibility // 'ineligible'
944
+ * qualifier.destroy()
945
+ * ```
946
+ */
931
947
  qualify(subject, definition) {
932
948
  this.#alive();
933
949
  if (this.#validate) {
934
950
  const validation = this.validate(definition);
935
- if (!validation.valid) throw new QualifierError("DEFINITION", `Qualification definition is invalid: ${validation.errors.join(", ")}`, definition.id);
951
+ if (!validation.valid) throw new QualifierError("DEFINITION", `Qualification definition is invalid: ${validation.errors.join(", ")}`, { definition: definition.id });
936
952
  }
937
953
  return this.#qualify(subject, definition);
938
954
  }
955
+ /**
956
+ * Validates one authored definition semantically, without running it.
957
+ *
958
+ * @remarks
959
+ * Structural shape is `isQualificationDefinition`'s job. This checks the meaning:
960
+ * non-empty id and name, valid and uniquely identified passes and rulings, every
961
+ * ruling reference resolving to a logical pass and one of its rules, and no pass id
962
+ * shadowing `QUALIFICATION_KEY`. An empty definition, a logical pass carrying no
963
+ * ruling, and a derivation no later pass reads are warnings rather than errors.
964
+ *
965
+ * @param definition - The authored qualification definition
966
+ * @returns A fresh validation result carrying `valid`, `errors`, and `warnings`
967
+ * @throws {@link QualifierError} `'DESTROYED'` after `destroy`
968
+ *
969
+ * @example
970
+ * ```ts
971
+ * import { createQualificationDefinition, Qualifier } from '@orkestrel/qualifier'
972
+ *
973
+ * const qualifier = new Qualifier()
974
+ * qualifier.validate(createQualificationDefinition('empty', 'Empty', []))
975
+ * // { valid: true, errors: [], warnings: ['Definition has no passes'] }
976
+ * qualifier.destroy()
977
+ * ```
978
+ */
939
979
  validate(definition) {
940
980
  this.#alive();
941
981
  const errors = [];
@@ -950,16 +990,33 @@ var Qualifier = class {
950
990
  });
951
991
  for (const id of (0, _orkestrel_reason.findDuplicates)(definition.passes)) errors.push(`Duplicate pass id '${id}'`);
952
992
  for (const id of (0, _orkestrel_reason.findDuplicates)(definition.rulings ?? [])) errors.push(`Duplicate ruling id '${id}'`);
953
- for (const reference of findMissingReferences(definition)) errors.push(reference);
993
+ for (const reference of describeMissingReferences(definition)) errors.push(reference);
954
994
  if (definition.passes.length === 0) warnings.push("Definition has no passes");
955
- for (const warning of findEmptyLogicalPasses(definition)) warnings.push(warning);
956
- for (const warning of findUnreadDerivations(definition)) warnings.push(warning);
995
+ for (const warning of describeEmptyLogicalPasses(definition)) warnings.push(warning);
996
+ for (const warning of describeUnreadDerivations(definition)) warnings.push(warning);
957
997
  return {
958
998
  valid: errors.length === 0,
959
999
  errors,
960
1000
  warnings
961
1001
  };
962
1002
  }
1003
+ /**
1004
+ * Destroys this qualifier, idempotently.
1005
+ *
1006
+ * @remarks
1007
+ * An engine this qualifier created is destroyed; an injected engine stays
1008
+ * caller-owned and is left alone. The `destroy` event fires before the emitter
1009
+ * itself is destroyed, and a later call returns without re-emitting.
1010
+ *
1011
+ * @example
1012
+ * ```ts
1013
+ * import { Qualifier } from '@orkestrel/qualifier'
1014
+ *
1015
+ * const qualifier = new Qualifier()
1016
+ * qualifier.destroy()
1017
+ * qualifier.destroy() // idempotent
1018
+ * ```
1019
+ */
963
1020
  destroy() {
964
1021
  if (this.#destroyed) return;
965
1022
  if (this.#owned) this.#engine.destroy();
@@ -1035,12 +1092,15 @@ var Qualifier = class {
1035
1092
  //#endregion
1036
1093
  //#region src/core/factories.ts
1037
1094
  /**
1038
- * Create one qualifier over a reason engine.
1095
+ * Creates one qualifier over a reason engine.
1039
1096
  *
1040
1097
  * @remarks
1041
1098
  * A standalone qualifier creates and OWNS one shared quantitative-plus-logical
1042
1099
  * reason engine, destroying it on `destroy()`. An injected `options.engine`
1043
- * remains caller-owned and is never destroyed. Validation is on by default.
1100
+ * remains caller-owned and is never destroyed. `validate` runs semantic
1101
+ * validation before every qualification. Default: `true`. `labels` overrides the
1102
+ * display name of a premise field, keyed by the dot-joined field path
1103
+ * `formatField` produces, and takes precedence over a premise's own `label`.
1044
1104
  *
1045
1105
  * @param options - Optional injected engine, validation, labels, and emitter hooks
1046
1106
  * @returns A {@link QualifierInterface}
@@ -1057,6 +1117,64 @@ var Qualifier = class {
1057
1117
  function createQualifier(options) {
1058
1118
  return new Qualifier(options);
1059
1119
  }
1120
+ /**
1121
+ * Creates a {@link QualificationDefinition}.
1122
+ *
1123
+ * @remarks
1124
+ * Returns a fresh top-level definition, omitting absent optional keys;
1125
+ * `passes` and `rulings` arrays are copied, and record `metadata` is
1126
+ * shallow-copied so nested values are not deep-cloned.
1127
+ *
1128
+ * @param id - The definition id
1129
+ * @param name - The display name
1130
+ * @param passes - The ordered qualification passes
1131
+ * @param input - Optional description, rulings, and metadata
1132
+ * @returns A fresh qualification definition
1133
+ *
1134
+ * @example
1135
+ * ```ts
1136
+ * import { createQualificationDefinition } from '@orkestrel/qualifier'
1137
+ *
1138
+ * createQualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
1139
+ * ```
1140
+ */
1141
+ function createQualificationDefinition(id, name, passes, input) {
1142
+ return {
1143
+ id,
1144
+ name,
1145
+ passes: [...passes],
1146
+ ...input?.description === void 0 ? {} : { description: input.description },
1147
+ ...input?.rulings === void 0 ? {} : { rulings: [...input.rulings] },
1148
+ ...input?.metadata === void 0 ? {} : { metadata: { ...input.metadata } }
1149
+ };
1150
+ }
1151
+ /**
1152
+ * Creates a {@link Ruling} — one authored consequence for one rule in one pass.
1153
+ *
1154
+ * @param id - The ruling id
1155
+ * @param pass - The logical pass id the rule lives in
1156
+ * @param rule - The rule id whose firing this ruling reacts to
1157
+ * @param effect - The eligibility impact when the rule fires
1158
+ * @param input - Optional selection scope and message template
1159
+ * @returns A fresh ruling
1160
+ *
1161
+ * @example
1162
+ * ```ts
1163
+ * import { createRuling } from '@orkestrel/qualifier'
1164
+ *
1165
+ * createRuling('license', 'gates', 'licensed', 'restriction', { message: 'A license is required' })
1166
+ * ```
1167
+ */
1168
+ function createRuling(id, pass, rule, effect, input) {
1169
+ return {
1170
+ id,
1171
+ pass,
1172
+ rule,
1173
+ effect,
1174
+ ...input?.scope === void 0 ? {} : { scope: input.scope },
1175
+ ...input?.message === void 0 ? {} : { message: input.message }
1176
+ };
1177
+ }
1060
1178
  //#endregion
1061
1179
  exports.DEFAULT_QUALIFIER_VALIDATE = DEFAULT_QUALIFIER_VALIDATE;
1062
1180
  exports.EFFECT_ELIGIBILITIES = EFFECT_ELIGIBILITIES;
@@ -1065,17 +1183,17 @@ exports.QUALIFICATION_KEY = QUALIFICATION_KEY;
1065
1183
  exports.Qualifier = Qualifier;
1066
1184
  exports.QualifierError = QualifierError;
1067
1185
  exports.assertSubject = assertSubject;
1186
+ exports.checkToPremise = checkToPremise;
1068
1187
  exports.combineEligibilities = combineEligibilities;
1188
+ exports.createQualificationDefinition = createQualificationDefinition;
1069
1189
  exports.createQualifier = createQualifier;
1190
+ exports.createRuling = createRuling;
1070
1191
  exports.deriveFindingEligibility = deriveFindingEligibility;
1071
1192
  exports.deriveScopeEligibilities = deriveScopeEligibilities;
1072
- exports.describeComparison = describeComparison;
1073
- exports.describePremise = describePremise;
1074
- exports.describeValue = describeValue;
1075
- exports.findEmptyLogicalPasses = findEmptyLogicalPasses;
1076
- exports.findMissingReferences = findMissingReferences;
1193
+ exports.describeEmptyLogicalPasses = describeEmptyLogicalPasses;
1194
+ exports.describeMissingReferences = describeMissingReferences;
1195
+ exports.describeUnreadDerivations = describeUnreadDerivations;
1077
1196
  exports.findRule = findRule;
1078
- exports.findUnreadDerivations = findUnreadDerivations;
1079
1197
  exports.hasReservedKey = hasReservedKey;
1080
1198
  exports.interpolateMessage = interpolateMessage;
1081
1199
  exports.isDerivation = isDerivation;
@@ -1087,18 +1205,17 @@ exports.isQualificationDefinition = isQualificationDefinition;
1087
1205
  exports.isQualificationEffect = isQualificationEffect;
1088
1206
  exports.isQualificationPass = isQualificationPass;
1089
1207
  exports.isQualificationResult = isQualificationResult;
1090
- exports.isQualificationValidationResult = isQualificationValidationResult;
1091
1208
  exports.isQualifierError = isQualifierError;
1092
1209
  exports.isRuling = isRuling;
1093
- exports.logicalPremises = logicalPremises;
1094
1210
  exports.mapEngineError = mapEngineError;
1095
1211
  exports.mergeQualificationContext = mergeQualificationContext;
1096
- exports.premiseCheck = premiseCheck;
1097
- exports.qualificationDefinition = qualificationDefinition;
1098
1212
  exports.qualificationToRecord = qualificationToRecord;
1099
1213
  exports.quantitativeResultToDerivation = quantitativeResultToDerivation;
1100
1214
  exports.reasonResultToProjection = reasonResultToProjection;
1101
- exports.rulingDefinition = rulingDefinition;
1215
+ exports.renderComparison = renderComparison;
1216
+ exports.renderPremise = renderPremise;
1217
+ exports.renderValue = renderValue;
1218
+ exports.ruleToPremises = ruleToPremises;
1102
1219
  exports.rulingToFinding = rulingToFinding;
1103
1220
 
1104
1221
  //# sourceMappingURL=index.cjs.map