@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.
- package/README.md +14 -10
- package/dist/src/core/index.cjs +315 -198
- package/dist/src/core/index.cjs.map +1 -1
- package/dist/src/core/index.d.cts +636 -420
- package/dist/src/core/index.d.ts +636 -420
- package/dist/src/core/index.js +307 -189
- package/dist/src/core/index.js.map +1 -1
- package/package.json +19 -16
|
@@ -19,7 +19,7 @@ import { Rule } from '@orkestrel/reason';
|
|
|
19
19
|
import { Subject } from '@orkestrel/reason';
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Asserts a value is a valid qualification {@link Subject}, narrowing it in place.
|
|
23
23
|
*
|
|
24
24
|
* @param value - The candidate subject to validate
|
|
25
25
|
* @throws {@link QualifierError} `'MISMATCH'` when the value is not a record, or
|
|
@@ -35,7 +35,28 @@ import { Subject } from '@orkestrel/reason';
|
|
|
35
35
|
export declare function assertSubject(value: unknown): asserts value is Subject;
|
|
36
36
|
|
|
37
37
|
/**
|
|
38
|
-
*
|
|
38
|
+
* Builds a {@link Premise} from an authored {@link Check} and its evaluated
|
|
39
|
+
* `CheckResult`.
|
|
40
|
+
*
|
|
41
|
+
* @param check - The authored check
|
|
42
|
+
* @param result - The evaluated check result carrying the resolved `actual` and `met`
|
|
43
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
44
|
+
* @returns A fresh premise
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { checkToPremise } from '@orkestrel/qualifier'
|
|
49
|
+
* import { createCheck } from '@orkestrel/reason'
|
|
50
|
+
*
|
|
51
|
+
* const authored = createCheck('age', 'above', 18)
|
|
52
|
+
* checkToPremise(authored, { field: 'age', actual: 25, met: true })
|
|
53
|
+
* // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
56
|
+
export declare function checkToPremise(check: Check, result: CheckResult, labels?: Readonly<Record<string, string>>): Premise;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns the most severe {@link Eligibility} in a list.
|
|
39
60
|
*
|
|
40
61
|
* @param eligibilities - The eligibilities to combine
|
|
41
62
|
* @returns The most severe eligibility, or `'eligible'` for an empty list
|
|
@@ -47,15 +68,41 @@ import { Subject } from '@orkestrel/reason';
|
|
|
47
68
|
* combineEligibilities(['eligible', 'referral']) // 'referral'
|
|
48
69
|
* ```
|
|
49
70
|
*/
|
|
50
|
-
export declare function combineEligibilities(eligibilities: Eligibility[]): Eligibility;
|
|
71
|
+
export declare function combineEligibilities(eligibilities: readonly Eligibility[]): Eligibility;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Creates a {@link QualificationDefinition}.
|
|
75
|
+
*
|
|
76
|
+
* @remarks
|
|
77
|
+
* Returns a fresh top-level definition, omitting absent optional keys;
|
|
78
|
+
* `passes` and `rulings` arrays are copied, and record `metadata` is
|
|
79
|
+
* shallow-copied so nested values are not deep-cloned.
|
|
80
|
+
*
|
|
81
|
+
* @param id - The definition id
|
|
82
|
+
* @param name - The display name
|
|
83
|
+
* @param passes - The ordered qualification passes
|
|
84
|
+
* @param input - Optional description, rulings, and metadata
|
|
85
|
+
* @returns A fresh qualification definition
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```ts
|
|
89
|
+
* import { createQualificationDefinition } from '@orkestrel/qualifier'
|
|
90
|
+
*
|
|
91
|
+
* createQualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
|
|
92
|
+
* ```
|
|
93
|
+
*/
|
|
94
|
+
export declare function createQualificationDefinition(id: string, name: string, passes: readonly QualificationPass[], input?: QualificationInput): QualificationDefinition;
|
|
51
95
|
|
|
52
96
|
/**
|
|
53
|
-
*
|
|
97
|
+
* Creates one qualifier over a reason engine.
|
|
54
98
|
*
|
|
55
99
|
* @remarks
|
|
56
100
|
* A standalone qualifier creates and OWNS one shared quantitative-plus-logical
|
|
57
101
|
* reason engine, destroying it on `destroy()`. An injected `options.engine`
|
|
58
|
-
* remains caller-owned and is never destroyed.
|
|
102
|
+
* remains caller-owned and is never destroyed. `validate` runs semantic
|
|
103
|
+
* validation before every qualification. Default: `true`. `labels` overrides the
|
|
104
|
+
* display name of a premise field, keyed by the dot-joined field path
|
|
105
|
+
* `formatField` produces, and takes precedence over a premise's own `label`.
|
|
59
106
|
*
|
|
60
107
|
* @param options - Optional injected engine, validation, labels, and emitter hooks
|
|
61
108
|
* @returns A {@link QualifierInterface}
|
|
@@ -71,10 +118,29 @@ import { Subject } from '@orkestrel/reason';
|
|
|
71
118
|
*/
|
|
72
119
|
export declare function createQualifier(options?: QualifierOptions): QualifierInterface;
|
|
73
120
|
|
|
74
|
-
/**
|
|
121
|
+
/**
|
|
122
|
+
* Creates a {@link Ruling} — one authored consequence for one rule in one pass.
|
|
123
|
+
*
|
|
124
|
+
* @param id - The ruling id
|
|
125
|
+
* @param pass - The logical pass id the rule lives in
|
|
126
|
+
* @param rule - The rule id whose firing this ruling reacts to
|
|
127
|
+
* @param effect - The eligibility impact when the rule fires
|
|
128
|
+
* @param input - Optional selection scope and message template
|
|
129
|
+
* @returns A fresh ruling
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* import { createRuling } from '@orkestrel/qualifier'
|
|
134
|
+
*
|
|
135
|
+
* createRuling('license', 'gates', 'licensed', 'restriction', { message: 'A license is required' })
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare function createRuling(id: string, pass: string, rule: string, effect: QualificationEffect, input?: RulingInput): Ruling;
|
|
139
|
+
|
|
140
|
+
/** Holds the default definition validation policy for `createQualifier` / `Qualifier.qualify`. */
|
|
75
141
|
export declare const DEFAULT_QUALIFIER_VALIDATE = true;
|
|
76
142
|
|
|
77
|
-
/**
|
|
143
|
+
/** Represents one quantitative pass's audit result. */
|
|
78
144
|
export declare interface Derivation {
|
|
79
145
|
readonly id: string;
|
|
80
146
|
readonly value: number;
|
|
@@ -84,7 +150,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
84
150
|
}
|
|
85
151
|
|
|
86
152
|
/**
|
|
87
|
-
*
|
|
153
|
+
* Derives global eligibility from applied, unscoped findings.
|
|
88
154
|
*
|
|
89
155
|
* @remarks
|
|
90
156
|
* Only findings that both applied and carry no `scope` shape global eligibility.
|
|
@@ -92,7 +158,8 @@ import { Subject } from '@orkestrel/reason';
|
|
|
92
158
|
* `failed`.
|
|
93
159
|
*
|
|
94
160
|
* @param findings - The resolved findings
|
|
95
|
-
* @param failed -
|
|
161
|
+
* @param failed - If `true`, adds a synthetic `referral` impact so an operational failure
|
|
162
|
+
* fails closed; if `false`, derives eligibility from the findings alone. Default: `false`
|
|
96
163
|
* @returns The most severe global eligibility
|
|
97
164
|
*
|
|
98
165
|
* @example
|
|
@@ -103,10 +170,10 @@ import { Subject } from '@orkestrel/reason';
|
|
|
103
170
|
* deriveFindingEligibility([finding]) // 'ineligible'
|
|
104
171
|
* ```
|
|
105
172
|
*/
|
|
106
|
-
export declare function deriveFindingEligibility(findings: Finding[], failed?: boolean): Eligibility;
|
|
173
|
+
export declare function deriveFindingEligibility(findings: readonly Finding[], failed?: boolean): Eligibility;
|
|
107
174
|
|
|
108
175
|
/**
|
|
109
|
-
*
|
|
176
|
+
* Derives one eligibility per finding scope.
|
|
110
177
|
*
|
|
111
178
|
* @remarks
|
|
112
179
|
* Only applied, scoped findings contribute. Each named scope combines its own
|
|
@@ -124,109 +191,97 @@ import { Subject } from '@orkestrel/reason';
|
|
|
124
191
|
* deriveScopeEligibilities([finding]) // { wind: 'ineligible' }
|
|
125
192
|
* ```
|
|
126
193
|
*/
|
|
127
|
-
export declare function deriveScopeEligibilities(findings: Finding[]): Readonly<Record<string, Eligibility>>;
|
|
194
|
+
export declare function deriveScopeEligibilities(findings: readonly Finding[]): Readonly<Record<string, Eligibility>>;
|
|
128
195
|
|
|
129
196
|
/**
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
* @param comparison - The comparison to describe
|
|
133
|
-
* @returns A display-neutral phrase
|
|
134
|
-
*
|
|
135
|
-
* @example
|
|
136
|
-
* ```ts
|
|
137
|
-
* import { describeComparison } from '@orkestrel/qualifier'
|
|
138
|
-
*
|
|
139
|
-
* describeComparison('above') // 'is more than'
|
|
140
|
-
* ```
|
|
141
|
-
*/
|
|
142
|
-
export declare function describeComparison(comparison: NonNullable<Premise['comparison']>): string;
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Render one {@link Premise} into a display-neutral sentence.
|
|
197
|
+
* Describes each logical pass carrying no rulings.
|
|
146
198
|
*
|
|
147
199
|
* @remarks
|
|
148
|
-
*
|
|
149
|
-
*
|
|
150
|
-
*
|
|
200
|
+
* A logical pass with no matching ruling can never contribute a finding, so its
|
|
201
|
+
* rules only ever influence later passes' reads — this is very likely an authoring
|
|
202
|
+
* oversight.
|
|
151
203
|
*
|
|
152
|
-
* @param
|
|
153
|
-
* @
|
|
154
|
-
* @returns A display-neutral sentence
|
|
204
|
+
* @param definition - The qualification definition to check
|
|
205
|
+
* @returns A fresh list of warning messages
|
|
155
206
|
*
|
|
156
207
|
* @example
|
|
157
208
|
* ```ts
|
|
158
|
-
* import {
|
|
209
|
+
* import { createQualificationDefinition, describeEmptyLogicalPasses } from '@orkestrel/qualifier'
|
|
210
|
+
* import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
159
211
|
*
|
|
160
|
-
*
|
|
161
|
-
*
|
|
212
|
+
* const gates = createLogicalDefinition('gates', 'Gates', [
|
|
213
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
214
|
+
* ])
|
|
215
|
+
* const definition = createQualificationDefinition('standard', 'Standard', [gates])
|
|
162
216
|
*
|
|
163
|
-
*
|
|
164
|
-
* // 'Applicant is enrolled → met'
|
|
217
|
+
* describeEmptyLogicalPasses(definition) // ["Logical pass 'gates' has no rulings"]
|
|
165
218
|
* ```
|
|
166
219
|
*/
|
|
167
|
-
export declare function
|
|
220
|
+
export declare function describeEmptyLogicalPasses(definition: QualificationDefinition): readonly string[];
|
|
168
221
|
|
|
169
222
|
/**
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* @remarks
|
|
173
|
-
* An array renders as its elements, each `String`-coerced, joined with `', '`.
|
|
174
|
-
* A reason `Bounds`-shaped record (the `between` / `outside` expected value)
|
|
175
|
-
* renders its present sides joined with the word `and`; an absent side is
|
|
176
|
-
* omitted. A finite number and everything else fall back to plain `String()` —
|
|
177
|
-
* a finite number is NOT grouped here (grouping is {@link interpolateMessage}'s
|
|
178
|
-
* message-token-only behavior).
|
|
223
|
+
* Describes each ruling whose pass or rule does not exist or whose pass is not
|
|
224
|
+
* logical, and each pass id shadowing the reserved {@link QUALIFICATION_KEY}.
|
|
179
225
|
*
|
|
180
|
-
* @param
|
|
181
|
-
* @returns A
|
|
226
|
+
* @param definition - The qualification definition to check
|
|
227
|
+
* @returns A fresh list of reference-error messages
|
|
182
228
|
*
|
|
183
229
|
* @example
|
|
184
230
|
* ```ts
|
|
185
|
-
* import {
|
|
231
|
+
* import { createQualificationDefinition, createRuling, describeMissingReferences } from '@orkestrel/qualifier'
|
|
232
|
+
* import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
233
|
+
*
|
|
234
|
+
* const gates = createLogicalDefinition('gates', 'Gates', [
|
|
235
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
236
|
+
* ])
|
|
237
|
+
* const definition = createQualificationDefinition('standard', 'Standard', [gates], {
|
|
238
|
+
* rulings: [createRuling('license', 'gates', 'absent', 'restriction')],
|
|
239
|
+
* })
|
|
186
240
|
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
189
|
-
* describeValue(42) // '42'
|
|
241
|
+
* describeMissingReferences(definition)
|
|
242
|
+
* // ["Ruling 'license' references missing rule 'absent' in pass 'gates'"]
|
|
190
243
|
* ```
|
|
191
244
|
*/
|
|
192
|
-
export declare function
|
|
193
|
-
|
|
194
|
-
/** The eligibility impact of each {@link QualificationEffect}; `condition` remains eligible. */
|
|
195
|
-
export declare const EFFECT_ELIGIBILITIES: Readonly<Record<QualificationEffect, Eligibility>>;
|
|
196
|
-
|
|
197
|
-
/** The eligibility outcome axis. */
|
|
198
|
-
export declare type Eligibility = 'eligible' | 'ineligible' | 'referral';
|
|
199
|
-
|
|
200
|
-
/** Eligibility severity order — most to least severe. */
|
|
201
|
-
export declare const ELIGIBILITY_PRECEDENCE: readonly Eligibility[];
|
|
245
|
+
export declare function describeMissingReferences(definition: QualificationDefinition): readonly string[];
|
|
202
246
|
|
|
203
247
|
/**
|
|
204
|
-
*
|
|
248
|
+
* Describes each quantitative pass never read by a later pass.
|
|
205
249
|
*
|
|
206
250
|
* @remarks
|
|
207
|
-
* A
|
|
208
|
-
*
|
|
209
|
-
*
|
|
251
|
+
* A quantitative pass's projected value lives under `qualification.<id>` — if no
|
|
252
|
+
* later logical premise or quantitative factor field reads that key, the
|
|
253
|
+
* derivation is dead weight. Reads are collected per later pass: a logical pass's
|
|
254
|
+
* premise atoms (conclusion atoms are writes, not reads), and a quantitative
|
|
255
|
+
* pass's field/lookup/range factor sources plus factor checks.
|
|
210
256
|
*
|
|
211
257
|
* @param definition - The qualification definition to check
|
|
212
258
|
* @returns A fresh list of warning messages
|
|
213
259
|
*
|
|
214
260
|
* @example
|
|
215
261
|
* ```ts
|
|
216
|
-
* import {
|
|
217
|
-
* import {
|
|
262
|
+
* import { createQualificationDefinition, describeUnreadDerivations } from '@orkestrel/qualifier'
|
|
263
|
+
* import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
|
|
218
264
|
*
|
|
219
|
-
* const
|
|
220
|
-
*
|
|
265
|
+
* const cap = createQuantitativeDefinition('cap', 'TIV cap', [
|
|
266
|
+
* createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
|
|
221
267
|
* ])
|
|
222
|
-
* const definition =
|
|
268
|
+
* const definition = createQualificationDefinition('standard', 'Standard', [cap])
|
|
223
269
|
*
|
|
224
|
-
*
|
|
270
|
+
* describeUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
|
|
225
271
|
* ```
|
|
226
272
|
*/
|
|
227
|
-
export declare function
|
|
273
|
+
export declare function describeUnreadDerivations(definition: QualificationDefinition): readonly string[];
|
|
274
|
+
|
|
275
|
+
/** Maps each {@link QualificationEffect} to its eligibility impact; `condition` remains eligible. */
|
|
276
|
+
export declare const EFFECT_ELIGIBILITIES: Readonly<Record<QualificationEffect, Eligibility>>;
|
|
277
|
+
|
|
278
|
+
/** Represents the eligibility outcome axis. */
|
|
279
|
+
export declare type Eligibility = 'eligible' | 'ineligible' | 'referral';
|
|
280
|
+
|
|
281
|
+
/** Lists the eligibility severities in order — most to least severe. */
|
|
282
|
+
export declare const ELIGIBILITY_PRECEDENCE: readonly Eligibility[];
|
|
228
283
|
|
|
229
|
-
/**
|
|
284
|
+
/** Represents one resolved ruling. */
|
|
230
285
|
export declare interface Finding {
|
|
231
286
|
readonly id: string;
|
|
232
287
|
readonly pass: string;
|
|
@@ -239,32 +294,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
239
294
|
}
|
|
240
295
|
|
|
241
296
|
/**
|
|
242
|
-
*
|
|
243
|
-
* a pass id shadowing the reserved {@link QUALIFICATION_KEY}.
|
|
244
|
-
*
|
|
245
|
-
* @param definition - The qualification definition to check
|
|
246
|
-
* @returns A fresh list of reference-error messages
|
|
247
|
-
*
|
|
248
|
-
* @example
|
|
249
|
-
* ```ts
|
|
250
|
-
* import { findMissingReferences, qualificationDefinition, rulingDefinition } from '@orkestrel/qualifier'
|
|
251
|
-
* import { atom, logicalDefinition, rule } from '@orkestrel/reason'
|
|
252
|
-
*
|
|
253
|
-
* const gates = logicalDefinition('gates', 'Gates', [
|
|
254
|
-
* rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
|
|
255
|
-
* ])
|
|
256
|
-
* const definition = qualificationDefinition('standard', 'Standard', [gates], {
|
|
257
|
-
* rulings: [rulingDefinition('license', 'gates', 'absent', 'restriction')],
|
|
258
|
-
* })
|
|
259
|
-
*
|
|
260
|
-
* findMissingReferences(definition)
|
|
261
|
-
* // ["Ruling 'license' references missing rule 'absent' in pass 'gates'"]
|
|
262
|
-
* ```
|
|
263
|
-
*/
|
|
264
|
-
export declare function findMissingReferences(definition: QualificationDefinition): readonly string[];
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* Locate an authored {@link Rule} by id.
|
|
297
|
+
* Locates an authored {@link Rule} by id.
|
|
268
298
|
*
|
|
269
299
|
* @param definition - The logical definition to search
|
|
270
300
|
* @param id - The rule id
|
|
@@ -273,10 +303,10 @@ import { Subject } from '@orkestrel/reason';
|
|
|
273
303
|
* @example
|
|
274
304
|
* ```ts
|
|
275
305
|
* import { findRule } from '@orkestrel/qualifier'
|
|
276
|
-
* import {
|
|
306
|
+
* import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
277
307
|
*
|
|
278
|
-
* const gates =
|
|
279
|
-
*
|
|
308
|
+
* const gates = createLogicalDefinition('gates', 'Gates', [
|
|
309
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
280
310
|
* ])
|
|
281
311
|
*
|
|
282
312
|
* findRule(gates, 'licensed')?.id // 'licensed'
|
|
@@ -285,38 +315,10 @@ import { Subject } from '@orkestrel/reason';
|
|
|
285
315
|
export declare function findRule(definition: LogicalDefinition, id: string): Rule | undefined;
|
|
286
316
|
|
|
287
317
|
/**
|
|
288
|
-
*
|
|
289
|
-
*
|
|
290
|
-
* @remarks
|
|
291
|
-
* A quantitative pass's projected value lives under `qualification.<id>` — if no
|
|
292
|
-
* later logical premise or quantitative factor field reads that key, the
|
|
293
|
-
* derivation is dead weight. Reads are collected per later pass: a logical pass's
|
|
294
|
-
* premise atoms (conclusion atoms are writes, not reads), and a quantitative
|
|
295
|
-
* pass's field/lookup/range factor sources plus factor checks.
|
|
296
|
-
*
|
|
297
|
-
* @param definition - The qualification definition to check
|
|
298
|
-
* @returns A fresh list of warning messages
|
|
299
|
-
*
|
|
300
|
-
* @example
|
|
301
|
-
* ```ts
|
|
302
|
-
* import { findUnreadDerivations, qualificationDefinition } from '@orkestrel/qualifier'
|
|
303
|
-
* import { factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
|
|
304
|
-
*
|
|
305
|
-
* const cap = quantitativeDefinition('cap', 'TIV cap', [
|
|
306
|
-
* factorGroup('limit', 'sum', [staticFactor('base', 500_000)]),
|
|
307
|
-
* ])
|
|
308
|
-
* const definition = qualificationDefinition('standard', 'Standard', [cap])
|
|
309
|
-
*
|
|
310
|
-
* findUnreadDerivations(definition) // ["Quantitative pass 'cap' is never read by a later pass"]
|
|
311
|
-
* ```
|
|
312
|
-
*/
|
|
313
|
-
export declare function findUnreadDerivations(definition: QualificationDefinition): readonly string[];
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Determine whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
|
|
318
|
+
* Determines whether a subject already owns the reserved {@link QUALIFICATION_KEY}.
|
|
317
319
|
*
|
|
318
320
|
* @param subject - The subject to check
|
|
319
|
-
* @returns
|
|
321
|
+
* @returns True if the subject owns the reserved key; false otherwise
|
|
320
322
|
*
|
|
321
323
|
* @example
|
|
322
324
|
* ```ts
|
|
@@ -328,7 +330,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
328
330
|
export declare function hasReservedKey(subject: Subject): boolean;
|
|
329
331
|
|
|
330
332
|
/**
|
|
331
|
-
*
|
|
333
|
+
* Interpolates `{{dotted.path}}` tokens in a message template against a subject.
|
|
332
334
|
*
|
|
333
335
|
* @remarks
|
|
334
336
|
* Each token is split on `.` into a `FieldPath` array and resolved with
|
|
@@ -352,46 +354,52 @@ import { Subject } from '@orkestrel/reason';
|
|
|
352
354
|
export declare function interpolateMessage(template: string, subject: Readonly<Record<string, unknown>>): string;
|
|
353
355
|
|
|
354
356
|
/**
|
|
355
|
-
*
|
|
357
|
+
* Determines whether a value is an open result-side {@link Derivation}.
|
|
356
358
|
*
|
|
357
359
|
* @remarks
|
|
358
360
|
* Unknown members are admitted. `value` follows the published `number` type, including `NaN` and
|
|
359
361
|
* infinities.
|
|
360
362
|
*
|
|
361
363
|
* @param value - The value to test
|
|
362
|
-
* @returns
|
|
364
|
+
* @returns True if every published derivation member is valid; false otherwise
|
|
363
365
|
*/
|
|
364
366
|
export declare function isDerivation(value: unknown): value is Derivation;
|
|
365
367
|
|
|
366
|
-
/**
|
|
368
|
+
/**
|
|
369
|
+
* Determines whether a value is an {@link Eligibility} literal.
|
|
370
|
+
*
|
|
371
|
+
* @param value - The value to test
|
|
372
|
+
* @returns True if `value` is one of the eligibility literals; false otherwise
|
|
373
|
+
*/
|
|
367
374
|
export declare const isEligibility: Guard<"eligible" | "ineligible" | "referral">;
|
|
368
375
|
|
|
369
376
|
/**
|
|
370
|
-
*
|
|
377
|
+
* Determines whether a value is an open string-keyed record of {@link Eligibility} values.
|
|
371
378
|
*
|
|
372
379
|
* @remarks
|
|
373
380
|
* Every own string-named property is checked, including non-enumerable properties. Inherited and
|
|
374
381
|
* symbol-named members are left unchecked because they are outside the record this guard certifies.
|
|
375
382
|
*
|
|
376
383
|
* @param value - The value to test
|
|
377
|
-
* @returns
|
|
384
|
+
* @returns True if `value` is a non-array object whose own string-named values are
|
|
385
|
+
* eligibilities; false otherwise
|
|
378
386
|
*/
|
|
379
387
|
export declare function isEligibilityRecord(value: unknown): value is Readonly<Record<string, Eligibility>>;
|
|
380
388
|
|
|
381
389
|
/**
|
|
382
|
-
*
|
|
390
|
+
* Determines whether a value is an open result-side {@link Finding}.
|
|
383
391
|
*
|
|
384
392
|
* @remarks
|
|
385
393
|
* Unknown members are admitted. Optional `scope` and `message` members may be absent or
|
|
386
394
|
* `undefined`; a present defined value must be a string.
|
|
387
395
|
*
|
|
388
396
|
* @param value - The value to test
|
|
389
|
-
* @returns
|
|
397
|
+
* @returns True if every published finding member is valid; false otherwise
|
|
390
398
|
*/
|
|
391
399
|
export declare function isFinding(value: unknown): value is Finding;
|
|
392
400
|
|
|
393
401
|
/**
|
|
394
|
-
*
|
|
402
|
+
* Determines whether a value is an open result-side {@link Premise}.
|
|
395
403
|
*
|
|
396
404
|
* @remarks
|
|
397
405
|
* `expected` is unchecked and may be absent or contain any value because its published type is
|
|
@@ -399,91 +407,69 @@ import { Subject } from '@orkestrel/reason';
|
|
|
399
407
|
* Unknown members are admitted.
|
|
400
408
|
*
|
|
401
409
|
* @param value - The value to test
|
|
402
|
-
* @returns
|
|
410
|
+
* @returns True if every checked premise member follows its published type; false otherwise
|
|
403
411
|
*/
|
|
404
412
|
export declare function isPremise(value: unknown): value is Premise;
|
|
405
413
|
|
|
406
|
-
/**
|
|
414
|
+
/**
|
|
415
|
+
* Determines whether a value is an exact {@link QualificationDefinition} record.
|
|
416
|
+
*
|
|
417
|
+
* @param value - The value to test
|
|
418
|
+
* @returns True if `value` carries every definition member and no unknown key; false otherwise
|
|
419
|
+
*/
|
|
407
420
|
export declare function isQualificationDefinition(value: unknown): value is QualificationDefinition;
|
|
408
421
|
|
|
409
|
-
/**
|
|
422
|
+
/**
|
|
423
|
+
* Determines whether a value is a {@link QualificationEffect} literal.
|
|
424
|
+
*
|
|
425
|
+
* @param value - The value to test
|
|
426
|
+
* @returns True if `value` is one of the effect literals; false otherwise
|
|
427
|
+
*/
|
|
410
428
|
export declare const isQualificationEffect: Guard<"referral" | "restriction" | "condition">;
|
|
411
429
|
|
|
412
|
-
/**
|
|
430
|
+
/**
|
|
431
|
+
* Determines whether a value is a {@link QualificationPass} (a quantitative or logical definition).
|
|
432
|
+
*
|
|
433
|
+
* @param value - The value to test
|
|
434
|
+
* @returns True if `value` is a complete reason quantitative or logical definition; false otherwise
|
|
435
|
+
*/
|
|
413
436
|
export declare function isQualificationPass(value: unknown): value is QualificationPass;
|
|
414
437
|
|
|
415
438
|
/**
|
|
416
|
-
*
|
|
439
|
+
* Determines whether a value is an open {@link QualificationResult} returned by a qualifier.
|
|
417
440
|
*
|
|
418
441
|
* @remarks
|
|
419
442
|
* This guard is result-postured for values returned through a borrowed qualifier interface. It
|
|
420
443
|
* admits unknown members and class instances while checking the complete published result closure.
|
|
421
444
|
*
|
|
422
445
|
* @param value - The value to test
|
|
423
|
-
* @returns
|
|
446
|
+
* @returns True if every published qualification-result member is valid; false otherwise
|
|
424
447
|
*/
|
|
425
448
|
export declare function isQualificationResult(value: unknown): value is QualificationResult;
|
|
426
449
|
|
|
427
450
|
/**
|
|
428
|
-
*
|
|
429
|
-
*
|
|
430
|
-
* @remarks
|
|
431
|
-
* `QualificationValidationResult` is an alias of reasons' validation-result type, so this IS
|
|
432
|
-
* reasons' published open result guard by delegation — the alias tracks reasons' shape
|
|
433
|
-
* automatically, and a hand-written member list here would silently stop checking a member a
|
|
434
|
-
* later reason release adds.
|
|
451
|
+
* Narrows a caught value to a {@link QualifierError}.
|
|
435
452
|
*
|
|
436
|
-
* @param value - The value to test
|
|
437
|
-
* @returns `
|
|
453
|
+
* @param value - The caught value to test
|
|
454
|
+
* @returns True if `value` is a {@link QualifierError} instance; false otherwise
|
|
438
455
|
*/
|
|
439
|
-
export declare const isQualificationValidationResult: Guard<QualificationValidationResult>;
|
|
440
|
-
|
|
441
|
-
/** Narrow a caught value to a {@link QualifierError}. */
|
|
442
456
|
export declare function isQualifierError(value: unknown): value is QualifierError;
|
|
443
457
|
|
|
444
|
-
/** Determine whether a value is an exact {@link Ruling} record. */
|
|
445
|
-
export declare function isRuling(value: unknown): value is Ruling;
|
|
446
|
-
|
|
447
458
|
/**
|
|
448
|
-
*
|
|
449
|
-
* and re-evaluating each against the working subject.
|
|
459
|
+
* Determines whether a value is an exact {@link Ruling} record.
|
|
450
460
|
*
|
|
451
|
-
* @
|
|
452
|
-
*
|
|
453
|
-
* premise-audit projection: each authored premise expression is flattened to its
|
|
454
|
-
* atom leaves via `extractAtoms`, and each leaf's `Check` is re-evaluated through
|
|
455
|
-
* the injected `evaluator`. A membership check (`any` / `none`) over an EMPTY
|
|
456
|
-
* array value is content-free and is skipped.
|
|
457
|
-
*
|
|
458
|
-
* @param rule - The authored rule
|
|
459
|
-
* @param working - The working subject to evaluate against
|
|
460
|
-
* @param evaluator - The shared reason check evaluator
|
|
461
|
-
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
462
|
-
* @returns A fresh, ordered list of rich premises
|
|
463
|
-
*
|
|
464
|
-
* @example
|
|
465
|
-
* ```ts
|
|
466
|
-
* import { logicalPremises } from '@orkestrel/qualifier'
|
|
467
|
-
* import { atom, createEvaluator, logicalDefinition, rule } from '@orkestrel/reason'
|
|
468
|
-
*
|
|
469
|
-
* const gates = logicalDefinition('gates', 'Gates', [
|
|
470
|
-
* rule('licensed', [atom('licensed', 'equals', false)], atom('blocked', 'equals', true)),
|
|
471
|
-
* ])
|
|
472
|
-
* const evaluator = createEvaluator()
|
|
473
|
-
* const premises = logicalPremises(gates.rules[0], { licensed: false }, evaluator)
|
|
474
|
-
*
|
|
475
|
-
* premises[0]?.met // true
|
|
476
|
-
* ```
|
|
461
|
+
* @param value - The value to test
|
|
462
|
+
* @returns True if `value` carries every ruling member and no unknown key; false otherwise
|
|
477
463
|
*/
|
|
478
|
-
export declare function
|
|
464
|
+
export declare function isRuling(value: unknown): value is Ruling;
|
|
479
465
|
|
|
480
466
|
/**
|
|
481
|
-
*
|
|
467
|
+
* Maps an engine throw caught while running one pass to a typed {@link QualifierError}.
|
|
482
468
|
*
|
|
483
469
|
* @remarks
|
|
484
470
|
* A reason `ReasonError('INVALID')` maps to `DEFINITION` (the engine rejected the
|
|
485
471
|
* pass as an invalid definition); `ReasonError('DESTROYED')` maps to `DESTROYED`;
|
|
486
|
-
* every other `ReasonError` code (
|
|
472
|
+
* every other `ReasonError` code (for example `MISSING`) and every non-`ReasonError` throw
|
|
487
473
|
* (including a `bail: true` reasoner rethrow) maps to `ENGINE`. The original throw
|
|
488
474
|
* is preserved as `context.cause`.
|
|
489
475
|
*
|
|
@@ -502,7 +488,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
502
488
|
export declare function mapEngineError(error: unknown, pass: string): QualifierError;
|
|
503
489
|
|
|
504
490
|
/**
|
|
505
|
-
*
|
|
491
|
+
* Merges one pass projection into the context, copy-on-write.
|
|
506
492
|
*
|
|
507
493
|
* @param context - The current projection context
|
|
508
494
|
* @param id - The pass id to project under
|
|
@@ -520,7 +506,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
520
506
|
export declare function mergeQualificationContext(context: QualificationContext, id: string, projection: QualificationProjection): QualificationContext;
|
|
521
507
|
|
|
522
508
|
/**
|
|
523
|
-
*
|
|
509
|
+
* Represents display-neutral evidence for one condition, in one of two authoring modes.
|
|
524
510
|
* A CHECKED premise carries `field` and `comparison`; a DESCRIBED premise
|
|
525
511
|
* carries neither and renders from `description` instead. The checked form
|
|
526
512
|
* renders only when `field` and `comparison` are BOTH present, and
|
|
@@ -538,34 +524,13 @@ import { Subject } from '@orkestrel/reason';
|
|
|
538
524
|
readonly met?: boolean;
|
|
539
525
|
}
|
|
540
526
|
|
|
541
|
-
/**
|
|
542
|
-
* Build a {@link Premise} from an authored {@link Check} and its evaluated
|
|
543
|
-
* `CheckResult`.
|
|
544
|
-
*
|
|
545
|
-
* @param check - The authored check
|
|
546
|
-
* @param result - The evaluated check result carrying the resolved `actual` and `met`
|
|
547
|
-
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
548
|
-
* @returns A fresh premise
|
|
549
|
-
*
|
|
550
|
-
* @example
|
|
551
|
-
* ```ts
|
|
552
|
-
* import { premiseCheck } from '@orkestrel/qualifier'
|
|
553
|
-
* import { check } from '@orkestrel/reason'
|
|
554
|
-
*
|
|
555
|
-
* const authored = check('age', 'above', 18)
|
|
556
|
-
* premiseCheck(authored, { field: 'age', actual: 25, met: true })
|
|
557
|
-
* // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
|
|
558
|
-
* ```
|
|
559
|
-
*/
|
|
560
|
-
export declare function premiseCheck(check: Check, result: CheckResult, labels?: Readonly<Record<string, string>>): Premise;
|
|
561
|
-
|
|
562
|
-
/** The reserved internal projection namespace a pass's working projection is written under. */
|
|
527
|
+
/** Names the reserved internal projection namespace a pass's working projection is written under. */
|
|
563
528
|
export declare const QUALIFICATION_KEY = "qualification";
|
|
564
529
|
|
|
565
|
-
/**
|
|
530
|
+
/** Represents the internal projection record stored under `QUALIFICATION_KEY`. */
|
|
566
531
|
export declare type QualificationContext = Readonly<Record<string, QualificationProjection>>;
|
|
567
532
|
|
|
568
|
-
/**
|
|
533
|
+
/** Represents a pure authored qualification definition. */
|
|
569
534
|
export declare interface QualificationDefinition {
|
|
570
535
|
readonly id: string;
|
|
571
536
|
readonly name: string;
|
|
@@ -575,46 +540,23 @@ import { Subject } from '@orkestrel/reason';
|
|
|
575
540
|
readonly metadata?: Readonly<Record<string, JSONValue>>;
|
|
576
541
|
}
|
|
577
542
|
|
|
578
|
-
/**
|
|
579
|
-
* Build a {@link QualificationDefinition}.
|
|
580
|
-
*
|
|
581
|
-
* @remarks
|
|
582
|
-
* Returns a fresh top-level definition, omitting absent optional keys;
|
|
583
|
-
* `passes` and `rulings` arrays are copied, and record `metadata` is
|
|
584
|
-
* shallow-copied so nested values are not deep-cloned.
|
|
585
|
-
*
|
|
586
|
-
* @param id - The definition id
|
|
587
|
-
* @param name - The display name
|
|
588
|
-
* @param passes - The ordered qualification passes
|
|
589
|
-
* @param input - Optional description, rulings, and metadata
|
|
590
|
-
* @returns A fresh qualification definition
|
|
591
|
-
*
|
|
592
|
-
* @example
|
|
593
|
-
* ```ts
|
|
594
|
-
* import { qualificationDefinition } from '@orkestrel/qualifier'
|
|
595
|
-
*
|
|
596
|
-
* qualificationDefinition('standard', 'Standard', [gates], { rulings: [ruling] })
|
|
597
|
-
* ```
|
|
598
|
-
*/
|
|
599
|
-
export declare function qualificationDefinition(id: string, name: string, passes: QualificationPass[], input?: QualificationInput): QualificationDefinition;
|
|
600
|
-
|
|
601
|
-
/** An authored ruling's eligibility impact. */
|
|
543
|
+
/** Represents an authored ruling's eligibility impact. */
|
|
602
544
|
export declare type QualificationEffect = 'restriction' | 'referral' | 'condition';
|
|
603
545
|
|
|
604
|
-
/**
|
|
546
|
+
/** Carries the optional fields `createQualificationDefinition` accepts. */
|
|
605
547
|
export declare interface QualificationInput {
|
|
606
548
|
readonly description?: string;
|
|
607
549
|
readonly rulings?: readonly Ruling[];
|
|
608
550
|
readonly metadata?: Readonly<Record<string, JSONValue>>;
|
|
609
551
|
}
|
|
610
552
|
|
|
611
|
-
/**
|
|
553
|
+
/** Represents one ordered derivation or rule pass. */
|
|
612
554
|
export declare type QualificationPass = QuantitativeDefinition | LogicalDefinition;
|
|
613
555
|
|
|
614
|
-
/**
|
|
556
|
+
/** Represents one pass's internal working projection. */
|
|
615
557
|
export declare type QualificationProjection = number | boolean | Readonly<Record<string, unknown>>;
|
|
616
558
|
|
|
617
|
-
/**
|
|
559
|
+
/** Represents one subject's complete qualification outcome. */
|
|
618
560
|
export declare interface QualificationResult {
|
|
619
561
|
readonly id: string;
|
|
620
562
|
readonly name: string;
|
|
@@ -628,7 +570,7 @@ import { Subject } from '@orkestrel/reason';
|
|
|
628
570
|
}
|
|
629
571
|
|
|
630
572
|
/**
|
|
631
|
-
*
|
|
573
|
+
* Wraps a {@link QualificationContext} under {@link QUALIFICATION_KEY}.
|
|
632
574
|
*
|
|
633
575
|
* @param context - The accumulated projection context
|
|
634
576
|
* @returns A record carrying the context under the reserved key
|
|
@@ -642,12 +584,8 @@ import { Subject } from '@orkestrel/reason';
|
|
|
642
584
|
*/
|
|
643
585
|
export declare function qualificationToRecord(context: QualificationContext): Readonly<Record<string, QualificationContext>>;
|
|
644
586
|
|
|
645
|
-
/** Semantic definition validation. */
|
|
646
|
-
export declare type QualificationValidationResult = ReasonValidationResult;
|
|
647
|
-
|
|
648
587
|
/**
|
|
649
|
-
*
|
|
650
|
-
* eligibility.
|
|
588
|
+
* Runs ordered passes over one reason engine and returns eligibility.
|
|
651
589
|
*
|
|
652
590
|
* @remarks
|
|
653
591
|
* The engine is OWNED when self-created (destroyed on `destroy()`) and borrowed
|
|
@@ -658,177 +596,455 @@ import { Subject } from '@orkestrel/reason';
|
|
|
658
596
|
export declare class Qualifier implements QualifierInterface {
|
|
659
597
|
#private;
|
|
660
598
|
constructor(options?: QualifierOptions);
|
|
599
|
+
/**
|
|
600
|
+
* Holds the typed observation surface carrying `derive`, `finding`, `qualify`, and
|
|
601
|
+
* `destroy`.
|
|
602
|
+
*
|
|
603
|
+
* @returns The emitter this qualifier owns
|
|
604
|
+
*
|
|
605
|
+
* @example
|
|
606
|
+
* ```ts
|
|
607
|
+
* import { Qualifier } from '@orkestrel/qualifier'
|
|
608
|
+
*
|
|
609
|
+
* const qualifier = new Qualifier()
|
|
610
|
+
* qualifier.emitter.on('qualify', (result) => console.log(result.eligibility))
|
|
611
|
+
* qualifier.destroy()
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
661
614
|
get emitter(): EmitterInterface<QualifierEventMap>;
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
615
|
+
/**
|
|
616
|
+
* Qualifies one subject against one authored definition.
|
|
617
|
+
*
|
|
618
|
+
* @remarks
|
|
619
|
+
* Semantic validation runs first when the `validate` option is on, so an invalid
|
|
620
|
+
* definition throws before any pass runs. Passes run in authored order over a
|
|
621
|
+
* copy-on-write working subject, and the caller's `subject` is never mutated. An
|
|
622
|
+
* unscoped restriction stops the remaining passes because no later finding can be
|
|
623
|
+
* more severe.
|
|
624
|
+
*
|
|
625
|
+
* @param subject - The record to qualify
|
|
626
|
+
* @param definition - The authored qualification definition
|
|
627
|
+
* @returns A fresh, frozen qualification result
|
|
628
|
+
* @throws {@link QualifierError} `'DEFINITION'` when validation is enabled and the
|
|
629
|
+
* definition fails semantic validation, or when the engine rejects a pass as an invalid
|
|
630
|
+
* definition; `'MISMATCH'` when the subject is not a record or already carries the
|
|
631
|
+
* reserved `qualification` key; `'DESTROYED'` after `destroy`, or when the engine
|
|
632
|
+
* reports itself destroyed mid-pass; `'ENGINE'` for every other engine throw
|
|
633
|
+
*
|
|
634
|
+
* @example
|
|
635
|
+
* ```ts
|
|
636
|
+
* import { createQualificationDefinition, createRuling, Qualifier } from '@orkestrel/qualifier'
|
|
637
|
+
* import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
638
|
+
*
|
|
639
|
+
* const gates = createLogicalDefinition('gates', 'Eligibility gates', [
|
|
640
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
641
|
+
* ])
|
|
642
|
+
* const definition = createQualificationDefinition('standard', 'Standard eligibility', [gates], {
|
|
643
|
+
* rulings: [createRuling('license', 'gates', 'licensed', 'restriction')],
|
|
644
|
+
* })
|
|
645
|
+
*
|
|
646
|
+
* const qualifier = new Qualifier()
|
|
647
|
+
* qualifier.qualify({ id: 'risk-1', licensed: false }, definition).eligibility // 'ineligible'
|
|
648
|
+
* qualifier.destroy()
|
|
649
|
+
* ```
|
|
650
|
+
*/
|
|
651
|
+
qualify(subject: Subject, definition: QualificationDefinition): QualificationResult;
|
|
652
|
+
/**
|
|
653
|
+
* Validates one authored definition semantically, without running it.
|
|
654
|
+
*
|
|
655
|
+
* @remarks
|
|
656
|
+
* Structural shape is `isQualificationDefinition`'s job. This checks the meaning:
|
|
657
|
+
* non-empty id and name, valid and uniquely identified passes and rulings, every
|
|
658
|
+
* ruling reference resolving to a logical pass and one of its rules, and no pass id
|
|
659
|
+
* shadowing `QUALIFICATION_KEY`. An empty definition, a logical pass carrying no
|
|
660
|
+
* ruling, and a derivation no later pass reads are warnings rather than errors.
|
|
661
|
+
*
|
|
662
|
+
* @param definition - The authored qualification definition
|
|
663
|
+
* @returns A fresh validation result carrying `valid`, `errors`, and `warnings`
|
|
664
|
+
* @throws {@link QualifierError} `'DESTROYED'` after `destroy`
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```ts
|
|
668
|
+
* import { createQualificationDefinition, Qualifier } from '@orkestrel/qualifier'
|
|
669
|
+
*
|
|
670
|
+
* const qualifier = new Qualifier()
|
|
671
|
+
* qualifier.validate(createQualificationDefinition('empty', 'Empty', []))
|
|
672
|
+
* // { valid: true, errors: [], warnings: ['Definition has no passes'] }
|
|
673
|
+
* qualifier.destroy()
|
|
674
|
+
* ```
|
|
675
|
+
*/
|
|
676
|
+
validate(definition: QualificationDefinition): ReasonValidationResult;
|
|
677
|
+
/**
|
|
678
|
+
* Destroys this qualifier, idempotently.
|
|
679
|
+
*
|
|
680
|
+
* @remarks
|
|
681
|
+
* An engine this qualifier created is destroyed; an injected engine stays
|
|
682
|
+
* caller-owned and is left alone. The `destroy` event fires before the emitter
|
|
683
|
+
* itself is destroyed, and a later call returns without re-emitting.
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```ts
|
|
687
|
+
* import { Qualifier } from '@orkestrel/qualifier'
|
|
688
|
+
*
|
|
689
|
+
* const qualifier = new Qualifier()
|
|
690
|
+
* qualifier.destroy()
|
|
691
|
+
* qualifier.destroy() // idempotent
|
|
692
|
+
* ```
|
|
693
|
+
*/
|
|
694
|
+
destroy(): void;
|
|
695
|
+
}
|
|
666
696
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
697
|
+
/**
|
|
698
|
+
* Represents a coded programmer error thrown by the qualifier layer.
|
|
699
|
+
*
|
|
700
|
+
* @remarks
|
|
701
|
+
* `DEFINITION` — a qualification definition failed semantic validation; when
|
|
702
|
+
* `qualify`'s own validation raises it, `context.definition` names the
|
|
703
|
+
* definition, and when an engine throw maps to it, `context.pass` names the pass
|
|
704
|
+
* and `context.cause` preserves the original throw. `MISMATCH` — a subject is
|
|
705
|
+
* not a record or already carries the reserved `qualification` key.
|
|
706
|
+
* `DESTROYED` — use of a destroyed qualifier; an engine-mapped one carries
|
|
707
|
+
* `context.pass` and `context.cause`. `ENGINE` — the underlying reason engine
|
|
708
|
+
* threw while running a pass; `context.pass` names the pass and `context.cause`
|
|
709
|
+
* preserves the original throw. `context` is absent for a throw that carries no
|
|
710
|
+
* payload.
|
|
711
|
+
*/
|
|
712
|
+
export declare class QualifierError extends Error {
|
|
713
|
+
readonly code: QualifierErrorCode;
|
|
714
|
+
readonly context: QualifierErrorContext | undefined;
|
|
715
|
+
constructor(code: QualifierErrorCode, message: string, context?: QualifierErrorContext);
|
|
716
|
+
}
|
|
682
717
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
/** The push observation surface of a {@link QualifierInterface} (AGENTS §13). */
|
|
687
|
-
export declare type QualifierEventMap = {
|
|
688
|
-
readonly derive: readonly [derivation: Derivation];
|
|
689
|
-
readonly finding: readonly [finding: Finding];
|
|
690
|
-
readonly qualify: readonly [result: QualificationResult];
|
|
691
|
-
readonly destroy: readonly [];
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
/** One qualifier — owns or borrows one reason engine and returns eligibility. */
|
|
695
|
-
export declare interface QualifierInterface {
|
|
696
|
-
readonly emitter: EmitterInterface<QualifierEventMap>;
|
|
697
|
-
qualify(subject: Subject, definition: QualificationDefinition): QualificationResult;
|
|
698
|
-
validate(definition: QualificationDefinition): QualificationValidationResult;
|
|
699
|
-
destroy(): void;
|
|
700
|
-
}
|
|
718
|
+
/** Represents a coded {@link QualifierError} programmer-error code. */
|
|
719
|
+
export declare type QualifierErrorCode = 'DEFINITION' | 'MISMATCH' | 'DESTROYED' | 'ENGINE';
|
|
701
720
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
721
|
+
/**
|
|
722
|
+
* Represents the structured payload a {@link QualifierError} carries.
|
|
723
|
+
*
|
|
724
|
+
* @remarks
|
|
725
|
+
* `pass` names the pass that was running when the engine threw. `definition` names the
|
|
726
|
+
* qualification definition that failed semantic validation. `cause` is the original throw
|
|
727
|
+
* an engine failure wrapped, and stays `unknown` because a thrown value can be anything.
|
|
728
|
+
* Every member is absent rather than `undefined` when the throw does not carry it.
|
|
729
|
+
*/
|
|
730
|
+
export declare interface QualifierErrorContext {
|
|
731
|
+
readonly pass?: string;
|
|
732
|
+
readonly definition?: string;
|
|
733
|
+
readonly cause?: unknown;
|
|
734
|
+
}
|
|
710
735
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
* @example
|
|
719
|
-
* ```ts
|
|
720
|
-
* import { quantitativeResultToDerivation } from '@orkestrel/qualifier'
|
|
721
|
-
*
|
|
722
|
-
* const result = { reasoning: 'quantitative' as const, value: 500_000, groups: [], count: 1, success: true, trace: ['cap: 500000'], errors: [] }
|
|
723
|
-
* quantitativeResultToDerivation('cap', result)
|
|
724
|
-
* // { id: 'cap', value: 500000, success: true, trace: ['cap: 500000'], errors: [] }
|
|
725
|
-
* ```
|
|
726
|
-
*/
|
|
727
|
-
export declare function quantitativeResultToDerivation(id: string, result: QuantitativeResult): Derivation;
|
|
736
|
+
/** Represents the push observation surface of a {@link QualifierInterface}. */
|
|
737
|
+
export declare type QualifierEventMap = {
|
|
738
|
+
readonly derive: readonly [derivation: Derivation];
|
|
739
|
+
readonly finding: readonly [finding: Finding];
|
|
740
|
+
readonly qualify: readonly [result: QualificationResult];
|
|
741
|
+
readonly destroy: readonly [];
|
|
742
|
+
};
|
|
728
743
|
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
744
|
+
/** Owns or borrows one reason engine and returns eligibility. */
|
|
745
|
+
export declare interface QualifierInterface {
|
|
746
|
+
/** Holds the typed observation surface carrying `derive`, `finding`, `qualify`, and `destroy`. */
|
|
747
|
+
readonly emitter: EmitterInterface<QualifierEventMap>;
|
|
748
|
+
/**
|
|
749
|
+
* Qualifies one subject against one authored definition.
|
|
750
|
+
*
|
|
751
|
+
* @remarks
|
|
752
|
+
* Semantic validation runs first when the `validate` option is on, so an invalid
|
|
753
|
+
* definition throws before any pass runs. Passes run in authored order and the
|
|
754
|
+
* caller's `subject` is never mutated: the working projection under
|
|
755
|
+
* `QUALIFICATION_KEY` is built copy-on-write and discarded after the call.
|
|
756
|
+
*
|
|
757
|
+
* @param subject - The record to qualify
|
|
758
|
+
* @param definition - The authored qualification definition
|
|
759
|
+
* @returns A fresh, frozen qualification result
|
|
760
|
+
* @throws {@link QualifierError} `'DEFINITION'` when validation is enabled and the
|
|
761
|
+
* definition fails semantic validation, or when the engine rejects a pass as an invalid
|
|
762
|
+
* definition; `'MISMATCH'` when the subject is not a record or already carries the
|
|
763
|
+
* reserved `qualification` key; `'DESTROYED'` after `destroy`, or when the engine
|
|
764
|
+
* reports itself destroyed mid-pass; `'ENGINE'` for every other engine throw
|
|
765
|
+
*
|
|
766
|
+
* @example
|
|
767
|
+
* ```ts
|
|
768
|
+
* import { createQualificationDefinition, createQualifier, createRuling } from '@orkestrel/qualifier'
|
|
769
|
+
* import { createAtom, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
770
|
+
*
|
|
771
|
+
* const gates = createLogicalDefinition('gates', 'Eligibility gates', [
|
|
772
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
773
|
+
* ])
|
|
774
|
+
* const definition = createQualificationDefinition('standard', 'Standard eligibility', [gates], {
|
|
775
|
+
* rulings: [createRuling('license', 'gates', 'licensed', 'restriction')],
|
|
776
|
+
* })
|
|
777
|
+
*
|
|
778
|
+
* const qualifier = createQualifier()
|
|
779
|
+
* qualifier.qualify({ id: 'risk-1', licensed: false }, definition).eligibility // 'ineligible'
|
|
780
|
+
* qualifier.destroy()
|
|
781
|
+
* ```
|
|
782
|
+
*/
|
|
783
|
+
qualify(subject: Subject, definition: QualificationDefinition): QualificationResult;
|
|
784
|
+
/**
|
|
785
|
+
* Validates one authored definition semantically, without running it.
|
|
786
|
+
*
|
|
787
|
+
* @remarks
|
|
788
|
+
* Structural shape is `isQualificationDefinition`'s job. This checks the meaning:
|
|
789
|
+
* non-empty id and name, valid and uniquely identified passes and rulings, every
|
|
790
|
+
* ruling reference resolving to a logical pass and one of its rules, and no pass id
|
|
791
|
+
* shadowing `QUALIFICATION_KEY`. An empty definition, a logical pass carrying no
|
|
792
|
+
* ruling, and a derivation no later pass reads are warnings rather than errors.
|
|
793
|
+
*
|
|
794
|
+
* @param definition - The authored qualification definition
|
|
795
|
+
* @returns A fresh validation result carrying `valid`, `errors`, and `warnings`
|
|
796
|
+
* @throws {@link QualifierError} `'DESTROYED'` after `destroy`
|
|
797
|
+
*
|
|
798
|
+
* @example
|
|
799
|
+
* ```ts
|
|
800
|
+
* import { createQualificationDefinition, createQualifier } from '@orkestrel/qualifier'
|
|
801
|
+
*
|
|
802
|
+
* const qualifier = createQualifier()
|
|
803
|
+
* qualifier.validate(createQualificationDefinition('empty', 'Empty', []))
|
|
804
|
+
* // { valid: true, errors: [], warnings: ['Definition has no passes'] }
|
|
805
|
+
* qualifier.destroy()
|
|
806
|
+
* ```
|
|
807
|
+
*/
|
|
808
|
+
validate(definition: QualificationDefinition): ReasonValidationResult;
|
|
809
|
+
/**
|
|
810
|
+
* Destroys this qualifier, idempotently.
|
|
811
|
+
*
|
|
812
|
+
* @remarks
|
|
813
|
+
* An owned engine is destroyed; an injected engine stays caller-owned and is left
|
|
814
|
+
* alone. The `destroy` event fires before the emitter itself is destroyed, and a
|
|
815
|
+
* later call returns without re-emitting.
|
|
816
|
+
*
|
|
817
|
+
* @example
|
|
818
|
+
* ```ts
|
|
819
|
+
* import { createQualifier } from '@orkestrel/qualifier'
|
|
820
|
+
*
|
|
821
|
+
* const qualifier = createQualifier()
|
|
822
|
+
* qualifier.destroy()
|
|
823
|
+
* qualifier.destroy() // idempotent
|
|
824
|
+
* ```
|
|
825
|
+
*/
|
|
826
|
+
destroy(): void;
|
|
827
|
+
}
|
|
763
828
|
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
829
|
+
/**
|
|
830
|
+
* Carries the options for `createQualifier` / the `Qualifier` constructor.
|
|
831
|
+
*
|
|
832
|
+
* @remarks
|
|
833
|
+
* `engine` injects a reason engine that stays caller-owned and is never destroyed; an
|
|
834
|
+
* absent `engine` makes the qualifier create and own one. `validate` runs semantic
|
|
835
|
+
* validation before every qualification. Default: `true`. `labels` overrides the display
|
|
836
|
+
* name of a premise field, keyed by the dot-joined field path `formatField` produces, and
|
|
837
|
+
* takes precedence over a premise's own `label`. `on` carries the initial emitter hooks,
|
|
838
|
+
* and `error` handles a listener throw.
|
|
839
|
+
*/
|
|
840
|
+
export declare interface QualifierOptions {
|
|
841
|
+
readonly engine?: ReasonInterface;
|
|
842
|
+
readonly validate?: boolean;
|
|
843
|
+
readonly labels?: Readonly<Record<string, string>>;
|
|
844
|
+
readonly on?: EmitterHooks<QualifierEventMap>;
|
|
845
|
+
readonly error?: EmitterErrorHandler;
|
|
846
|
+
}
|
|
773
847
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
export declare function rulingDefinition(id: string, pass: string, rule: string, effect: QualificationEffect, input?: RulingInput): Ruling;
|
|
848
|
+
/**
|
|
849
|
+
* Projects a quantitative result into a {@link Derivation} audit record.
|
|
850
|
+
*
|
|
851
|
+
* @param id - The pass id
|
|
852
|
+
* @param result - The evaluated quantitative result
|
|
853
|
+
* @returns A fresh derivation
|
|
854
|
+
*
|
|
855
|
+
* @example
|
|
856
|
+
* ```ts
|
|
857
|
+
* import { quantitativeResultToDerivation } from '@orkestrel/qualifier'
|
|
858
|
+
*
|
|
859
|
+
* const result = { reasoning: 'quantitative' as const, value: 500_000, groups: [], count: 1, success: true, trace: ['cap: 500000'], errors: [] }
|
|
860
|
+
* quantitativeResultToDerivation('cap', result)
|
|
861
|
+
* // { id: 'cap', value: 500000, success: true, trace: ['cap: 500000'], errors: [] }
|
|
862
|
+
* ```
|
|
863
|
+
*/
|
|
864
|
+
export declare function quantitativeResultToDerivation(id: string, result: QuantitativeResult): Derivation;
|
|
792
865
|
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
866
|
+
/**
|
|
867
|
+
* Projects one reason result into the internal qualification namespace.
|
|
868
|
+
*
|
|
869
|
+
* @remarks
|
|
870
|
+
* A quantitative result contributes its numeric `value`. A logical result
|
|
871
|
+
* contributes a record of its `conclusion` plus every applied rule's asserted
|
|
872
|
+
* conclusion fields (`extractConclusions`). A mismatched pairing contributes
|
|
873
|
+
* `false`. The projection is pure — it never mutates the subject or the result.
|
|
874
|
+
*
|
|
875
|
+
* @param definition - The pass definition that produced the result
|
|
876
|
+
* @param result - The evaluated reason result
|
|
877
|
+
* @returns The pass's working projection
|
|
878
|
+
*
|
|
879
|
+
* @example
|
|
880
|
+
* ```ts
|
|
881
|
+
* import { reasonResultToProjection } from '@orkestrel/qualifier'
|
|
882
|
+
* import { createFactorGroup, createQuantitativeDefinition, createStaticFactor } from '@orkestrel/reason'
|
|
883
|
+
*
|
|
884
|
+
* const cap = createQuantitativeDefinition('cap', 'TIV cap', [
|
|
885
|
+
* createFactorGroup('limit', 'sum', [createStaticFactor('base', 500_000)]),
|
|
886
|
+
* ])
|
|
887
|
+
* const result = {
|
|
888
|
+
* reasoning: 'quantitative' as const,
|
|
889
|
+
* value: 500_000,
|
|
890
|
+
* groups: [],
|
|
891
|
+
* count: 1,
|
|
892
|
+
* success: true,
|
|
893
|
+
* trace: [],
|
|
894
|
+
* errors: [],
|
|
895
|
+
* }
|
|
896
|
+
* reasonResultToProjection(cap, result) // 500000
|
|
897
|
+
* ```
|
|
898
|
+
*/
|
|
899
|
+
export declare function reasonResultToProjection(definition: QualificationPass, result: ReasonResult): QualificationProjection;
|
|
798
900
|
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
901
|
+
/**
|
|
902
|
+
* Renders a {@link Premise} comparison as a display-neutral verb phrase.
|
|
903
|
+
*
|
|
904
|
+
* @param comparison - The comparison to describe
|
|
905
|
+
* @returns A display-neutral phrase
|
|
906
|
+
*
|
|
907
|
+
* @example
|
|
908
|
+
* ```ts
|
|
909
|
+
* import { renderComparison } from '@orkestrel/qualifier'
|
|
910
|
+
*
|
|
911
|
+
* renderComparison('above') // 'is more than'
|
|
912
|
+
* ```
|
|
913
|
+
*/
|
|
914
|
+
export declare function renderComparison(comparison: NonNullable<Premise['comparison']>): string;
|
|
915
|
+
|
|
916
|
+
/**
|
|
917
|
+
* Renders one {@link Premise} into a display-neutral sentence.
|
|
918
|
+
*
|
|
919
|
+
* @remarks
|
|
920
|
+
* The checked form renders only when `field` and `comparison` are BOTH
|
|
921
|
+
* present, and `description` then goes unused; a premise missing either half
|
|
922
|
+
* of the checked pair renders as described instead.
|
|
923
|
+
*
|
|
924
|
+
* @param entry - The premise to render
|
|
925
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
926
|
+
* @returns A display-neutral sentence
|
|
927
|
+
*
|
|
928
|
+
* @example
|
|
929
|
+
* ```ts
|
|
930
|
+
* import { renderPremise } from '@orkestrel/qualifier'
|
|
931
|
+
*
|
|
932
|
+
* renderPremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
|
|
933
|
+
* // 'age is more than 18 → met'
|
|
934
|
+
*
|
|
935
|
+
* renderPremise({ description: 'Applicant is enrolled', met: true })
|
|
936
|
+
* // 'Applicant is enrolled → met'
|
|
937
|
+
* ```
|
|
938
|
+
*/
|
|
939
|
+
export declare function renderPremise(entry: Premise, labels?: Readonly<Record<string, string>>): string;
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Renders a structured or scalar expected value display-neutrally.
|
|
943
|
+
*
|
|
944
|
+
* @remarks
|
|
945
|
+
* An array renders as its elements, each `String`-coerced, joined with `', '`.
|
|
946
|
+
* A reason `Bounds`-shaped record (the `between` / `outside` expected value)
|
|
947
|
+
* renders its present sides joined with the word `and`; an absent side is
|
|
948
|
+
* omitted. A finite number and everything else fall back to plain `String()` —
|
|
949
|
+
* a finite number is NOT grouped here (grouping is {@link interpolateMessage}'s
|
|
950
|
+
* message-token-only behavior).
|
|
951
|
+
*
|
|
952
|
+
* @param value - The value to render
|
|
953
|
+
* @returns A display-neutral rendering
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```ts
|
|
957
|
+
* import { renderValue } from '@orkestrel/qualifier'
|
|
958
|
+
*
|
|
959
|
+
* renderValue([18, 25, 40]) // '18, 25, 40'
|
|
960
|
+
* renderValue({ minimum: 18, maximum: 65 }) // '18 and 65'
|
|
961
|
+
* renderValue(42) // '42'
|
|
962
|
+
* ```
|
|
963
|
+
*/
|
|
964
|
+
export declare function renderValue(value: unknown): string;
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* Builds rich premises for one fired {@link Rule} by walking its premise atoms
|
|
968
|
+
* and re-evaluating each against the working subject.
|
|
969
|
+
*
|
|
970
|
+
* @remarks
|
|
971
|
+
* A reason rule result carries only booleans, so this is the qualifier's own
|
|
972
|
+
* premise-audit projection: each authored premise expression is flattened to its
|
|
973
|
+
* atom leaves through `extractAtoms`, and each leaf's `Check` is re-evaluated through
|
|
974
|
+
* the injected `evaluator`. A membership check (`any` / `none`) over an EMPTY
|
|
975
|
+
* array value is content-free and is skipped.
|
|
976
|
+
*
|
|
977
|
+
* @param rule - The authored rule
|
|
978
|
+
* @param working - The working subject to evaluate against
|
|
979
|
+
* @param evaluator - The shared reason check evaluator
|
|
980
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
981
|
+
* @returns A fresh, ordered list of rich premises
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```ts
|
|
985
|
+
* import { ruleToPremises } from '@orkestrel/qualifier'
|
|
986
|
+
* import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
987
|
+
*
|
|
988
|
+
* const gates = createLogicalDefinition('gates', 'Gates', [
|
|
989
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
990
|
+
* ])
|
|
991
|
+
* const evaluator = createEvaluator()
|
|
992
|
+
* const premises = ruleToPremises(gates.rules[0], { licensed: false }, evaluator)
|
|
993
|
+
*
|
|
994
|
+
* premises[0]?.met // true
|
|
995
|
+
* ```
|
|
996
|
+
*/
|
|
997
|
+
export declare function ruleToPremises(rule: Rule, working: Subject, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): readonly Premise[];
|
|
998
|
+
|
|
999
|
+
/** Represents an authored consequence for one rule in one logical pass. */
|
|
1000
|
+
export declare interface Ruling {
|
|
1001
|
+
readonly id: string;
|
|
1002
|
+
readonly pass: string;
|
|
1003
|
+
readonly rule: string;
|
|
1004
|
+
readonly effect: QualificationEffect;
|
|
1005
|
+
readonly scope?: string;
|
|
1006
|
+
readonly message?: string;
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
/** Carries the optional fields `createRuling` accepts. */
|
|
1010
|
+
export declare interface RulingInput {
|
|
1011
|
+
readonly scope?: string;
|
|
1012
|
+
readonly message?: string;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/**
|
|
1016
|
+
* Joins a ruling, its logical rule result, the pass, the pre-projection subject,
|
|
1017
|
+
* and an evaluator into a {@link Finding}.
|
|
1018
|
+
*
|
|
1019
|
+
* @remarks
|
|
1020
|
+
* `applied` reflects whether the ruling's rule fired. Premises are re-evaluated
|
|
1021
|
+
* against the SAME subject snapshot the pass evaluated, so a conclusion the pass
|
|
1022
|
+
* derived can never satisfy its own reported premise. The message interpolates
|
|
1023
|
+
* against that snapshot.
|
|
1024
|
+
*
|
|
1025
|
+
* @param ruling - The authored ruling
|
|
1026
|
+
* @param pass - The pass the ruling's rule lives in
|
|
1027
|
+
* @param result - The pass's evaluated logical result
|
|
1028
|
+
* @param subject - The pre-projection subject snapshot the pass evaluated
|
|
1029
|
+
* @param evaluator - The shared reason check evaluator
|
|
1030
|
+
* @param labels - Optional field-to-label overrides, keyed by dot-joined field
|
|
1031
|
+
* @returns A fresh finding
|
|
1032
|
+
*
|
|
1033
|
+
* @example
|
|
1034
|
+
* ```ts
|
|
1035
|
+
* import { rulingToFinding } from '@orkestrel/qualifier'
|
|
1036
|
+
* import { createAtom, createEvaluator, createLogicalDefinition, createRule } from '@orkestrel/reason'
|
|
1037
|
+
*
|
|
1038
|
+
* const gates = createLogicalDefinition('gates', 'Gates', [
|
|
1039
|
+
* createRule('licensed', [createAtom('licensed', 'equals', false)], createAtom('blocked', 'equals', true)),
|
|
1040
|
+
* ])
|
|
1041
|
+
* const ruling = { id: 'license', pass: 'gates', rule: 'licensed', effect: 'restriction' as const }
|
|
1042
|
+
* const result = { reasoning: 'logical' as const, conclusion: true, rules: [{ id: 'licensed', applied: true, premises: [true] }], count: 1, success: true, trace: [], errors: [] }
|
|
1043
|
+
* const finding = rulingToFinding(ruling, gates, result, { licensed: false }, createEvaluator())
|
|
1044
|
+
*
|
|
1045
|
+
* finding.applied // true
|
|
1046
|
+
* ```
|
|
1047
|
+
*/
|
|
1048
|
+
export declare function rulingToFinding(ruling: Ruling, pass: QualificationPass, result: LogicalResult, subject: Subject, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): Finding;
|
|
833
1049
|
|
|
834
|
-
|
|
1050
|
+
export { }
|