@orkestrel/rater 0.0.1 → 0.0.2

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.
@@ -5,1167 +5,544 @@ import { Comparison } from '@orkestrel/reason';
5
5
  import { EmitterErrorHandler } from '@orkestrel/emitter';
6
6
  import { EmitterHooks } from '@orkestrel/emitter';
7
7
  import { EmitterInterface } from '@orkestrel/emitter';
8
- import { EvaluatorInterface } from '@orkestrel/reason';
9
- import { Expression } from '@orkestrel/reason';
10
8
  import { Factor } from '@orkestrel/reason';
11
9
  import { FactorResult } from '@orkestrel/reason';
12
10
  import { FieldPath } from '@orkestrel/contract';
13
11
  import { GroupResult } from '@orkestrel/reason';
14
12
  import { Guard } from '@orkestrel/contract';
15
13
  import { JSONValue } from '@orkestrel/contract';
16
- import { LogicalDefinition } from '@orkestrel/reason';
17
- import { LogicalResult } from '@orkestrel/reason';
18
14
  import { QuantitativeDefinition } from '@orkestrel/reason';
19
15
  import { QuantitativeResult } from '@orkestrel/reason';
20
16
  import { ReasonInterface } from '@orkestrel/reason';
21
- import { Rule } from '@orkestrel/reason';
22
17
  import { Subject } from '@orkestrel/reason';
23
18
 
24
- /** The reserved working-subject key a batch's aggregate projection is written under. */
25
- export declare const AGGREGATE_KEY = "aggregate";
26
-
27
- /** Batch aggregate fields, an optional partition key, and optional gates. */
28
- export declare interface AggregateDefinition {
29
- readonly fields: readonly FieldPath[];
30
- readonly by?: FieldPath;
31
- readonly gates?: LogicalDefinition;
32
- }
19
+ /**
20
+ * Build premises from a quantitative factor's authored checks and evaluated
21
+ * check results.
22
+ *
23
+ * @param checks - The factor's authored checks
24
+ * @param results - The corresponding {@link CheckResult}s, in the same order
25
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
26
+ * @returns A fresh list of premises, one per authored check
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * import { check } from '@orkestrel/reason'
31
+ * import { checkPremises } from '@orkestrel/rater'
32
+ *
33
+ * const checks = [check('age', 'above', 18)]
34
+ * checkPremises(checks, [{ field: 'age', met: true, actual: 25 }])
35
+ * ```
36
+ */
37
+ export declare function checkPremises(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Premise[];
33
38
 
34
39
  /**
35
- * Build an {@link AggregateDefinition}.
40
+ * Create a rating orchestrator.
36
41
  *
37
- * @param fields - The batch fields to sum
38
- * @param by - The partition key field, when the aggregate is partitioned
39
- * @param gates - The logical definition gating aggregate determinations, when present
40
- * @returns A fresh aggregate definition
42
+ * @param options - Optional total handler, labels, injected engine, and emitter hooks
43
+ * @returns A {@link RaterInterface}
41
44
  *
42
45
  * @example
43
46
  * ```ts
44
- * import { aggregateDefinition } from '@orkestrel/rater'
47
+ * import { createRater } from '@orkestrel/rater'
45
48
  *
46
- * aggregateDefinition(['amount'])
49
+ * const rater = createRater()
50
+ * rater.destroy()
47
51
  * ```
48
52
  */
49
- export declare function aggregateDefinition(fields: readonly FieldPath[], by?: FieldPath, gates?: LogicalDefinition): AggregateDefinition;
53
+ export declare function createRater(options?: RaterOptions): RaterInterface;
50
54
 
51
55
  /**
52
- * Collect the deduped, ordered union of every program's aggregate fields.
56
+ * Determine whether a value is an exact {@link LineDefinition} record.
57
+ *
58
+ * @remarks
59
+ * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)
60
+ * returns `false`, never throws. The record shape is EXACT — an extra key fails.
53
61
  *
54
- * @param programs - The compiled programs to scan
55
- * @returns A fresh list of aggregate field paths, in first-seen order
62
+ * @param value - The value to test
63
+ * @returns `true` when `value` is a `LineDefinition`
56
64
  *
57
65
  * @example
58
66
  * ```ts
59
- * import { aggregateFields } from '@orkestrel/rater'
67
+ * import { quantitativeDefinition } from '@orkestrel/reason'
68
+ * import { isLineDefinition } from '@orkestrel/rater'
60
69
  *
61
- * aggregateFields(programs) // [['income'], ['assets']]
70
+ * isLineDefinition({ id: 'base', name: 'Base', rate: quantitativeDefinition('base', 'Base', []) }) // true
62
71
  * ```
63
72
  */
64
- export declare function aggregateFields(programs: readonly ProgramInterface[]): readonly FieldPath[];
73
+ export declare function isLineDefinition(value: unknown): value is LineDefinition;
65
74
 
66
- /** One batch aggregate partition. */
67
- export declare interface AggregateGroup {
68
- readonly key: string;
69
- readonly count: number;
70
- readonly sums: Readonly<Record<string, number>>;
71
- }
75
+ /**
76
+ * Narrow a caught value to a {@link RaterError}.
77
+ *
78
+ * @param value - The caught value to test
79
+ * @returns `true` when `value` is a `RaterError`
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * import { isRaterError, RaterError } from '@orkestrel/rater'
84
+ *
85
+ * try {
86
+ * throw new RaterError('DESTROYED', 'Rater has been destroyed')
87
+ * } catch (error) {
88
+ * if (isRaterError(error)) error.code // 'DESTROYED'
89
+ * }
90
+ * ```
91
+ */
92
+ export declare function isRaterError(value: unknown): value is RaterError;
72
93
 
73
94
  /**
74
- * Partition a batch of subjects by a field, summing aggregate fields per
75
- * partition.
95
+ * Determine whether a value is an exact {@link RatingDefinition} record.
76
96
  *
77
97
  * @remarks
78
- * The partition key is the resolved `by` field, coerced with `String` —
79
- * `undefined` collapses to the empty string, so a subject missing the field
80
- * and a subject whose field is literally `''` land in the SAME partition,
81
- * and a numeric `1` collides with the string `'1'`.
98
+ * Total guard (AGENTS §14): adversarial input (cycles, hostile prototypes)
99
+ * returns `false`, never throws. The record shape is EXACT — an extra key fails.
82
100
  *
83
- * @param subjects - The batch of subjects
84
- * @param fields - The fields to sum within each partition
85
- * @param by - The partition key field; no partition is built when absent
86
- * @returns A fresh list of aggregate groups, or an empty list when `by` is absent
101
+ * @param value - The value to test
102
+ * @returns `true` when `value` is a `RatingDefinition`
103
+ *
104
+ * @example
105
+ * ```ts
106
+ * import { isRatingDefinition } from '@orkestrel/rater'
107
+ *
108
+ * isRatingDefinition({ id: 'r1', name: 'Rating', lines: [] }) // true
109
+ * ```
87
110
  */
88
- export declare function aggregateGroups(subjects: readonly Subject[], fields: readonly FieldPath[], by?: FieldPath): readonly AggregateGroup[];
111
+ export declare function isRatingDefinition(value: unknown): value is RatingDefinition;
89
112
 
90
113
  /**
91
- * Build the batch aggregate working projection written under
92
- * {@link AGGREGATE_KEY}'s value.
114
+ * Determine whether a value is a {@link Stage} literal.
115
+ *
116
+ * @param value - The value to test
117
+ * @returns `true` when `value` is `'factor'`, `'group'`, or `'total'`
118
+ *
119
+ * @example
120
+ * ```ts
121
+ * import { isStage } from '@orkestrel/rater'
93
122
  *
94
- * @param count - The batch (or partition) subject count
95
- * @param sums - The summed aggregate fields
96
- * @param group - The subject's own partition, when the aggregate is partitioned
97
- * @returns A fresh aggregate projection record
123
+ * isStage('group') // true
124
+ * isStage('step') // false
125
+ * ```
98
126
  */
99
- export declare function aggregateProjection(count: number, sums: Readonly<Record<string, number>>, group?: AggregateGroup): Readonly<Record<string, unknown>>;
127
+ export declare const isStage: Guard<Stage>;
100
128
 
101
129
  /**
102
- * Build the reserved-key record an aggregate gate definition runs against.
130
+ * One rateable line — a quantitative definition joined to display metadata.
103
131
  *
104
- * @param count - The batch (or partition) subject count
105
- * @param sums - The summed aggregate fields
106
- * @param group - The subject's own partition, when the aggregate is partitioned
107
- * @returns A fresh record carrying the aggregate projection under {@link AGGREGATE_KEY}
132
+ * @remarks
133
+ * `rate` is a plain reason {@link QuantitativeDefinition}; Rater delegates every
134
+ * actual evaluation of it to the shared engine.
108
135
  */
109
- export declare function aggregateRecord(count: number, sums: Readonly<Record<string, number>>, group?: AggregateGroup): Readonly<Record<string, unknown>>;
110
-
111
- /** A batch rating outcome across every subject. */
112
- export declare interface AggregateResult {
113
- readonly subjects: readonly SubjectResult[];
114
- readonly determinations: readonly Determination[];
115
- readonly groups: readonly AggregateGroup[];
116
- readonly tallies: Readonly<Record<string, Readonly<Record<Status, Tally>>>>;
117
- readonly count: number;
118
- readonly sums: Readonly<Record<string, number>>;
136
+ export declare interface LineDefinition {
137
+ readonly id: string;
138
+ readonly name: string;
139
+ readonly description?: string;
140
+ readonly rate: QuantitativeDefinition;
141
+ readonly metadata?: JSONValue;
119
142
  }
120
143
 
121
144
  /**
122
- * Sum aggregate fields across a batch of subjects.
145
+ * Build a {@link LineDefinition}.
146
+ *
147
+ * @param id - The line id
148
+ * @param name - The display name
149
+ * @param rate - The line's quantitative rating definition
150
+ * @param overrides - Optional {@link LineDefinition} fields merged over the defaults
151
+ * @returns A fresh line definition
123
152
  *
124
- * @param subjects - The batch of subjects
125
- * @param fields - The fields to sum
126
- * @returns A fresh record of dot-joined field to summed finite value
153
+ * @example
154
+ * ```ts
155
+ * import { quantitativeDefinition } from '@orkestrel/reason'
156
+ * import { lineDefinition } from '@orkestrel/rater'
157
+ *
158
+ * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))
159
+ * ```
127
160
  */
128
- export declare function aggregateSums(subjects: readonly Subject[], fields: readonly FieldPath[]): Readonly<Record<string, number>>;
161
+ export declare function lineDefinition(id: string, name: string, rate: QuantitativeDefinition, overrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>): LineDefinition;
129
162
 
130
163
  /**
131
- * Assert a value is a valid rater {@link Subject}, narrowing it in place.
164
+ * One line's rating outcome.
132
165
  *
133
- * @param subject - The candidate subject to validate
134
- * @throws {@link RaterError} `'MISMATCH'` when the value is not a record, or
135
- * when it already carries a reserved rater working-subject key
136
- */
137
- export declare function assertSubject(subject: unknown): asserts subject is Subject;
138
-
139
- /**
140
- * Convert an authority's fired {@link RuleResult}s into `limit`
141
- * {@link Determination}s.
142
- *
143
- * @param definition - The authority's logical definition
144
- * @param result - The evaluated logical result
145
- * @param rulings - Authored consequences, keyed by rule id
146
- * @param working - The working subject the authority ran against
147
- * @param evaluator - The shared reason check evaluator
148
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
149
- * @returns A fresh list of `limit` determinations
150
- */
151
- export declare function authorityToDeterminations(definition: LogicalDefinition, result: LogicalResult, rulings: Readonly<Record<string, Ruling>> | undefined, working: Subject, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): readonly Determination[];
152
-
153
- /**
154
- * Build premises from a quantitative factor's authored checks and evaluated
155
- * check results.
156
- *
157
- * @param checks - The factor's authored checks
158
- * @param results - The corresponding {@link CheckResult}s, in the same order
159
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
160
- * @returns A fresh list of premises, one per authored check
161
- */
162
- export declare function checkPremises(checks: readonly Check[] | undefined, results: readonly CheckResult[] | undefined, labels?: Readonly<Record<string, string>>): readonly Premise[];
163
-
164
- /**
165
- * Return the most severe {@link Eligibility} in a list.
166
- *
167
- * @param eligibilities - The eligibilities to combine
168
- * @returns The most severe eligibility, or `'eligible'` for an empty list
169
- *
170
- * @example
171
- * ```ts
172
- * import { combineEligibilities } from '@orkestrel/rater'
173
- *
174
- * combineEligibilities(['eligible', 'referral']) // 'referral'
175
- * ```
176
- */
177
- export declare function combineEligibilities(eligibilities: readonly Eligibility[]): Eligibility;
178
-
179
- /**
180
- * Complete a partial status tally record with zero entries for every missing
181
- * {@link Status}.
182
- *
183
- * @param entries - The partial tally entries to complete
184
- * @returns A record with all five statuses present
185
- */
186
- export declare function completeTallies(entries: Partial<Record<Status, Tally>>): Readonly<Record<Status, Tally>>;
187
-
188
- /**
189
- * Create and validate one compiled program over a shared reasoning engine.
190
- *
191
- * @param definition - The authored program definition
192
- * @param reason - The shared reasoning engine, injected (never owned or destroyed by the program)
193
- * @param options - Optional total handler and labels
194
- * @returns A {@link ProgramInterface}
195
- * @throws {@link RaterError} `'DEFINITION'` when the definition fails {@link isProgramDefinition}
196
- *
197
- * @example
198
- * ```ts
199
- * import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@orkestrel/reason'
200
- * import { createProgram, programDefinition } from '@orkestrel/rater'
201
- *
202
- * const reason = createReason({ reasoners: [createQuantitativeReasoner(), createLogicalReasoner()], bail: false })
203
- * const program = createProgram(programDefinition('p1', 'Program', []), reason)
204
- * ```
205
- */
206
- export declare function createProgram(definition: ProgramDefinition, reason: ReasonInterface, options?: ProgramOptions): ProgramInterface;
207
-
208
- /**
209
- * Create an ordered manager over compiled programs (AGENTS §9), built over an
210
- * injected, shared reasoning engine.
211
- *
212
- * @param reason - The shared reasoning engine, injected into every compiled program
213
- * @param options - Optional total handler, labels, validation policy, and emitter hooks
214
- * @returns A {@link ProgramManagerInterface}
215
- *
216
- * @example
217
- * ```ts
218
- * import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@orkestrel/reason'
219
- * import { createProgramManager, programDefinition } from '@orkestrel/rater'
220
- *
221
- * const reason = createReason({ reasoners: [createQuantitativeReasoner(), createLogicalReasoner()], bail: false })
222
- * const manager = createProgramManager(reason)
223
- * manager.add(programDefinition('p1', 'Program', []))
224
- * manager.destroy()
225
- * ```
226
- */
227
- export declare function createProgramManager(reason: ReasonInterface, options?: ProgramManagerOptions): ProgramManagerInterface;
228
-
229
- /**
230
- * Create a rating orchestrator.
231
- *
232
- * @param options - Optional total handler, seed programs, labels, validation policy, and emitter hooks
233
- * @returns A {@link RaterInterface}
234
- *
235
- * @example
236
- * ```ts
237
- * import { createRater } from '@orkestrel/rater'
238
- *
239
- * const rater = createRater()
240
- * rater.destroy()
241
- * ```
242
- */
243
- export declare function createRater(options?: RaterOptions): RaterInterface;
244
-
245
- /**
246
- * Convert an {@link Eligibility} to its deterministic authority {@link Decision}.
247
- *
248
- * @param eligibility - The eligibility to convert
249
- * @returns The matching decision
250
- */
251
- export declare function decideEligibility(eligibility: Eligibility): Decision;
252
-
253
- /** Deterministic authority decision derived from eligibility. */
254
- export declare type Decision = 'approved' | 'denied' | 'submitted';
255
-
256
- /** Default definition validation policy for `ProgramManager.add`. */
257
- export declare const DEFAULT_RATER_VALIDATE = true;
258
-
259
- /**
260
- * Derive the eligibility impact of a set of determinations.
261
- *
262
- * @param determinations - The determinations to fold
263
- * @returns The most severe eligibility any APPLIED determination's effect carries
264
- *
265
- * @example
266
- * ```ts
267
- * import { deriveDeterminationEligibility } from '@orkestrel/rater'
268
- *
269
- * deriveDeterminationEligibility([{ id: 'r1', effect: 'restriction', applied: true, premises: [] }])
270
- * // 'ineligible'
271
- * ```
272
- */
273
- export declare function deriveDeterminationEligibility(determinations: readonly Determination[]): Eligibility;
274
-
275
- /**
276
- * Derive the final {@link Status} from eligibility, determinations, and rated
277
- * line evidence.
278
- *
279
- * @param eligibility - The program's combined eligibility
280
- * @param determinations - Every determination in scope (program and line)
281
- * @param lines - The program's rated lines
282
- * @returns The derived status
283
- */
284
- export declare function deriveStatus(eligibility: Eligibility, determinations: readonly Determination[], lines: readonly LineResult[]): Status;
285
-
286
- /**
287
- * Describe a {@link Premise} comparison as a display-neutral verb phrase.
288
- *
289
- * @param comparison - The comparison to describe
290
- * @returns A display-neutral phrase
291
- *
292
- * @example
293
- * ```ts
294
- * import { describeComparison } from '@orkestrel/rater'
295
- *
296
- * describeComparison('above') // 'is more than'
297
- * ```
298
- */
299
- export declare function describeComparison(comparison: NonNullable<Premise['comparison']>): string;
300
-
301
- /**
302
- * Describe a logical {@link Expression} tree without atom-specific evidence.
303
- *
304
- * @param expression - The expression to describe
305
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
306
- * @returns A display-neutral description
307
- */
308
- export declare function describeExpression(expression: Expression, labels?: Readonly<Record<string, string>>): string;
309
-
310
- /**
311
- * Render one {@link Premise} into a display-neutral sentence.
312
- *
313
- * @param entry - The premise to render
314
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
315
- * @returns A display-neutral sentence
316
- *
317
- * @example
318
- * ```ts
319
- * import { describePremise } from '@orkestrel/rater'
320
- *
321
- * describePremise({ field: 'age', comparison: 'above', expected: 18, actual: 25, met: true })
322
- * // 'age is more than 18 ? met'
323
- * ```
324
- */
325
- export declare function describePremise(entry: Premise, labels?: Readonly<Record<string, string>>): string;
326
-
327
- /**
328
- * Render a structured or scalar value display-neutrally.
329
- *
330
- * @remarks
331
- * An array renders as its elements, each `String`-coerced, joined with
332
- * `', '`. A Bounds-shaped record (a reason `Bounds` — the `between`/`outside`
333
- * comparison's expected value shape) renders its present sides joined with
334
- * the word `and`; an absent side is omitted. A finite number and everything
335
- * else fall back to plain `String()` coercion — a finite number is NOT
336
- * grouped here (that grouping is {@link interpolateMessage}'s own,
337
- * message-token-only behavior).
338
- *
339
- * @param value - The value to render
340
- * @returns A display-neutral rendering
341
- *
342
- * @example
343
- * ```ts
344
- * import { describeValue } from '@orkestrel/rater'
345
- *
346
- * describeValue([18, 25, 40]) // '18, 25, 40'
347
- * describeValue({ minimum: 18, maximum: 65 }) // '18 and 65'
348
- * describeValue(42) // '42'
349
- * ```
350
- */
351
- export declare function describeValue(value: unknown): string;
352
-
353
- /**
354
- * A resolved rule, authority, or notice outcome.
355
- *
356
- * @remarks
357
- * `premises` lists only content-bearing premises — content-free atoms (an
358
- * empty `any`/`none` membership check) are omitted, so its length may be
359
- * smaller than the engine's boolean premise count.
360
- */
361
- export declare interface Determination {
362
- readonly id: string;
363
- readonly effect: Effect;
364
- readonly applied: boolean;
365
- readonly line?: string;
366
- readonly message?: string;
367
- readonly premises: readonly Premise[];
368
- }
369
-
370
- /** A resolved determination effect. */
371
- export declare type Effect = 'restriction' | 'referral' | 'condition' | 'notice' | 'limit';
372
-
373
- /** The direct eligibility impact of an applied determination, by its effect. */
374
- export declare const EFFECT_ELIGIBILITIES: Readonly<Record<Effect, Eligibility | undefined>>;
375
-
376
- /** Advisory eligibility axis a program or line outcome carries. */
377
- export declare type Eligibility = 'eligible' | 'ineligible' | 'referral';
378
-
379
- /** The deterministic authority decision for each eligibility. */
380
- export declare const ELIGIBILITY_DECISIONS: Readonly<Record<Eligibility, Decision>>;
381
-
382
- /** Eligibility severity order, most severe first — the {@link combineEligibilities} scan order. */
383
- export declare const ELIGIBILITY_PRECEDENCE: readonly Eligibility[];
384
-
385
- /**
386
- * Build zero sums for a set of fields.
387
- *
388
- * @param fields - The fields to zero
389
- * @returns A fresh record of dot-joined field to `0`
390
- */
391
- export declare function emptySums(fields: readonly FieldPath[]): Readonly<Record<string, number>>;
392
-
393
- /**
394
- * Build empty status tallies in precedence order.
395
- *
396
- * @param fields - The fields each tally's sums are zeroed for
397
- * @returns A fresh, complete tally record
398
- */
399
- export declare function emptyTallies(fields: readonly FieldPath[]): Readonly<Record<Status, Tally>>;
400
-
401
- /**
402
- * Keep only the determinations scoped to one line.
403
- *
404
- * @param determinations - The determinations to filter
405
- * @param id - The line id to keep
406
- * @returns The determinations whose `line` matches `id`
407
- */
408
- export declare function filterLineDeterminations(determinations: readonly Determination[], id: string): readonly Determination[];
409
-
410
- /**
411
- * Keep only program-scoped (line-unscoped) determinations.
412
- *
413
- * @param determinations - The determinations to filter
414
- * @returns The determinations with no `line`
415
- */
416
- export declare function filterProgramDeterminations(determinations: readonly Determination[]): readonly Determination[];
417
-
418
- /**
419
- * Return authored line references (in passes, rulings, or notices) that name
420
- * no line on the program.
421
- *
422
- * @param definition - The program definition to check
423
- * @returns A fresh, deduped list of missing line ids
424
- */
425
- export declare function findMissingLineReferences(definition: ProgramDefinition): readonly string[];
426
-
427
- /**
428
- * Locate a {@link ProgramDefinition}'s reserved-key collisions — a quantitative
429
- * pass whose id shadows {@link AGGREGATE_KEY} / {@link OUTCOME_KEY}, or a
430
- * logical pass's or the authority's fired rule conclusion whose asserted
431
- * field's FIRST path segment shadows one.
432
- *
433
- * @remarks
434
- * A quantitative pass writes its value onto the working subject under its own
435
- * definition id (`assignField`), silently clobbering the reserved aggregate or
436
- * outcome projection when the id collides. A fired rule's conclusion atoms
437
- * merge onto the working subject (`mergeSubjects`/`extractConclusions`) keyed
438
- * by `formatField(check.field)` — an atom whose field's first segment is a
439
- * reserved key is flagged here even when its FULL formatted key is a nested
440
- * form (`outcome.total`), since authoring a conclusion under a reserved
441
- * top-level segment is never intentional.
442
- *
443
- * @param definition - The program definition to scan
444
- * @returns A fresh, deduped list of offending pass or rule ids
445
- *
446
- * @example
447
- * ```ts
448
- * import { quantitativeDefinition } from '@orkestrel/reason'
449
- * import { findReservedCollisions, passDefinition, programDefinition } from '@orkestrel/rater'
450
- *
451
- * findReservedCollisions(
452
- * programDefinition('p1', 'P1', [], {
453
- * passes: [passDefinition(quantitativeDefinition('aggregate', 'Aggregate', []))],
454
- * }),
455
- * ) // ['aggregate']
456
- * ```
457
- */
458
- export declare function findReservedCollisions(definition: ProgramDefinition): readonly string[];
459
-
460
- /**
461
- * Locate an authored {@link Rule} by id.
462
- *
463
- * @param definition - The logical definition to search
464
- * @param id - The rule id
465
- * @returns The matching rule, or `undefined`
466
- */
467
- export declare function findRule(definition: LogicalDefinition, id: string): Rule | undefined;
468
-
469
- /**
470
- * Locate the {@link AggregateGroup} a subject belongs to.
471
- *
472
- * @remarks
473
- * The lookup key is the resolved `by` field, coerced with `String` —
474
- * `undefined` collapses to the empty string, matching {@link aggregateGroups}'
475
- * partition-key coercion (a missing key and an empty-string key collide, and
476
- * a numeric `1` collides with the string `'1'`).
477
- *
478
- * @param subject - The subject to key
479
- * @param groups - The candidate groups to search
480
- * @param by - The partition key field; no group is found when absent
481
- * @returns The subject's matching group, or `undefined`
482
- *
483
- * @example
484
- * ```ts
485
- * import { groupFor } from '@orkestrel/rater'
486
- *
487
- * groupFor({ region: 'east' }, [{ key: 'east', count: 1, sums: {} }], ['region'])
488
- * // { key: 'east', count: 1, sums: {} }
489
- * ```
490
- */
491
- export declare function groupFor(subject: Subject, groups: readonly AggregateGroup[], by?: FieldPath): AggregateGroup | undefined;
492
-
493
- /** Determine whether a working record already carries a reserved rater key. */
494
- export declare function hasReservedKey(subject: Readonly<Record<string, unknown>>): boolean;
495
-
496
- /**
497
- * Interpolate `{{dotted.path}}` tokens in a message template against a record.
498
- *
499
- * @remarks
500
- * Each token is split on `.` into a {@link FieldPath} array and resolved with
501
- * the contracts {@link resolveField} (a plain string field is ONE key, never
502
- * dot-split — the split here is the token-to-path bridge). A finite number
503
- * renders with `en-US` thousand grouping (`5010` → `5,010`); any other resolved
504
- * value String-coerces. An UNRESOLVED path (the resolved value is `undefined`)
505
- * renders as the empty string — the deterministic "nothing to show" rule.
506
- *
507
- * @param template - The message template carrying `{{dotted.path}}` tokens
508
- * @param record - The record tokens resolve against
509
- * @returns The template with every token replaced
510
- *
511
- * @example
512
- * ```ts
513
- * import { interpolateMessage } from '@orkestrel/rater'
514
- *
515
- * interpolateMessage('Limit is {{limit}}', { limit: 5010 }) // 'Limit is 5,010'
516
- * interpolateMessage('Missing {{gone}}', {}) // 'Missing '
517
- * ```
518
- */
519
- export declare function interpolateMessage(template: string, record: Readonly<Record<string, unknown>>): string;
520
-
521
- /** Determine whether a value is an exact {@link AggregateDefinition} record. */
522
- export declare function isAggregateDefinition(value: unknown): value is AggregateDefinition;
523
-
524
- /** Determine whether a value is a {@link Decision} literal. */
525
- export declare const isDecision: Guard<Decision>;
526
-
527
- /** Determine whether a value is an {@link Effect} literal. */
528
- export declare const isEffect: Guard<Effect>;
529
-
530
- /** Determine whether a value is an {@link Eligibility} literal. */
531
- export declare const isEligibility: Guard<Eligibility>;
532
-
533
- /** Determine whether a value is an exact {@link LineDefinition} record. */
534
- export declare function isLineDefinition(value: unknown): value is LineDefinition;
535
-
536
- /** Determine whether a value is an exact {@link Notice} record. */
537
- export declare function isNotice(value: unknown): value is Notice;
538
-
539
- /** Determine whether a value is an exact {@link PassDefinition} record. */
540
- export declare function isPassDefinition(value: unknown): value is PassDefinition;
541
-
542
- /** Determine whether a value is an exact {@link ProgramDefinition} record. */
543
- export declare function isProgramDefinition(value: unknown): value is ProgramDefinition;
544
-
545
- /** Narrow a caught value to a {@link RaterError}. */
546
- export declare function isRaterError(value: unknown): value is RaterError;
547
-
548
- /** Determine whether a value is an exact {@link Ruling} record. */
549
- export declare function isRuling(value: unknown): value is Ruling;
550
-
551
- /** Determine whether a value is a rule-id-keyed {@link Ruling} record. */
552
- export declare const isRulings: Guard<Readonly<Record<string, Ruling>>>;
553
-
554
- /** Determine whether a value is a {@link Stage} literal. */
555
- export declare const isStage: Guard<Stage>;
556
-
557
- /** Determine whether a value is a {@link Status} literal. */
558
- export declare const isStatus: Guard<Status>;
559
-
560
- /**
561
- * One rateable line — a quantitative definition joined to display metadata.
562
- *
563
- * @remarks
564
- * `rate` is a plain reason {@link QuantitativeDefinition}; Rater delegates every
565
- * actual evaluation of it to the shared engine.
566
- */
567
- export declare interface LineDefinition {
568
- readonly id: string;
569
- readonly name: string;
570
- readonly description?: string;
571
- readonly rate: QuantitativeDefinition;
572
- readonly metadata?: JSONValue;
573
- }
574
-
575
- /**
576
- * Build a {@link LineDefinition}.
577
- *
578
- * @param id - The line id
579
- * @param name - The display name
580
- * @param rate - The line's quantitative rating definition
581
- * @param overrides - Optional {@link LineDefinition} fields merged over the defaults
582
- * @returns A fresh line definition
583
- *
584
- * @example
585
- * ```ts
586
- * import { quantitativeDefinition } from '@orkestrel/reason'
587
- * import { lineDefinition } from '@orkestrel/rater'
588
- *
589
- * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', []))
590
- * ```
591
- */
592
- export declare function lineDefinition(id: string, name: string, rate: QuantitativeDefinition, overrides?: Partial<Omit<LineDefinition, 'id' | 'name' | 'rate'>>): LineDefinition;
593
-
594
- /** One line's rating outcome. */
595
- export declare interface LineResult {
596
- readonly id: string;
597
- readonly name: string;
598
- readonly eligibility: Eligibility;
599
- readonly amount?: number;
600
- readonly worksheet?: Worksheet;
601
- readonly determinations: readonly Determination[];
602
- }
603
-
604
- /**
605
- * Build rich premises for one fired {@link Rule} by walking its premise atoms
606
- * and re-evaluating each against the working subject.
607
- *
608
- * @remarks
609
- * A reason {@link RuleResult} carries only booleans, so this is rater's own
610
- * premise-audit projection: each authored premise expression is flattened to
611
- * its atom leaves via {@link extractAtoms}, and each leaf's {@link Check} is
612
- * re-evaluated through the injected `evaluator`. A membership check (`any` /
613
- * `none`) over an EMPTY array value is content-free — a tautology or
614
- * contradiction that reads as "is none of —" in every surface — and is skipped.
615
- *
616
- * @param rule - The authored rule
617
- * @param working - The working subject to evaluate against
618
- * @param evaluator - The shared reason check evaluator
619
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
620
- * @returns A fresh, ordered list of rich premises
621
- */
622
- export declare function logicalPremises(rule: Rule, working: Subject, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): readonly Premise[];
623
-
624
- /** An authored informational determination emitted unconditionally. */
625
- export declare interface Notice {
626
- readonly id: string;
627
- readonly message: string;
628
- readonly line?: string;
629
- }
630
-
631
- /**
632
- * Build a {@link Notice}.
633
- *
634
- * @param id - The notice id
635
- * @param message - An interpolated message template
636
- * @param line - The line id this notice is scoped to, when it is line-scoped
637
- * @returns A fresh notice
638
- *
639
- * @example
640
- * ```ts
641
- * import { noticeDefinition } from '@orkestrel/rater'
642
- *
643
- * noticeDefinition('n1', 'Rated on {{date}}')
644
- * ```
645
- */
646
- export declare function noticeDefinition(id: string, message: string, line?: string): Notice;
647
-
648
- /**
649
- * Convert authored {@link Notice}s into unconditionally-applied
650
- * `notice` {@link Determination}s.
651
- *
652
- * @param notices - The authored notices
653
- * @param working - The working subject to interpolate messages against
654
- * @returns A fresh list of notice determinations
655
- */
656
- export declare function noticesToDeterminations(notices: readonly Notice[] | undefined, working: Readonly<Record<string, unknown>>): readonly Determination[];
657
-
658
- /** The reserved working-subject key an authority's outcome projection is written under. */
659
- export declare const OUTCOME_KEY = "outcome";
660
-
661
- /**
662
- * Build an authority outcome projection from an assembled {@link ProgramResult}.
663
- *
664
- * @param result - The program result computed before authority runs
665
- * @returns A record shaped for the authority's `outcome` projection
666
- */
667
- export declare function outcomeProjection(result: ProgramResult): Readonly<Record<string, unknown>>;
668
-
669
- /**
670
- * An ordered pre-rating pass over the working subject.
671
- *
672
- * @remarks
673
- * `line` scopes a logical pass's determinations to one line; a quantitative pass
674
- * has no line scope (its value is assigned onto the working subject under its own
675
- * definition id).
676
- */
677
- export declare interface PassDefinition {
678
- readonly line?: string;
679
- readonly definition: LogicalDefinition | QuantitativeDefinition;
680
- }
681
-
682
- /**
683
- * Build a {@link PassDefinition}.
684
- *
685
- * @param definition - The pass's logical or quantitative definition
686
- * @param line - The line id this pass is scoped to, when it is line-scoped
687
- * @returns A fresh pass definition
688
- *
689
- * @example
690
- * ```ts
691
- * import { quantitativeDefinition } from '@orkestrel/reason'
692
- * import { passDefinition } from '@orkestrel/rater'
693
- *
694
- * passDefinition(quantitativeDefinition('surcharge', 'Surcharge', []))
695
- * ```
696
- */
697
- export declare function passDefinition(definition: LogicalDefinition | QuantitativeDefinition, line?: string): PassDefinition;
698
-
699
- /** A shared checked-evidence row rendered into a display-neutral sentence. */
700
- export declare interface Premise {
701
- readonly field?: FieldPath;
702
- readonly label?: string;
703
- readonly description?: string;
704
- readonly comparison?: Comparison;
705
- readonly expected?: unknown;
706
- readonly actual?: unknown;
707
- readonly met?: boolean;
708
- }
709
-
710
- /**
711
- * Build a {@link Premise} from an evaluated {@link Check}.
712
- *
713
- * @param check - The authored check
714
- * @param actual - The resolved subject value
715
- * @param met - Whether the check was met (absent when not yet evaluated)
716
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
717
- * @returns A fresh premise
718
- */
719
- export declare function premiseCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Premise;
720
-
721
- /**
722
- * A compiled program — rates one subject at a time over an injected, shared
723
- * reasoning engine.
724
- *
725
- * @remarks
726
- * The engine is INJECTED and never owned, destroyed, or otherwise mutated by
727
- * the program: every actual evaluation (passes, lines, authority) delegates to
728
- * it. `rate` builds its working subject through copy-on-write overlays only —
729
- * the caller's `subject` is never mutated.
730
- */
731
- export declare class Program implements ProgramInterface {
732
- #private;
733
- constructor(definition: ProgramDefinition, reason: ReasonInterface, options?: ProgramOptions);
734
- get id(): string;
735
- get name(): string;
736
- get definition(): ProgramDefinition;
737
- rate(subject: Subject, aggregate?: Readonly<Record<string, unknown>>): ProgramResult;
738
- }
739
-
740
- /**
741
- * A pure authored program definition.
742
- *
743
- * @remarks
744
- * `passes` runs in order before `lines`; `authority` (a logical definition) runs
745
- * over the assembled result, extended with an outcome projection, to derive limit
746
- * determinations and the final decision.
747
- */
748
- export declare interface ProgramDefinition {
749
- readonly id: string;
750
- readonly name: string;
751
- readonly description?: string;
752
- readonly passes?: readonly PassDefinition[];
753
- readonly lines: readonly LineDefinition[];
754
- readonly rulings?: Readonly<Record<string, Ruling>>;
755
- readonly notices?: readonly Notice[];
756
- readonly authority?: LogicalDefinition;
757
- readonly aggregate?: AggregateDefinition;
758
- readonly metadata?: JSONValue;
759
- }
760
-
761
- /**
762
- * Build a {@link ProgramDefinition}.
763
- *
764
- * @param id - The program id
765
- * @param name - The display name
766
- * @param lines - The program's rateable lines
767
- * @param overrides - Optional {@link ProgramDefinition} fields merged over the defaults
768
- * @returns A fresh program definition
769
- *
770
- * @example
771
- * ```ts
772
- * import { programDefinition } from '@orkestrel/rater'
773
- *
774
- * programDefinition('p1', 'Program', [])
775
- * ```
776
- */
777
- export declare function programDefinition(id: string, name: string, lines: readonly LineDefinition[], overrides?: Partial<Omit<ProgramDefinition, 'id' | 'name' | 'lines'>>): ProgramDefinition;
778
-
779
- /**
780
- * A compiled program — engine-injected, rates one subject at a time against its
781
- * frozen definition.
782
- */
783
- export declare interface ProgramInterface {
784
- readonly id: string;
785
- readonly name: string;
786
- readonly definition: ProgramDefinition;
787
- rate(subject: Subject, aggregate?: Readonly<Record<string, unknown>>): ProgramResult;
788
- }
789
-
790
- /**
791
- * An ordered manager over compiled {@link ProgramInterface}s (AGENTS §9), built
792
- * over an injected, shared reasoning engine.
793
- *
794
- * @remarks
795
- * OWNS its `#programs` collection and its own {@link Emitter} over
796
- * {@link ProgramManagerEventMap}. `destroy()` is idempotent and tears the
797
- * emitter down LAST; every other call afterwards throws
798
- * {@link RaterError} `'DESTROYED'`.
799
- */
800
- export declare class ProgramManager implements ProgramManagerInterface {
801
- #private;
802
- constructor(engine: ReasonInterface, options?: ProgramManagerOptions);
803
- get emitter(): EmitterInterface<ProgramManagerEventMap>;
804
- get size(): number;
805
- has(id: string): boolean;
806
- program(id: string): ProgramInterface | undefined;
807
- programs(): readonly ProgramInterface[];
808
- add(definition: ProgramDefinition, options?: ProgramOptions): ProgramInterface;
809
- remove(ids: readonly string[]): boolean;
810
- remove(id: string): boolean;
811
- remove(): void;
812
- destroy(): void;
813
- }
814
-
815
- /**
816
- * The push observation surface of a {@link ProgramManagerInterface} (AGENTS §13).
817
- */
818
- export declare type ProgramManagerEventMap = {
819
- /** A program was added — carries its id. */
820
- readonly add: readonly [id: string];
821
- /** A program was removed — carries its id. */
822
- readonly remove: readonly [id: string];
823
- /** The manager was destroyed. */
824
- readonly destroy: readonly [];
825
- };
826
-
827
- /** An ordered manager over compiled programs (AGENTS §9). */
828
- export declare interface ProgramManagerInterface {
829
- readonly emitter: EmitterInterface<ProgramManagerEventMap>;
830
- readonly size: number;
831
- has(id: string): boolean;
832
- program(id: string): ProgramInterface | undefined;
833
- programs(): readonly ProgramInterface[];
834
- add(definition: ProgramDefinition, options?: ProgramOptions): ProgramInterface;
835
- remove(ids: readonly string[]): boolean;
836
- remove(id: string): boolean;
837
- remove(): void;
838
- destroy(): void;
839
- }
840
-
841
- /** Options for `createProgramManager` / the `ProgramManager` constructor. */
842
- export declare interface ProgramManagerOptions {
843
- readonly on?: EmitterHooks<ProgramManagerEventMap>;
844
- readonly error?: EmitterErrorHandler;
845
- readonly total?: TotalHandler;
846
- readonly labels?: Readonly<Record<string, string>>;
847
- readonly validate?: boolean;
848
- }
849
-
850
- /** Runtime options for one compiled program. */
851
- export declare interface ProgramOptions {
852
- readonly total?: TotalHandler;
853
- readonly labels?: Readonly<Record<string, string>>;
854
- }
855
-
856
- /** One program's rating outcome. */
857
- export declare interface ProgramResult {
858
- readonly id: string;
859
- readonly name: string;
860
- readonly eligibility: Eligibility;
861
- readonly status: Status;
862
- /**
863
- * @remarks
864
- * Present ONLY when the program HAS an `authority`, the authority result is
865
- * `logical`, NO `limit` determinations fired, and the authority result
866
- * carries no errors — absent otherwise.
867
- */
868
- readonly decision?: Decision;
869
- readonly lines: readonly LineResult[];
870
- readonly determinations: readonly Determination[];
871
- readonly derivations: readonly Worksheet[];
872
- readonly total?: number;
873
- readonly success: boolean;
874
- readonly trace: readonly string[];
875
- readonly errors: readonly string[];
876
- }
877
-
878
- /**
879
- * Assemble a final {@link ProgramResult} from its rated parts.
880
- *
881
- * @param definition - The authored program definition
882
- * @param lines - The program's rated lines
883
- * @param determinations - The program-scoped determinations
884
- * @param derivations - The pass-level quantitative worksheets
885
- * @param total - The program's total amount, when the total handler produced one
886
- * @param decision - The authority-derived decision, when one was reached
887
- * @param trace - The accumulated reasoning trace
888
- * @param errors - The accumulated reasoning errors
889
- * @returns A fresh program result
890
- */
891
- export declare function programResult(definition: ProgramDefinition, lines: readonly LineResult[], determinations: readonly Determination[], derivations: readonly Worksheet[], total: number | undefined, decision: Decision | undefined, trace: readonly string[], errors: readonly string[]): ProgramResult;
892
-
893
- /**
894
- * Build a rated {@link LineResult} from a line's evaluated
895
- * {@link QuantitativeResult}.
896
- *
897
- * @param definition - The authored line definition
898
- * @param result - The evaluated quantitative result
899
- * @param determinations - The line-scoped determinations
900
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
901
- * @returns A fresh line result
902
- */
903
- export declare function ratedLine(definition: LineDefinition, result: QuantitativeResult, determinations: readonly Determination[], labels?: Readonly<Record<string, string>>): LineResult;
904
-
905
- /**
906
- * The rating orchestrator — owns the shared reasoning engine and an ordered
907
- * {@link ProgramManager}, and projects results into the rating domain
908
- * vocabulary.
909
- *
910
- * @remarks
911
- * Constructs ONE shared reason engine (quantitative + logical reasoners,
912
- * `bail: false`) and injects it into every compiled program — Rater performs
913
- * NO evaluation arithmetic of its own; it only orchestrates and projects. The
914
- * batch `rate` overload is declared FIRST (AGENTS §9.2) so an array subject
915
- * resolves to the batch form. `destroy()` tears down the manager, then the
916
- * engine, then the emitter LAST (AGENTS §13); afterwards every other method
917
- * throws {@link RaterError} `'DESTROYED'`.
918
- */
919
- export declare class Rater implements RaterInterface {
920
- #private;
921
- constructor(options?: RaterOptions);
922
- get emitter(): EmitterInterface<RaterEventMap>;
923
- get programs(): ProgramManagerInterface;
924
- rate(subjects: readonly Subject[]): AggregateResult;
925
- rate(subject: Subject): SubjectResult;
926
- destroy(): void;
927
- }
928
-
929
- /**
930
- * A coded programmer error thrown by the rating layer.
931
- *
932
- * @remarks
933
- * `DUPLICATE` — a program id collision on `ProgramManager.add`. `MISSING` — an
934
- * unknown authored line reference at compile time.
935
- * `DEFINITION` — a program definition failed `isProgramDefinition`. `MISMATCH` —
936
- * a rated subject is not a record, or uses a reserved working-subject key.
937
- * `DESTROYED` — use of a destroyed entity.
938
- */
939
- export declare class RaterError extends Error {
940
- readonly code: RaterErrorCode;
941
- readonly context?: Readonly<Record<string, unknown>>;
942
- constructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
943
- }
944
-
945
- /** A coded {@link RaterError} programmer-error code. */
946
- export declare type RaterErrorCode = 'DUPLICATE' | 'MISSING' | 'DEFINITION' | 'MISMATCH' | 'DESTROYED';
947
-
948
- /**
949
- * The push observation surface of a {@link RaterInterface} (AGENTS §13).
950
- *
951
- * @remarks
952
- * `rate` fires once per rated subject (batch or single); `aggregate` fires once
953
- * per batch call; `determine` fires per APPLIED determination; `decide` fires
954
- * once a program derives a decision.
955
- */
956
- export declare type RaterEventMap = {
957
- /** A subject was rated — carries its result. */
958
- readonly rate: readonly [result: SubjectResult];
959
- /** A batch was rated — carries the aggregate result. */
960
- readonly aggregate: readonly [result: AggregateResult];
961
- /** An applied determination was produced — carries it. */
962
- readonly determine: readonly [determination: Determination];
963
- /** A program derived a decision — carries the decision and its result. */
964
- readonly decide: readonly [decision: Decision, result: ProgramResult];
965
- };
966
-
967
- /**
968
- * The rating orchestrator over the program manager and the shared reasoning
969
- * engine.
970
- *
971
- * @remarks
972
- * The batch `rate` overload is declared FIRST (AGENTS §9.2) so an array subject
973
- * resolves to the batch form.
974
- */
975
- export declare interface RaterInterface {
976
- readonly emitter: EmitterInterface<RaterEventMap>;
977
- readonly programs: ProgramManagerInterface;
978
- rate(subjects: readonly Subject[]): AggregateResult;
979
- rate(subject: Subject): SubjectResult;
980
- destroy(): void;
981
- }
982
-
983
- /** Options for `createRater` / the `Rater` constructor. */
984
- export declare interface RaterOptions {
985
- readonly on?: EmitterHooks<RaterEventMap>;
986
- readonly error?: EmitterErrorHandler;
987
- readonly total?: TotalHandler;
988
- readonly programs?: readonly ProgramDefinition[];
989
- readonly labels?: Readonly<Record<string, string>>;
990
- readonly validate?: boolean;
991
- }
992
-
993
- /**
994
- * Join a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into
995
- * a {@link Worksheet} — the rating audit trail.
996
- *
997
- * @param definition - The authored quantitative definition
998
- * @param result - The evaluated quantitative result
999
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
1000
- * @returns A fresh worksheet
1001
- */
1002
- export declare function resultsWorksheet(definition: QuantitativeDefinition, result: QuantitativeResult, labels?: Readonly<Record<string, string>>): Worksheet;
166
+ * @remarks
167
+ * `worksheet` is always present — even a failed evaluation resolves to a
168
+ * type-shaped failure worksheet, so a `LineResult` is always constructible.
169
+ * `amount` is present ONLY when `success` is `true`.
170
+ */
171
+ export declare interface LineResult {
172
+ readonly id: string;
173
+ readonly name: string;
174
+ readonly amount?: number;
175
+ readonly worksheet: Worksheet;
176
+ readonly success: boolean;
177
+ }
1003
178
 
1004
- /**
1005
- * Convert fired logical {@link RuleResult}s into line- or program-scoped
1006
- * {@link Determination}s.
1007
- *
1008
- * @param definition - The authored logical definition
1009
- * @param result - The evaluated logical result
1010
- * @param rulings - Authored consequences, keyed by rule id
1011
- * @param working - The working subject the rules ran against
1012
- * @param line - The line id this pass is scoped to, when it is line-scoped
1013
- * @param evaluator - The shared reason check evaluator
1014
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
1015
- * @returns A fresh list of determinations
1016
- */
1017
- export declare function rulesToDeterminations(definition: LogicalDefinition, result: LogicalResult, rulings: Readonly<Record<string, Ruling>> | undefined, working: Subject, line: string | undefined, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): readonly Determination[];
179
+ /** A shared checked-evidence row rendered into a display-neutral sentence. */
180
+ export declare interface Premise {
181
+ readonly field?: FieldPath;
182
+ readonly label?: string;
183
+ readonly comparison?: Comparison;
184
+ readonly expected?: unknown;
185
+ readonly actual?: unknown;
186
+ readonly met?: boolean;
187
+ }
1018
188
 
1019
- /** An authored consequence routed to a fired rule by its id. */
1020
- export declare interface Ruling {
1021
- readonly effect: Effect;
1022
- readonly line?: string;
1023
- readonly message?: string;
1024
- }
189
+ /**
190
+ * Build a {@link Premise} from an evaluated {@link Check}.
191
+ *
192
+ * @param check - The authored check
193
+ * @param actual - The resolved subject value
194
+ * @param met - Whether the check was met (absent when not yet evaluated)
195
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
196
+ * @returns A fresh premise
197
+ *
198
+ * @example
199
+ * ```ts
200
+ * import { check } from '@orkestrel/reason'
201
+ * import { premiseCheck } from '@orkestrel/rater'
202
+ *
203
+ * const entry = check('age', 'above', 18)
204
+ * premiseCheck(entry, 25, true) // { field: 'age', comparison: 'above', expected: 18, actual: 25, met: true }
205
+ * ```
206
+ */
207
+ export declare function premiseCheck(check: Check, actual: unknown, met: boolean | undefined, labels?: Readonly<Record<string, string>>): Premise;
1025
208
 
1026
- /**
1027
- * Build a {@link Ruling}.
1028
- *
1029
- * @param effect - The determination effect a fired rule routes to
1030
- * @param line - The line id this ruling is scoped to, when it is line-scoped
1031
- * @param message - An interpolated message template, when the ruling carries one
1032
- * @returns A fresh ruling
1033
- *
1034
- * @example
1035
- * ```ts
1036
- * import { rulingDefinition } from '@orkestrel/rater'
1037
- *
1038
- * rulingDefinition('restriction', 'base', 'Amount exceeds {{limit}}')
1039
- * ```
1040
- */
1041
- export declare function rulingDefinition(effect: Effect, line?: string, message?: string): Ruling;
209
+ /**
210
+ * Build a rated {@link LineResult} from a line's evaluated
211
+ * {@link QuantitativeResult}.
212
+ *
213
+ * @param definition - The authored line definition
214
+ * @param result - The evaluated quantitative result
215
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
216
+ * @returns A fresh line result
217
+ *
218
+ * @example
219
+ * ```ts
220
+ * import { createQuantitativeReasoner, createReason, quantitativeDefinition } from '@orkestrel/reason'
221
+ * import { lineDefinition, ratedLine } from '@orkestrel/rater'
222
+ *
223
+ * const line = lineDefinition('base', 'Base', quantitativeDefinition('base', 'Base', []))
224
+ * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, line.rate)
225
+ * if (result.reasoning === 'quantitative') ratedLine(line, result) // amount present only when success
226
+ * ```
227
+ */
228
+ export declare function ratedLine(definition: LineDefinition, result: QuantitativeResult, labels?: Readonly<Record<string, string>>): LineResult;
1042
229
 
1043
- /** A worksheet derivation step stage. */
1044
- export declare type Stage = 'factor' | 'group' | 'total';
230
+ /**
231
+ * The rating orchestrator — owns (or receives) the shared quantitative
232
+ * reasoning engine and projects results into the rating domain vocabulary.
233
+ *
234
+ * @remarks
235
+ * When no `engine` is injected, `Rater` builds and OWNS its own
236
+ * quantitative-only engine (`bail: false`) — Rater performs NO evaluation
237
+ * arithmetic of its own; it only orchestrates and projects. The
238
+ * array-of-lines `rate` overload is declared FIRST (AGENTS §9.2). `destroy()`
239
+ * destroys an OWNED engine, then the emitter LAST (AGENTS §13); an INJECTED
240
+ * engine is never destroyed. Afterwards every other method throws
241
+ * {@link RaterError} `'DESTROYED'`.
242
+ *
243
+ * @example
244
+ * ```ts
245
+ * import { Rater } from '@orkestrel/rater'
246
+ *
247
+ * const rater = new Rater()
248
+ * rater.rate([], { id: 'subject-1' }) // { lines: [], success: true }
249
+ * rater.destroy()
250
+ * ```
251
+ */
252
+ export declare class Rater implements RaterInterface {
253
+ #private;
254
+ constructor(options?: RaterOptions);
255
+ get emitter(): EmitterInterface<RaterEventMap>;
256
+ rate(lines: readonly LineDefinition[], subject: Subject): RatingResult;
257
+ rate(definition: RatingDefinition, subject: Subject): RatingResult;
258
+ destroy(): void;
259
+ }
1045
260
 
1046
- /** Presentation and tally status derived from eligibility, conditions, and rating success. */
1047
- export declare type Status = 'ineligible' | 'referral' | 'conditional' | 'unrated' | 'eligible';
261
+ /**
262
+ * A coded programmer error thrown by the rating layer.
263
+ *
264
+ * @remarks
265
+ * `DEFINITION` — the `rate` input failed both the array-of-lines and rating
266
+ * definition validation. `MISMATCH` — a rated subject is not a record.
267
+ * `DESTROYED` — use of a destroyed entity.
268
+ *
269
+ * @example
270
+ * ```ts
271
+ * import { RaterError } from '@orkestrel/rater'
272
+ *
273
+ * throw new RaterError('MISMATCH', 'Subject must be a record')
274
+ * ```
275
+ */
276
+ export declare class RaterError extends Error {
277
+ readonly code: RaterErrorCode;
278
+ readonly context?: Readonly<Record<string, unknown>>;
279
+ constructor(code: RaterErrorCode, message: string, context?: Readonly<Record<string, unknown>>);
280
+ }
1048
281
 
1049
- /** Status tally precedence order — least to most resolved. */
1050
- export declare const STATUS_PRECEDENCE: readonly Status[];
282
+ /** A coded {@link RaterError} programmer-error code. */
283
+ export declare type RaterErrorCode = 'DEFINITION' | 'MISMATCH' | 'DESTROYED';
1051
284
 
1052
- /** A display-neutral worksheet derivation step. */
1053
- export declare interface Step {
1054
- readonly stage: Stage;
1055
- readonly id?: string;
1056
- readonly name?: string;
1057
- readonly value: number;
1058
- readonly expression?: string;
1059
- }
285
+ /**
286
+ * The push observation surface of a {@link RaterInterface} (AGENTS §13).
287
+ *
288
+ * @remarks
289
+ * `rate` fires once per `rate` call, carrying the rated subject and the result.
290
+ */
291
+ export declare type RaterEventMap = {
292
+ /** A subject was rated — carries the subject and its result. */
293
+ readonly rate: readonly [subject: Subject, result: RatingResult];
294
+ };
1060
295
 
1061
- /** One rated subject's outcome across every compiled program. */
1062
- export declare interface SubjectResult {
1063
- readonly subject: Subject;
1064
- readonly programs: readonly ProgramResult[];
1065
- }
296
+ /**
297
+ * The rating orchestrator over the shared quantitative reasoning engine.
298
+ *
299
+ * @remarks
300
+ * The array-of-lines `rate` overload is declared FIRST (AGENTS §9.2). Both
301
+ * overloads rate a SINGLE subject — there is no batch-of-subjects overload.
302
+ */
303
+ export declare interface RaterInterface {
304
+ readonly emitter: EmitterInterface<RaterEventMap>;
305
+ rate(lines: readonly LineDefinition[], subject: Subject): RatingResult;
306
+ rate(definition: RatingDefinition, subject: Subject): RatingResult;
307
+ destroy(): void;
308
+ }
1066
309
 
1067
- /**
1068
- * Sum defined line amounts.
1069
- *
1070
- * @param lines - The rated lines
1071
- * @returns The sum of every line's `amount`, or `undefined` when no line has one
1072
- */
1073
- export declare function sumAmounts(lines: readonly LineResult[]): number | undefined;
310
+ /**
311
+ * Options for `createRater` / the `Rater` constructor.
312
+ *
313
+ * @remarks
314
+ * `engine` — an injected {@link ReasonInterface}; when omitted, `Rater` builds
315
+ * and OWNS its own quantitative-only engine (`bail: false`), destroying it on
316
+ * `destroy()`. `total` — a {@link TotalHandler} overriding the default
317
+ * {@link sumAmounts} projection. `labels` — field-to-label display overrides
318
+ * threaded into every resolved {@link Premise}.
319
+ */
320
+ export declare interface RaterOptions {
321
+ readonly on?: EmitterHooks<RaterEventMap>;
322
+ readonly error?: EmitterErrorHandler;
323
+ readonly engine?: ReasonInterface;
324
+ readonly total?: TotalHandler;
325
+ readonly labels?: Readonly<Record<string, string>>;
326
+ }
1074
327
 
1075
- /** A status tally for one program — a count plus summed aggregate fields. */
1076
- export declare interface Tally {
1077
- readonly count: number;
1078
- readonly sums: Readonly<Record<string, number>>;
1079
- }
328
+ /**
329
+ * A pure authored rating — a named, ordered set of lines.
330
+ *
331
+ * @remarks
332
+ * `rate` accepts either a plain `readonly LineDefinition[]` or a full
333
+ * `RatingDefinition`; the array overload is the terse path, this shape adds
334
+ * an id, a name, and optional metadata for authoring and storage.
335
+ */
336
+ export declare interface RatingDefinition {
337
+ readonly id: string;
338
+ readonly name: string;
339
+ readonly description?: string;
340
+ readonly lines: readonly LineDefinition[];
341
+ readonly metadata?: JSONValue;
342
+ }
1080
343
 
1081
- /**
1082
- * Add one subject's aggregate contribution to a status tally record.
1083
- *
1084
- * @param tallies - The tallies to update
1085
- * @param status - The subject's derived status
1086
- * @param subject - The subject to fold in
1087
- * @param fields - The fields to sum
1088
- * @returns A fresh, complete tally record with the subject folded in
1089
- */
1090
- export declare function tallySubject(tallies: Readonly<Record<Status, Tally>>, status: Status, subject: Subject, fields: readonly FieldPath[]): Readonly<Record<Status, Tally>>;
344
+ /**
345
+ * Build a {@link RatingDefinition}.
346
+ *
347
+ * @param id - The rating id
348
+ * @param name - The display name
349
+ * @param lines - The rating's ordered lines
350
+ * @param overrides - Optional {@link RatingDefinition} fields merged over the defaults
351
+ * @returns A fresh rating definition
352
+ *
353
+ * @example
354
+ * ```ts
355
+ * import { lineDefinition, ratingDefinition } from '@orkestrel/rater'
356
+ * import { quantitativeDefinition } from '@orkestrel/reason'
357
+ *
358
+ * ratingDefinition('r1', 'Rating', [
359
+ * lineDefinition('base', 'Base Amount', quantitativeDefinition('base', 'Base', [])),
360
+ * ])
361
+ * ```
362
+ */
363
+ export declare function ratingDefinition(id: string, name: string, lines: readonly LineDefinition[], overrides?: Partial<Omit<RatingDefinition, 'id' | 'name' | 'lines'>>): RatingDefinition;
1091
364
 
1092
- /** A pure total port over resolved lines. */
1093
- export declare type TotalHandler = (lines: readonly LineResult[]) => number | undefined;
365
+ /**
366
+ * A rated outcome across every line of one `rate` call.
367
+ *
368
+ * @remarks
369
+ * `total` is derived by the {@link TotalHandler} (default {@link sumAmounts})
370
+ * over `lines` — only successfully rated lines carry an `amount`. `success`
371
+ * is `true` only when every line succeeded.
372
+ */
373
+ export declare interface RatingResult {
374
+ readonly lines: readonly LineResult[];
375
+ readonly total?: number;
376
+ readonly success: boolean;
377
+ }
1094
378
 
1095
- /** A quantitative definition joined to its result — the rating audit trail. */
1096
- export declare interface Worksheet {
1097
- readonly id: string;
1098
- readonly name: string;
1099
- readonly aggregation: Aggregation;
1100
- readonly precision?: number;
1101
- readonly value: number;
1102
- readonly groups: readonly WorksheetGroup[];
1103
- readonly steps: readonly Step[];
1104
- readonly trace: readonly string[];
1105
- readonly errors: readonly string[];
1106
- readonly success: boolean;
1107
- }
379
+ /**
380
+ * Join a {@link QuantitativeDefinition} and its {@link QuantitativeResult} into
381
+ * a {@link Worksheet} — the rating audit trail.
382
+ *
383
+ * @param definition - The authored quantitative definition
384
+ * @param result - The evaluated quantitative result
385
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
386
+ * @returns A fresh worksheet
387
+ *
388
+ * @example
389
+ * ```ts
390
+ * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
391
+ * import { resultsWorksheet } from '@orkestrel/rater'
392
+ *
393
+ * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])
394
+ * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
395
+ * if (result.reasoning === 'quantitative') resultsWorksheet(definition, result) // the full audit trail
396
+ * ```
397
+ */
398
+ export declare function resultsWorksheet(definition: QuantitativeDefinition, result: QuantitativeResult, labels?: Readonly<Record<string, string>>): Worksheet;
399
+
400
+ /** A worksheet derivation step stage. */
401
+ export declare type Stage = 'factor' | 'group' | 'total';
402
+
403
+ /** A display-neutral worksheet derivation step. */
404
+ export declare interface Step {
405
+ readonly stage: Stage;
406
+ readonly id?: string;
407
+ readonly name?: string;
408
+ readonly value: number;
409
+ readonly expression?: string;
410
+ }
1108
411
 
1109
- /** A resolved quantitative factor, joined to its authored metadata. */
1110
- export declare interface WorksheetFactor {
1111
- readonly id: string;
1112
- readonly name?: string;
1113
- readonly description?: string;
1114
- readonly applied: boolean;
1115
- readonly value?: number;
1116
- readonly premises: readonly Premise[];
1117
- }
412
+ /**
413
+ * Sum defined line amounts.
414
+ *
415
+ * @param lines - The rated lines
416
+ * @returns The sum of every line's `amount`, or `undefined` when no line has one
417
+ *
418
+ * @example
419
+ * ```ts
420
+ * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
421
+ * import { lineDefinition, ratedLine, sumAmounts } from '@orkestrel/rater'
422
+ *
423
+ * const rate = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])
424
+ * const line = lineDefinition('base', 'Base', rate)
425
+ * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, rate)
426
+ * if (result.reasoning === 'quantitative') sumAmounts([ratedLine(line, result)]) // 100
427
+ * ```
428
+ */
429
+ export declare function sumAmounts(lines: readonly LineResult[]): number | undefined;
430
+
431
+ /** A pure total port over resolved lines. */
432
+ export declare type TotalHandler = (lines: readonly LineResult[]) => number | undefined;
433
+
434
+ /** A quantitative definition joined to its result — the rating audit trail. */
435
+ export declare interface Worksheet {
436
+ readonly id: string;
437
+ readonly name: string;
438
+ readonly aggregation: Aggregation;
439
+ readonly precision?: number;
440
+ readonly value: number;
441
+ readonly groups: readonly WorksheetGroup[];
442
+ readonly steps: readonly Step[];
443
+ readonly trace: readonly string[];
444
+ readonly errors: readonly string[];
445
+ readonly success: boolean;
446
+ }
1118
447
 
1119
- /**
1120
- * Join one authored quantitative factor to its evaluated {@link FactorResult}.
1121
- *
1122
- * @param definition - The authored factor
1123
- * @param results - The group's factor results
1124
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
1125
- * @returns A fresh worksheet factor
1126
- */
1127
- export declare function worksheetFactor(definition: Factor, results: readonly FactorResult[], labels?: Readonly<Record<string, string>>): WorksheetFactor;
448
+ /** A resolved quantitative factor, joined to its authored metadata. */
449
+ export declare interface WorksheetFactor {
450
+ readonly id: string;
451
+ readonly name?: string;
452
+ readonly description?: string;
453
+ readonly applied: boolean;
454
+ readonly value?: number;
455
+ readonly premises: readonly Premise[];
456
+ }
1128
457
 
1129
- /** A resolved quantitative group, joined to its authored metadata. */
1130
- export declare interface WorksheetGroup {
1131
- readonly id: string;
1132
- readonly name?: string;
1133
- readonly description?: string;
1134
- readonly applied: boolean;
1135
- readonly value: number;
1136
- readonly factors: readonly WorksheetFactor[];
1137
- }
458
+ /**
459
+ * Join one authored quantitative factor to its evaluated {@link FactorResult}.
460
+ *
461
+ * @param definition - The authored factor
462
+ * @param results - The group's factor results
463
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
464
+ * @returns A fresh worksheet factor
465
+ *
466
+ * @example
467
+ * ```ts
468
+ * import { fieldFactor } from '@orkestrel/reason'
469
+ * import { worksheetFactor } from '@orkestrel/rater'
470
+ *
471
+ * const factor = fieldFactor('seats', 'seats')
472
+ * worksheetFactor(factor, [{ id: 'seats', applied: true, value: 10 }])
473
+ * ```
474
+ */
475
+ export declare function worksheetFactor(definition: Factor, results: readonly FactorResult[], labels?: Readonly<Record<string, string>>): WorksheetFactor;
476
+
477
+ /** A resolved quantitative group, joined to its authored metadata. */
478
+ export declare interface WorksheetGroup {
479
+ readonly id: string;
480
+ readonly name?: string;
481
+ readonly description?: string;
482
+ readonly applied: boolean;
483
+ readonly value: number;
484
+ readonly factors: readonly WorksheetFactor[];
485
+ }
1138
486
 
1139
- /**
1140
- * Join one authored quantitative group to its evaluated {@link GroupResult}.
1141
- *
1142
- * @param definition - The authored group
1143
- * @param results - The definition's group results
1144
- * @param labels - Optional field-to-label overrides, keyed by dot-joined field
1145
- * @returns A fresh worksheet group
1146
- */
1147
- export declare function worksheetGroup(definition: QuantitativeDefinition['groups'][number], results: readonly GroupResult[], labels?: Readonly<Record<string, string>>): WorksheetGroup;
487
+ /**
488
+ * Join one authored quantitative group to its evaluated {@link GroupResult}.
489
+ *
490
+ * @param definition - The authored group
491
+ * @param results - The definition's group results
492
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
493
+ * @returns A fresh worksheet group
494
+ *
495
+ * @example
496
+ * ```ts
497
+ * import { factorGroup, staticFactor } from '@orkestrel/reason'
498
+ * import { worksheetGroup } from '@orkestrel/rater'
499
+ *
500
+ * const group = factorGroup('charge', 'sum', [staticFactor('base', 100)])
501
+ * worksheetGroup(group, [{ id: 'charge', applied: true, value: 100, factors: [] }])
502
+ * ```
503
+ */
504
+ export declare function worksheetGroup(definition: QuantitativeDefinition['groups'][number], results: readonly GroupResult[], labels?: Readonly<Record<string, string>>): WorksheetGroup;
1148
505
 
1149
- /**
1150
- * Build one display-neutral {@link Step} row.
1151
- *
1152
- * @param stage - The step's stage
1153
- * @param id - The stage's authored id, when it has one
1154
- * @param name - The stage's authored name, when it has one
1155
- * @param value - The stage's resolved value
1156
- * @param expression - A display-neutral expression string
1157
- * @returns A fresh step
1158
- */
1159
- export declare function worksheetStep(stage: Stage, id: string | undefined, name: string | undefined, value: number, expression: string): Step;
506
+ /**
507
+ * Build one display-neutral {@link Step} row.
508
+ *
509
+ * @param stage - The step's stage
510
+ * @param id - The stage's authored id, when it has one
511
+ * @param name - The stage's authored name, when it has one
512
+ * @param value - The stage's resolved value
513
+ * @param expression - A display-neutral expression string
514
+ * @returns A fresh step
515
+ *
516
+ * @example
517
+ * ```ts
518
+ * import { worksheetStep } from '@orkestrel/rater'
519
+ *
520
+ * worksheetStep('total', 'quote', 'Quote', 110, 'sum = 110')
521
+ * // { stage: 'total', id: 'quote', name: 'Quote', value: 110, expression: 'sum = 110' }
522
+ * ```
523
+ */
524
+ export declare function worksheetStep(stage: Stage, id: string | undefined, name: string | undefined, value: number, expression: string): Step;
1160
525
 
1161
- /**
1162
- * Build the ordered {@link Step} rows for a resolved {@link Worksheet}.
1163
- *
1164
- * @param definition - The authored quantitative definition
1165
- * @param result - The evaluated quantitative result
1166
- * @param groups - The definition's already-joined worksheet groups
1167
- * @returns A fresh, ordered list of steps: applied factors, then groups, then the total
1168
- */
1169
- export declare function worksheetSteps(definition: QuantitativeDefinition, result: QuantitativeResult, groups: readonly WorksheetGroup[]): readonly Step[];
526
+ /**
527
+ * Build the ordered {@link Step} rows for a resolved {@link Worksheet}.
528
+ *
529
+ * @param definition - The authored quantitative definition
530
+ * @param result - The evaluated quantitative result
531
+ * @param groups - The definition's already-joined worksheet groups
532
+ * @returns A fresh, ordered list of steps: applied factors, then groups, then the total
533
+ *
534
+ * @example
535
+ * ```ts
536
+ * import { createQuantitativeReasoner, createReason, factorGroup, quantitativeDefinition, staticFactor } from '@orkestrel/reason'
537
+ * import { worksheetGroup, worksheetSteps } from '@orkestrel/rater'
538
+ *
539
+ * const definition = quantitativeDefinition('base', 'Base', [factorGroup('g', 'sum', [staticFactor('flat', 100)])])
540
+ * const result = createReason({ reasoners: [createQuantitativeReasoner()] }).reason({}, definition)
541
+ * if (result.reasoning === 'quantitative') {
542
+ * worksheetSteps(definition, result, definition.groups.map((group) => worksheetGroup(group, result.groups)))
543
+ * }
544
+ * ```
545
+ */
546
+ export declare function worksheetSteps(definition: QuantitativeDefinition, result: QuantitativeResult, groups: readonly WorksheetGroup[]): readonly Step[];
1170
547
 
1171
- export { }
548
+ export { }