@orkestrel/program 0.0.1

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.
@@ -0,0 +1,1137 @@
1
+ import { Eligibility } from '@orkestrel/qualifier';
2
+ import { EmitterErrorHandler } from '@orkestrel/emitter';
3
+ import { EmitterHooks } from '@orkestrel/emitter';
4
+ import { EmitterInterface } from '@orkestrel/emitter';
5
+ import { EvaluatorInterface } from '@orkestrel/reason';
6
+ import { FieldPath } from '@orkestrel/contract';
7
+ import { Guard } from '@orkestrel/contract';
8
+ import { JSONValue } from '@orkestrel/contract';
9
+ import { LineDefinition } from '@orkestrel/rater';
10
+ import { LogicalDefinition } from '@orkestrel/reason';
11
+ import { LogicalResult } from '@orkestrel/reason';
12
+ import { Premise } from '@orkestrel/qualifier';
13
+ import { QualificationDefinition } from '@orkestrel/qualifier';
14
+ import { QualificationResult } from '@orkestrel/qualifier';
15
+ import { QualifierInterface } from '@orkestrel/qualifier';
16
+ import { RaterInterface } from '@orkestrel/rater';
17
+ import { RatingDefinition } from '@orkestrel/rater';
18
+ import { RatingResult } from '@orkestrel/rater';
19
+ import { ReasonInterface } from '@orkestrel/reason';
20
+ import { Subject } from '@orkestrel/reason';
21
+
22
+ /** The reserved working-subject key a batch's aggregate projection is written under. */
23
+ export declare const AGGREGATE_KEY = "aggregate";
24
+
25
+ /** Batch aggregate fields, an optional partition key, and optional gates. */
26
+ export declare interface AggregateDefinition {
27
+ readonly fields: readonly FieldPath[];
28
+ readonly by?: FieldPath;
29
+ readonly gates?: LogicalDefinition;
30
+ }
31
+
32
+ /**
33
+ * Build an {@link AggregateDefinition}.
34
+ *
35
+ * @param fields - The aggregate fields to sum across a batch
36
+ * @param input - Optional partition field and aggregate gates
37
+ * @returns A fresh aggregate definition
38
+ *
39
+ * @example
40
+ * ```ts
41
+ * import { aggregateDefinition } from '@orkestrel/program'
42
+ *
43
+ * aggregateDefinition(['amount'], { by: 'location' })
44
+ * ```
45
+ */
46
+ export declare function aggregateDefinition(fields: readonly FieldPath[], input?: AggregateInput): AggregateDefinition;
47
+
48
+ /** One batch aggregate partition. */
49
+ export declare interface AggregateGroup {
50
+ readonly key: string;
51
+ readonly count: number;
52
+ readonly sums: Readonly<Record<string, number>>;
53
+ }
54
+
55
+ /**
56
+ * Partition a batch of subjects by a field, summing aggregate fields per key.
57
+ *
58
+ * @remarks
59
+ * The partition key is derived by {@link formatGroupKey}. Group order follows
60
+ * first appearance in the subject array.
61
+ *
62
+ * @param subjects - The batch of subjects
63
+ * @param fields - The fields to sum within each partition
64
+ * @param by - The partition key field; no partition is built when absent
65
+ * @returns A fresh list of aggregate groups, or an empty list when `by` is absent
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * import { aggregateGroups } from '@orkestrel/program'
70
+ *
71
+ * aggregateGroups([{ location: 'east', amount: 5 }], ['amount'], 'location')
72
+ * ```
73
+ */
74
+ export declare function aggregateGroups(subjects: readonly Subject[], fields: readonly FieldPath[], by?: FieldPath): readonly AggregateGroup[];
75
+
76
+ /**
77
+ * Optional fields accepted by `aggregateDefinition`.
78
+ *
79
+ * @remarks
80
+ * `by` — the partition key field; omitted skips partitioning. `gates` — a
81
+ * logical definition evaluated once over the whole batch to derive `limit`
82
+ * determinations.
83
+ */
84
+ export declare interface AggregateInput {
85
+ readonly by?: FieldPath;
86
+ readonly gates?: LogicalDefinition;
87
+ }
88
+
89
+ /** One subject's private aggregate working projection. */
90
+ export declare interface AggregateProjection {
91
+ readonly count: number;
92
+ readonly sums: Readonly<Record<string, number>>;
93
+ readonly group?: AggregateGroup;
94
+ }
95
+
96
+ /** A batch program outcome across every subject. */
97
+ export declare interface AggregateResult {
98
+ readonly id: string;
99
+ readonly name: string;
100
+ readonly subjects: readonly ProgramResult[];
101
+ readonly determinations: readonly Determination[];
102
+ readonly groups: readonly AggregateGroup[];
103
+ readonly tallies: Readonly<Record<Status, Tally>>;
104
+ readonly count: number;
105
+ readonly sums: Readonly<Record<string, number>>;
106
+ readonly success: boolean;
107
+ readonly trace: readonly string[];
108
+ readonly errors: readonly string[];
109
+ }
110
+
111
+ /**
112
+ * Sum aggregate fields across a batch of subjects.
113
+ *
114
+ * @remarks
115
+ * A {@link FieldPath} may be nested — a nested path sums a nested subject field
116
+ * exactly like a top-level one, and `formatField` renders the dot-joined key the
117
+ * returned record is keyed by. Only finite numbers contribute; a non-numeric or
118
+ * absent value contributes zero (never a coercion).
119
+ *
120
+ * @param subjects - The batch of subjects
121
+ * @param fields - The fields to sum
122
+ * @returns A fresh record of dot-joined field to summed finite value
123
+ *
124
+ * @example
125
+ * ```ts
126
+ * import { aggregateSums } from '@orkestrel/program'
127
+ *
128
+ * aggregateSums([{ amount: 5 }, { amount: 3 }], ['amount']) // { amount: 8 }
129
+ * ```
130
+ */
131
+ export declare function aggregateSums(subjects: readonly Subject[], fields: readonly FieldPath[]): Readonly<Record<string, number>>;
132
+
133
+ /**
134
+ * Assert a program definition's always-on construction invariants — missing
135
+ * scope references and duplicate rating-line or notice ids.
136
+ *
137
+ * @remarks
138
+ * These checks run at construction regardless of `options.validate` (unlike
139
+ * {@link validateProgramDefinition}, the standalone report-shaped validator) —
140
+ * an authoring mistake this severe cannot silently compile.
141
+ *
142
+ * @param definition - The program definition to assert
143
+ * @throws {@link ProgramError} `'MISSING'` when a ruling or notice scope names
144
+ * no rating line
145
+ * @throws {@link ProgramError} `'DUPLICATE'` when two rating lines or two
146
+ * notices share an id
147
+ *
148
+ * @example
149
+ * ```ts
150
+ * import { assertProgramDefinition } from '@orkestrel/program'
151
+ *
152
+ * assertProgramDefinition(definition) // does not throw
153
+ * ```
154
+ */
155
+ export declare function assertProgramDefinition(definition: ProgramDefinition): void;
156
+
157
+ /**
158
+ * Assert a value is a valid program {@link Subject}, narrowing it in place.
159
+ *
160
+ * @param subject - The candidate subject to validate
161
+ * @throws {@link ProgramError} `'MISMATCH'` when the value is not a record, or
162
+ * `'RESERVED'` when it already carries the `aggregate` or `outcome` key
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * import { assertProgramSubject } from '@orkestrel/program'
167
+ *
168
+ * assertProgramSubject({ id: 'r1' }) // does not throw
169
+ * ```
170
+ */
171
+ export declare function assertProgramSubject(subject: unknown): asserts subject is Subject;
172
+
173
+ /**
174
+ * Build one subject's overall and optional group aggregate projection.
175
+ *
176
+ * @remarks
177
+ * The projection carries the whole-batch `count` and `sums` plus the subject's
178
+ * OWN partition, located by the same {@link formatGroupKey} key
179
+ * {@link aggregateGroups} partitions under.
180
+ *
181
+ * @param subject - The subject to project for
182
+ * @param count - The whole-batch subject count
183
+ * @param sums - The whole-batch summed aggregate fields
184
+ * @param groups - The batch partitions
185
+ * @param by - The partition key field; no group is attached when absent
186
+ * @returns A fresh aggregate projection
187
+ *
188
+ * @example
189
+ * ```ts
190
+ * import { buildAggregateProjection } from '@orkestrel/program'
191
+ *
192
+ * buildAggregateProjection(subject, 2, { amount: 8 }, groups, 'location')
193
+ * ```
194
+ */
195
+ export declare function buildAggregateProjection(subject: Subject, count: number, sums: Readonly<Record<string, number>>, groups: readonly AggregateGroup[], by?: FieldPath): AggregateProjection;
196
+
197
+ /**
198
+ * Build the reserved-key record a batch aggregate-gate definition runs against.
199
+ *
200
+ * @remarks
201
+ * Unlike a per-subject {@link buildAggregateProjection}, the batch record carries
202
+ * every `group` (a `groups` array) under {@link AGGREGATE_KEY} so a gate rule can
203
+ * read `aggregate.sums.<field>` (overall) or a partition inside `aggregate.groups`.
204
+ *
205
+ * @param count - The whole-batch subject count
206
+ * @param sums - The whole-batch summed aggregate fields
207
+ * @param groups - The batch partitions
208
+ * @returns A fresh record carrying the batch aggregate under {@link AGGREGATE_KEY}
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * import { buildAggregateRecord } from '@orkestrel/program'
213
+ *
214
+ * buildAggregateRecord(2, { amount: 8 }, [])
215
+ * ```
216
+ */
217
+ export declare function buildAggregateRecord(count: number, sums: Readonly<Record<string, number>>, groups: readonly AggregateGroup[]): Readonly<Record<string, unknown>>;
218
+
219
+ /**
220
+ * Assemble one batch {@link AggregateResult} from its per-subject and aggregate
221
+ * parts.
222
+ *
223
+ * @remarks
224
+ * `count` is the subject count, `trace` / `errors` accumulate every subject's
225
+ * plus the batch aggregate-gate evaluation's (`options.gates`), and `success`
226
+ * requires every subject execution to succeed AND the gate evaluation to have
227
+ * produced no errors. A fired aggregate gate contributes a `limit`
228
+ * determination, never a technical failure (a non-logical gate result is a
229
+ * caller-facing `MISMATCH` thrown by `Program` before this assembles).
230
+ *
231
+ * @param definition - The authored program definition
232
+ * @param subjects - The per-subject program results, in input order
233
+ * @param determinations - The batch aggregate-gate `limit` determinations
234
+ * @param groups - The batch partitions
235
+ * @param tallies - The completed status tallies
236
+ * @param sums - The whole-batch summed aggregate fields
237
+ * @param options - Optional resolved aggregate-gate result
238
+ * @returns A fresh aggregate result
239
+ *
240
+ * @example
241
+ * ```ts
242
+ * import { buildAggregateResult } from '@orkestrel/program'
243
+ *
244
+ * buildAggregateResult(definition, subjects, [], [], tallies, { amount: 8 })
245
+ * ```
246
+ */
247
+ export declare function buildAggregateResult(definition: ProgramDefinition, subjects: readonly ProgramResult[], determinations: readonly Determination[], groups: readonly AggregateGroup[], tallies: Readonly<Record<Status, Tally>>, sums: Readonly<Record<string, number>>, options?: {
248
+ readonly gates?: LogicalResult;
249
+ }): AggregateResult;
250
+
251
+ /**
252
+ * Convert a logical result's applied rules into `limit` {@link Determination}s.
253
+ *
254
+ * @remarks
255
+ * Fires for both the per-subject authority and the batch aggregate gates — both
256
+ * are plain {@link LogicalDefinition}s with no program-authored ruling map, so a
257
+ * fired rule's own `description` (from `@orkestrel/reason`) is the message
258
+ * template, interpolated against the working record the definition ran against.
259
+ * Rich premises reuse the qualifier's {@link logicalPremises}. A rule that never
260
+ * fires produces no determination — program has no authored ruling map to keep
261
+ * evidence for.
262
+ *
263
+ * @param definition - The authority or aggregate-gate logical definition
264
+ * @param result - The evaluated logical result
265
+ * @param working - The working record the definition ran against
266
+ * @param evaluator - The shared reason check evaluator
267
+ * @param labels - Optional field-to-label overrides, keyed by dot-joined field
268
+ * @returns A fresh list of `limit` determinations
269
+ *
270
+ * @example
271
+ * ```ts
272
+ * import { buildLimits } from '@orkestrel/program'
273
+ *
274
+ * buildLimits(authority, resolved, outcome, evaluator)
275
+ * ```
276
+ */
277
+ export declare function buildLimits(definition: LogicalDefinition, result: LogicalResult, working: Readonly<Record<string, unknown>>, evaluator: EvaluatorInterface, labels?: Readonly<Record<string, string>>): readonly Determination[];
278
+
279
+ /**
280
+ * Resolve authored {@link Notice}s into unconditionally-applied `notice`
281
+ * {@link Determination}s.
282
+ *
283
+ * @remarks
284
+ * Notices are program output only — they never affect eligibility, status, line
285
+ * selection, or the decision. Each message interpolates against the original
286
+ * subject.
287
+ *
288
+ * @param notices - The authored notices
289
+ * @param subject - The original subject notices interpolate against
290
+ * @returns A fresh list of notice determinations
291
+ *
292
+ * @example
293
+ * ```ts
294
+ * import { buildNotices } from '@orkestrel/program'
295
+ *
296
+ * buildNotices([{ id: 'min', message: 'Minimum applies' }], { id: 'r1' })
297
+ * ```
298
+ */
299
+ export declare function buildNotices(notices: readonly Notice[], subject: Readonly<Record<string, unknown>>): readonly Determination[];
300
+
301
+ /**
302
+ * Build the private authority outcome projection from an assembled program result.
303
+ *
304
+ * @remarks
305
+ * The authority reads this record under {@link OUTCOME_KEY}; it never receives
306
+ * the mutable internal state of either sibling engine. `total` is carried from
307
+ * the nested rating result when rating occurred.
308
+ *
309
+ * @param result - The preliminary program result computed before authority runs
310
+ * @returns A record shaped for the authority's `outcome` projection
311
+ *
312
+ * @example
313
+ * ```ts
314
+ * import { buildOutcomeProjection } from '@orkestrel/program'
315
+ *
316
+ * buildOutcomeProjection(result) // { id, eligibility, status, rated, scopes }
317
+ * ```
318
+ */
319
+ export declare function buildOutcomeProjection(result: ProgramResult): Readonly<Record<string, unknown>>;
320
+
321
+ /**
322
+ * Assemble a {@link ProgramResult} from its qualification, rating, and
323
+ * determination parts — before or after authority.
324
+ *
325
+ * @remarks
326
+ * `eligibility` mirrors the qualification. `success` is execution integrity: the
327
+ * qualification succeeded, rating (when it ran) succeeded, and authority (when it
328
+ * ran) produced no errors — a valid ineligible or referral outcome still
329
+ * succeeds. `trace` and `errors` accumulate the qualification's, every rated
330
+ * line's worksheet trail, and the authority's. A `decision` is present ONLY when
331
+ * an authority ran (`options.authority`), the execution SUCCEEDED (`success`),
332
+ * no `limit` determination applied, and status is not `unrated`.
333
+ *
334
+ * @param definition - The authored program definition
335
+ * @param qualification - The subject's qualification result
336
+ * @param rating - The subject's rating result, when rating occurred
337
+ * @param determinations - The program-scoped determinations (notices, then limits)
338
+ * @param status - The already-derived status
339
+ * @param options - Optional authority result driving the decision projection
340
+ * @returns A fresh program result
341
+ *
342
+ * @example
343
+ * ```ts
344
+ * import { buildProgramResult } from '@orkestrel/program'
345
+ *
346
+ * buildProgramResult(definition, qualification, rating, [], 'eligible')
347
+ * ```
348
+ */
349
+ export declare function buildProgramResult(definition: ProgramDefinition, qualification: QualificationResult, rating: RatingResult | undefined, determinations: readonly Determination[], status: Status, options?: {
350
+ readonly authority?: LogicalResult;
351
+ }): ProgramResult;
352
+
353
+ /**
354
+ * Add optional aggregate context to a private subject copy for qualification.
355
+ *
356
+ * @remarks
357
+ * The original subject is returned unchanged when no aggregate context exists.
358
+ * When context exists the helper creates a private copy under {@link AGGREGATE_KEY}
359
+ * and defensively copies every nested record — the rater still receives the
360
+ * original subject, never this copy.
361
+ *
362
+ * @param subject - The original caller subject
363
+ * @param aggregate - The subject's aggregate projection, when a batch supplies one
364
+ * @returns The subject, or a private copy carrying the aggregate projection
365
+ *
366
+ * @example
367
+ * ```ts
368
+ * import { buildQualificationSubject } from '@orkestrel/program'
369
+ *
370
+ * buildQualificationSubject({ id: 'r1' }) // { id: 'r1' }
371
+ * ```
372
+ */
373
+ export declare function buildQualificationSubject(subject: Subject, aggregate?: AggregateProjection): Subject;
374
+
375
+ /**
376
+ * Complete a partial status tally record with zero entries for every missing
377
+ * {@link Status}.
378
+ *
379
+ * @param entries - The partial tally entries to complete
380
+ * @returns A record with all five statuses present
381
+ *
382
+ * @example
383
+ * ```ts
384
+ * import { completeTallies } from '@orkestrel/program'
385
+ *
386
+ * completeTallies({ eligible: { count: 1, sums: {} } })
387
+ * ```
388
+ */
389
+ export declare function completeTallies(entries: Partial<Record<Status, Tally>>): Readonly<Record<Status, Tally>>;
390
+
391
+ /**
392
+ * Return a fresh JSON value tree that does not alias the input.
393
+ *
394
+ * @remarks
395
+ * The input must be an acyclic JSON tree of bounded depth — a pathologically
396
+ * deep tree throws the engine's `RangeError` (stack exhaustion) rather than
397
+ * hanging. Each copied record uses `Object.defineProperty` for own-property
398
+ * definition, which defends against prototype-pollution keys (`__proto__`).
399
+ *
400
+ * @param value - The JSON value to copy
401
+ * @returns A fresh JSON value
402
+ *
403
+ * @example
404
+ * ```ts
405
+ * import { copyJSONValue } from '@orkestrel/program'
406
+ *
407
+ * copyJSONValue({ a: [1, 2] }) // { a: [1, 2] }, a fresh copy
408
+ * ```
409
+ */
410
+ export declare function copyJSONValue(value: JSONValue): JSONValue;
411
+
412
+ /**
413
+ * Create one compiled program over a qualifier and rater.
414
+ *
415
+ * @remarks
416
+ * Validates the definition at construction when `options.validate` is left at
417
+ * its {@link DEFAULT_PROGRAM_VALIDATE} default. A standalone program creates and
418
+ * OWNS one shared quantitative-plus-logical reason engine and injects it into the
419
+ * qualifier and rater it creates; injected dependencies remain caller-owned.
420
+ *
421
+ * @param definition - The authored program definition
422
+ * @param options - Optional injected qualifier, rater, engine, validation, labels, and emitter hooks
423
+ * @returns A {@link ProgramInterface}
424
+ *
425
+ * @example
426
+ * ```ts
427
+ * import { createProgram, programDefinition } from '@orkestrel/program'
428
+ *
429
+ * const program = createProgram(programDefinition('standard', 'Standard', qualification, rating))
430
+ * program.execute({ id: 'risk-1' })
431
+ * program.destroy()
432
+ * ```
433
+ */
434
+ export declare function createProgram(definition: ProgramDefinition, options?: ProgramOptions): ProgramInterface;
435
+
436
+ /**
437
+ * Create one ordered manager over compiled programs.
438
+ *
439
+ * @remarks
440
+ * Creates or borrows one shared reason engine, qualifier, and rater and injects
441
+ * them into every compiled program, so a batch of definitions shares one engine.
442
+ * Seed definitions are compiled in order.
443
+ *
444
+ * @param options - Optional injected qualifier, rater, engine, seed programs, validation, labels, and emitter hooks
445
+ * @returns A {@link ProgramManagerInterface}
446
+ *
447
+ * @example
448
+ * ```ts
449
+ * import { createProgramManager } from '@orkestrel/program'
450
+ *
451
+ * const manager = createProgramManager({ programs: [definition] })
452
+ * manager.program('standard')?.execute(subject)
453
+ * manager.destroy()
454
+ * ```
455
+ */
456
+ export declare function createProgramManager(options?: ProgramManagerOptions): ProgramManagerInterface;
457
+
458
+ /**
459
+ * Map a global {@link Eligibility} to its deterministic authority {@link Decision}.
460
+ *
461
+ * @param eligibility - The global eligibility
462
+ * @returns The matching decision
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * import { decideEligibility } from '@orkestrel/program'
467
+ *
468
+ * decideEligibility('eligible') // 'approved'
469
+ * decideEligibility('referral') // 'submitted'
470
+ * ```
471
+ */
472
+ export declare function decideEligibility(eligibility: Eligibility): Decision;
473
+
474
+ /** A final authority outcome, derived from global eligibility. */
475
+ export declare type Decision = 'approved' | 'denied' | 'submitted';
476
+
477
+ /** Default definition validation policy for `createProgram` / `ProgramManager.add`. */
478
+ export declare const DEFAULT_PROGRAM_VALIDATE = true;
479
+
480
+ /**
481
+ * Derive the final program {@link Status} from a definition's rating policy and
482
+ * qualification/rating evidence.
483
+ *
484
+ * @remarks
485
+ * Explicit policy, not an opaque precedence reduce (AGENTS §10): global
486
+ * ineligibility or referral is terminal; a scoped referral yields `referral`;
487
+ * an applied `condition` or an applied scoped `restriction` (a line was
488
+ * removed but others rated) is `conditional`. When the definition OMITS
489
+ * `rating` the program is eligibility-only — status resolves to `conditional`
490
+ * or `eligible` and is NEVER `unrated`. Otherwise a subject with no successful
491
+ * rating is `unrated`.
492
+ *
493
+ * @param definition - The authored program definition
494
+ * @param qualification - The subject's qualification result
495
+ * @param rating - The subject's rating result, when rating occurred
496
+ * @returns The derived status
497
+ *
498
+ * @example
499
+ * ```ts
500
+ * import { deriveStatus } from '@orkestrel/program'
501
+ *
502
+ * deriveStatus(definition, qualification, rating) // 'eligible'
503
+ * ```
504
+ */
505
+ export declare function deriveStatus(definition: ProgramDefinition, qualification: QualificationResult, rating?: RatingResult): Status;
506
+
507
+ /** One resolved notice or authority-limit outcome. */
508
+ export declare interface Determination {
509
+ readonly id: string;
510
+ readonly effect: ProgramEffect;
511
+ readonly applied: boolean;
512
+ readonly scope?: string;
513
+ readonly message?: string;
514
+ readonly premises: readonly Premise[];
515
+ }
516
+
517
+ /** The deterministic authority decision for each global eligibility. */
518
+ export declare const ELIGIBILITY_DECISIONS: Readonly<Record<Eligibility, Decision>>;
519
+
520
+ /**
521
+ * Build a zero-sum record for a set of aggregate fields.
522
+ *
523
+ * @param fields - The fields to zero
524
+ * @returns A fresh record of dot-joined field to `0`
525
+ *
526
+ * @example
527
+ * ```ts
528
+ * import { emptySums } from '@orkestrel/program'
529
+ *
530
+ * emptySums(['amount']) // { amount: 0 }
531
+ * ```
532
+ */
533
+ export declare function emptySums(fields: readonly FieldPath[]): Readonly<Record<string, number>>;
534
+
535
+ /**
536
+ * Build complete zero status tallies in {@link STATUS_PRECEDENCE} order.
537
+ *
538
+ * @param fields - The fields each tally's sums are zeroed for
539
+ * @returns A fresh, complete tally record
540
+ *
541
+ * @example
542
+ * ```ts
543
+ * import { emptyTallies } from '@orkestrel/program'
544
+ *
545
+ * emptyTallies(['amount'])
546
+ * ```
547
+ */
548
+ export declare function emptyTallies(fields: readonly FieldPath[]): Readonly<Record<Status, Tally>>;
549
+
550
+ /**
551
+ * Return authored scopes (qualification ruling scopes or notice scopes) that
552
+ * name no rating line on the program.
553
+ *
554
+ * @remarks
555
+ * A scope is an opaque string to the qualifier — program alone matches it to a
556
+ * rating-line id. A scope naming no line is a hard authoring error surfaced as
557
+ * {@link ProgramError} `'MISSING'` at construction, regardless of the validate
558
+ * option.
559
+ *
560
+ * @param definition - The program definition to check
561
+ * @returns A fresh, deduped list of missing scope references
562
+ *
563
+ * @example
564
+ * ```ts
565
+ * import { findMissingScopes } from '@orkestrel/program'
566
+ *
567
+ * findMissingScopes(definition) // []
568
+ * ```
569
+ */
570
+ export declare function findMissingScopes(definition: ProgramDefinition): readonly string[];
571
+
572
+ /**
573
+ * Coerce a subject's partition-key field to its group-key string.
574
+ *
575
+ * @remarks
576
+ * The key is the resolved field coerced with `String` — `undefined` collapses
577
+ * to the empty string, so a subject missing the field and a subject whose
578
+ * field is literally `''` land in the SAME partition, and a numeric `1`
579
+ * collides with the string `'1'`.
580
+ *
581
+ * @param subject - The subject to key
582
+ * @param by - The partition key field
583
+ * @returns The subject's group key
584
+ *
585
+ * @example
586
+ * ```ts
587
+ * import { formatGroupKey } from '@orkestrel/program'
588
+ *
589
+ * formatGroupKey({ location: 'east' }, 'location') // 'east'
590
+ * ```
591
+ */
592
+ export declare function formatGroupKey(subject: Subject, by: FieldPath): string;
593
+
594
+ /**
595
+ * Determine whether a caller subject already carries a reserved program key.
596
+ *
597
+ * @remarks
598
+ * `aggregate` and `outcome` are program-private working-subject namespaces — the
599
+ * batch aggregate projection and the authority outcome projection are written
600
+ * under them. A caller subject that already owns either key would silently
601
+ * collide with a projection, so it is rejected before qualification.
602
+ *
603
+ * @param subject - The caller subject to check
604
+ * @returns `true` when the subject owns `aggregate` or `outcome`
605
+ *
606
+ * @example
607
+ * ```ts
608
+ * import { hasReservedKey } from '@orkestrel/program'
609
+ *
610
+ * hasReservedKey({ id: 'r1' }) // false
611
+ * hasReservedKey({ id: 'r1', aggregate: {} }) // true
612
+ * ```
613
+ */
614
+ export declare function hasReservedKey(subject: Readonly<Record<string, unknown>>): boolean;
615
+
616
+ /**
617
+ * Determine whether a value is an exact {@link AggregateDefinition} record.
618
+ *
619
+ * @param value - The candidate value
620
+ * @returns `true` when `value` is an {@link AggregateDefinition}
621
+ *
622
+ * @example
623
+ * ```ts
624
+ * import { isAggregateDefinition } from '@orkestrel/program'
625
+ *
626
+ * isAggregateDefinition({ fields: ['amount'] }) // true
627
+ * ```
628
+ */
629
+ export declare function isAggregateDefinition(value: unknown): value is AggregateDefinition;
630
+
631
+ /**
632
+ * Determine whether a value is a {@link Decision} literal.
633
+ *
634
+ * @param value - The candidate value
635
+ * @returns `true` when `value` is a {@link Decision}
636
+ *
637
+ * @example
638
+ * ```ts
639
+ * import { isDecision } from '@orkestrel/program'
640
+ *
641
+ * isDecision('approved') // true
642
+ * ```
643
+ */
644
+ export declare const isDecision: Guard<Decision>;
645
+
646
+ /**
647
+ * Determine whether a value is an exact {@link Notice} record.
648
+ *
649
+ * @param value - The candidate value
650
+ * @returns `true` when `value` is a {@link Notice}
651
+ *
652
+ * @example
653
+ * ```ts
654
+ * import { isNotice } from '@orkestrel/program'
655
+ *
656
+ * isNotice({ id: 'minimum', message: 'Minimum applies' }) // true
657
+ * ```
658
+ */
659
+ export declare function isNotice(value: unknown): value is Notice;
660
+
661
+ /**
662
+ * Determine whether a value is an exact {@link ProgramDefinition} record.
663
+ *
664
+ * @remarks
665
+ * `rating` is optional — an omitted `rating` authors an eligibility-only
666
+ * program (see {@link ProgramDefinition}).
667
+ *
668
+ * @param value - The candidate value
669
+ * @returns `true` when `value` is a {@link ProgramDefinition}
670
+ *
671
+ * @example
672
+ * ```ts
673
+ * import { isProgramDefinition } from '@orkestrel/program'
674
+ *
675
+ * isProgramDefinition({ id: 'p', name: 'P', qualification }) // true
676
+ * ```
677
+ */
678
+ export declare function isProgramDefinition(value: unknown): value is ProgramDefinition;
679
+
680
+ /**
681
+ * Determine whether a value is a {@link ProgramEffect} literal.
682
+ *
683
+ * @param value - The candidate value
684
+ * @returns `true` when `value` is a {@link ProgramEffect}
685
+ *
686
+ * @example
687
+ * ```ts
688
+ * import { isProgramEffect } from '@orkestrel/program'
689
+ *
690
+ * isProgramEffect('notice') // true
691
+ * ```
692
+ */
693
+ export declare const isProgramEffect: Guard<ProgramEffect>;
694
+
695
+ /** Narrow a caught value to a {@link ProgramError}. */
696
+ export declare function isProgramError(value: unknown): value is ProgramError;
697
+
698
+ /**
699
+ * Determine whether a value is a {@link Status} literal.
700
+ *
701
+ * @param value - The candidate value
702
+ * @returns `true` when `value` is a {@link Status}
703
+ *
704
+ * @example
705
+ * ```ts
706
+ * import { isStatus } from '@orkestrel/program'
707
+ *
708
+ * isStatus('eligible') // true
709
+ * ```
710
+ */
711
+ export declare const isStatus: Guard<Status>;
712
+
713
+ /** An authored, unconditional program notice. */
714
+ export declare interface Notice {
715
+ readonly id: string;
716
+ readonly message: string;
717
+ readonly scope?: string;
718
+ }
719
+
720
+ /**
721
+ * Build a {@link Notice}.
722
+ *
723
+ * @param id - The notice id
724
+ * @param message - The message template, carrying optional `{{token}}`s
725
+ * @param input - Optional presentation scope
726
+ * @returns A fresh notice
727
+ *
728
+ * @example
729
+ * ```ts
730
+ * import { noticeDefinition } from '@orkestrel/program'
731
+ *
732
+ * noticeDefinition('minimum', 'Minimum earned premium applies')
733
+ * ```
734
+ */
735
+ export declare function noticeDefinition(id: string, message: string, input?: NoticeInput): Notice;
736
+
737
+ /**
738
+ * Optional fields accepted by `noticeDefinition`.
739
+ *
740
+ * @remarks
741
+ * `scope` — the rating-line id the notice presents against; omitted for an
742
+ * unscoped, program-wide notice.
743
+ */
744
+ export declare interface NoticeInput {
745
+ readonly scope?: string;
746
+ }
747
+
748
+ /** The reserved working-subject key the authority's outcome projection is written under. */
749
+ export declare const OUTCOME_KEY = "outcome";
750
+
751
+ /**
752
+ * One compiled program — composes one qualifier and one rater over a shared
753
+ * reason engine and executes single subjects or aggregate-aware batches.
754
+ *
755
+ * @remarks
756
+ * Qualification decides whether rating happens: a globally ineligible, referred,
757
+ * or failed subject never reaches the rater, and a scoped ineligibility removes
758
+ * only its line before the first rating call. The rater always receives the
759
+ * ORIGINAL subject; the qualifier's aggregate projection stays private. When no
760
+ * qualifier, rater, or engine is injected the program creates ONE shared
761
+ * quantitative-plus-logical engine, injects it into the qualifier and rater it
762
+ * creates, and destroys only what it owns. A definition failure during
763
+ * construction (an invalid definition under `options.validate`) tears down
764
+ * whatever the constructor had already allocated before throwing. `destroy()`
765
+ * is idempotent and REENTRANCY-SAFE — the destroyed flag is set BEFORE any
766
+ * teardown or the `destroy` event fires, so a listener that re-enters
767
+ * `destroy()` is a no-op — and tears the emitter down last.
768
+ */
769
+ export declare class Program implements ProgramInterface {
770
+ #private;
771
+ readonly id: string;
772
+ readonly name: string;
773
+ readonly definition: ProgramDefinition;
774
+ constructor(definition: ProgramDefinition, options?: ProgramOptions);
775
+ get emitter(): EmitterInterface<ProgramEventMap>;
776
+ execute(subjects: readonly Subject[]): AggregateResult;
777
+ execute(subject: Subject): ProgramResult;
778
+ validate(): ProgramValidationResult;
779
+ destroy(): void;
780
+ }
781
+
782
+ /**
783
+ * A pure authored program definition.
784
+ *
785
+ * @remarks
786
+ * `qualification` runs first through `@orkestrel/qualifier`; `rating` runs only
787
+ * over the lines scoped eligibility left standing, through `@orkestrel/rater`.
788
+ * `authority` (a logical definition) runs last, over the assembled result
789
+ * extended with an outcome projection, to derive limit determinations and the
790
+ * final decision. An omitted `rating` authors an ELIGIBILITY-ONLY program — the
791
+ * rater is never invoked, an eligible subject resolves to `'eligible'` (or
792
+ * `'conditional'` under an applied condition or scoped restriction), status is
793
+ * never `'unrated'`, and decisions remain reachable through `authority`.
794
+ */
795
+ export declare interface ProgramDefinition {
796
+ readonly id: string;
797
+ readonly name: string;
798
+ readonly description?: string;
799
+ readonly qualification: QualificationDefinition;
800
+ readonly rating?: RatingDefinition;
801
+ readonly notices?: readonly Notice[];
802
+ readonly authority?: LogicalDefinition;
803
+ readonly aggregate?: AggregateDefinition;
804
+ readonly metadata?: JSONValue;
805
+ }
806
+
807
+ /**
808
+ * Build a {@link ProgramDefinition}.
809
+ *
810
+ * @remarks
811
+ * Copies every collection and omits absent optional keys, so the returned
812
+ * definition is a fresh, JSON-serializable value that never aliases its inputs.
813
+ *
814
+ * @param id - The program id
815
+ * @param name - The display name
816
+ * @param qualification - The nested qualification definition
817
+ * @param rating - The nested rating definition; omit for an eligibility-only program
818
+ * @param input - Optional description, notices, authority, aggregate, and metadata
819
+ * @returns A fresh program definition
820
+ *
821
+ * @example
822
+ * ```ts
823
+ * import { programDefinition } from '@orkestrel/program'
824
+ *
825
+ * programDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
826
+ * ```
827
+ */
828
+ export declare function programDefinition(id: string, name: string, qualification: QualificationDefinition, rating?: RatingDefinition, input?: ProgramInput): ProgramDefinition;
829
+
830
+ /** A post-qualification program determination effect. */
831
+ export declare type ProgramEffect = 'notice' | 'limit';
832
+
833
+ /**
834
+ * A coded programmer error thrown by the program layer.
835
+ *
836
+ * @remarks
837
+ * `DUPLICATE` — a program id collision on `ProgramManager.add`, or a duplicate
838
+ * authored rating-line or notice id. `MISSING` — an
839
+ * authored notice or qualification ruling scope names no rating line.
840
+ * `DEFINITION` — a program, qualification, rating, authority, or aggregate
841
+ * policy failed validation. `MISMATCH` — an injected entity or a returned
842
+ * reason result has the wrong contract. `RESERVED` — a subject already
843
+ * carries `aggregate` or `outcome`. `DESTROYED` — use of a destroyed entity.
844
+ */
845
+ export declare class ProgramError extends Error {
846
+ readonly code: ProgramErrorCode;
847
+ readonly context?: unknown;
848
+ constructor(code: ProgramErrorCode, message: string, context?: unknown);
849
+ }
850
+
851
+ /** A coded {@link ProgramError} programmer-error code. */
852
+ export declare type ProgramErrorCode = 'DUPLICATE' | 'MISSING' | 'DEFINITION' | 'MISMATCH' | 'RESERVED' | 'DESTROYED';
853
+
854
+ /**
855
+ * The push observation surface of a {@link ProgramInterface} (AGENTS §13).
856
+ *
857
+ * @remarks
858
+ * `rate` fires only when at least one line was selected. `determine` fires once
859
+ * per notice, then once per applied limit. `decide` fires only when a decision
860
+ * was reached.
861
+ */
862
+ export declare type ProgramEventMap = {
863
+ readonly qualify: readonly [result: QualificationResult];
864
+ readonly rate: readonly [result: RatingResult];
865
+ readonly determine: readonly [result: Determination];
866
+ readonly decide: readonly [decision: Decision, result: ProgramResult];
867
+ readonly execute: readonly [result: ProgramResult];
868
+ readonly aggregate: readonly [result: AggregateResult];
869
+ readonly destroy: readonly [];
870
+ };
871
+
872
+ /**
873
+ * Optional fields accepted by `programDefinition`.
874
+ *
875
+ * @remarks
876
+ * `description` — a free-text summary. `notices` — authored unconditional
877
+ * notices. `authority` — a logical definition evaluated per subject to derive
878
+ * limit determinations and the decision. `aggregate` — batch aggregate fields,
879
+ * partition key, and gates. `metadata` — opaque caller data, copied fresh.
880
+ */
881
+ export declare interface ProgramInput {
882
+ readonly description?: string;
883
+ readonly notices?: readonly Notice[];
884
+ readonly authority?: LogicalDefinition;
885
+ readonly aggregate?: AggregateDefinition;
886
+ readonly metadata?: JSONValue;
887
+ }
888
+
889
+ /**
890
+ * One compiled program — composes one qualifier and one rater over a shared
891
+ * reason engine.
892
+ *
893
+ * @remarks
894
+ * The array-of-subjects `execute` overload is declared FIRST (AGENTS §9.2) so a
895
+ * subject list resolves to one aggregate-aware batch execution.
896
+ */
897
+ export declare interface ProgramInterface {
898
+ readonly id: string;
899
+ readonly name: string;
900
+ readonly definition: ProgramDefinition;
901
+ readonly emitter: EmitterInterface<ProgramEventMap>;
902
+ execute(subjects: readonly Subject[]): AggregateResult;
903
+ execute(subject: Subject): ProgramResult;
904
+ validate(): ProgramValidationResult;
905
+ destroy(): void;
906
+ }
907
+
908
+ /**
909
+ * An ordered manager over compiled {@link ProgramInterface}s (AGENTS §9), sharing
910
+ * one qualifier, rater, and reason engine across every program it compiles.
911
+ *
912
+ * @remarks
913
+ * OWNS its ordered `#programs` collection and its own {@link Emitter} over
914
+ * {@link ProgramManagerEventMap}. Creates or borrows one shared engine, qualifier,
915
+ * and rater and injects the same instances into every compiled program. `remove`
916
+ * destroys the programs it removes; `destroy()` removes all programs, then
917
+ * destroys only the owned shared dependencies, and tears the emitter down LAST.
918
+ * A seed-program failure during construction tears the manager down (destroying
919
+ * whatever had already been compiled) before rethrowing the original error.
920
+ * `destroy()` is REENTRANCY-SAFE — the destroyed flag is set BEFORE any teardown
921
+ * or the `remove` / `destroy` events fire, so a `remove` listener that re-enters
922
+ * `destroy()` is a no-op. Every call after `destroy()` throws {@link ProgramError}
923
+ * `'DESTROYED'`.
924
+ */
925
+ export declare class ProgramManager implements ProgramManagerInterface {
926
+ #private;
927
+ constructor(options?: ProgramManagerOptions);
928
+ get emitter(): EmitterInterface<ProgramManagerEventMap>;
929
+ get size(): number;
930
+ has(id: string): boolean;
931
+ program(id: string): ProgramInterface | undefined;
932
+ programs(): readonly ProgramInterface[];
933
+ add(definition: ProgramDefinition): ProgramInterface;
934
+ remove(ids: readonly string[]): boolean;
935
+ remove(id: string): boolean;
936
+ remove(): void;
937
+ destroy(): void;
938
+ }
939
+
940
+ /** The push observation surface of a {@link ProgramManagerInterface} (AGENTS §13). */
941
+ export declare type ProgramManagerEventMap = {
942
+ readonly add: readonly [id: string];
943
+ readonly remove: readonly [id: string];
944
+ readonly destroy: readonly [];
945
+ };
946
+
947
+ /** An ordered manager over compiled programs (AGENTS §9), sharing one qualifier and rater. */
948
+ export declare interface ProgramManagerInterface {
949
+ readonly emitter: EmitterInterface<ProgramManagerEventMap>;
950
+ readonly size: number;
951
+ has(id: string): boolean;
952
+ program(id: string): ProgramInterface | undefined;
953
+ programs(): readonly ProgramInterface[];
954
+ add(definition: ProgramDefinition): ProgramInterface;
955
+ remove(ids: readonly string[]): boolean;
956
+ remove(id: string): boolean;
957
+ remove(): void;
958
+ destroy(): void;
959
+ }
960
+
961
+ /**
962
+ * Options for `createProgramManager` / the `ProgramManager` constructor.
963
+ *
964
+ * @remarks
965
+ * `qualifier` — an injected, caller-owned qualifier; created and owned when
966
+ * omitted. `rater` — an injected, caller-owned rater; created and owned when
967
+ * omitted. `engine` — an injected, caller-owned reason engine; created and
968
+ * owned when omitted. `programs` — seed definitions compiled in order.
969
+ * `validate` — validate each seeded/added definition at construction (default
970
+ * {@link DEFAULT_PROGRAM_VALIDATE}). `labels` — field-to-label overrides for
971
+ * determination premises, keyed by dot-joined field. `on` — initial emitter
972
+ * hooks. `error` — the emitter's listener-error handler.
973
+ */
974
+ export declare interface ProgramManagerOptions {
975
+ readonly qualifier?: QualifierInterface;
976
+ readonly rater?: RaterInterface;
977
+ readonly engine?: ReasonInterface;
978
+ readonly programs?: readonly ProgramDefinition[];
979
+ readonly validate?: boolean;
980
+ readonly labels?: Readonly<Record<string, string>>;
981
+ readonly on?: EmitterHooks<ProgramManagerEventMap>;
982
+ readonly error?: EmitterErrorHandler;
983
+ }
984
+
985
+ /**
986
+ * Options for `createProgram` / the `Program` constructor.
987
+ *
988
+ * @remarks
989
+ * `qualifier` — an injected, caller-owned qualifier; created and owned by the
990
+ * program when omitted. `rater` — an injected, caller-owned rater; created and
991
+ * owned when omitted. `engine` — an injected, caller-owned reason engine;
992
+ * created and owned when omitted. `validate` — validate the definition at
993
+ * construction (default {@link DEFAULT_PROGRAM_VALIDATE}). `labels` —
994
+ * field-to-label overrides for determination premises, keyed by dot-joined
995
+ * field. `on` — initial emitter hooks. `error` — the emitter's listener-error
996
+ * handler.
997
+ */
998
+ export declare interface ProgramOptions {
999
+ readonly qualifier?: QualifierInterface;
1000
+ readonly rater?: RaterInterface;
1001
+ readonly engine?: ReasonInterface;
1002
+ readonly validate?: boolean;
1003
+ readonly labels?: Readonly<Record<string, string>>;
1004
+ readonly on?: EmitterHooks<ProgramEventMap>;
1005
+ readonly error?: EmitterErrorHandler;
1006
+ }
1007
+
1008
+ /** One subject's complete program outcome. */
1009
+ export declare interface ProgramResult {
1010
+ readonly id: string;
1011
+ readonly name: string;
1012
+ readonly eligibility: Eligibility;
1013
+ readonly status: Status;
1014
+ /**
1015
+ * @remarks
1016
+ * Present ONLY when the program HAS an `authority`, the execution SUCCEEDED
1017
+ * (qualification, rating when it ran, and authority all produced no errors),
1018
+ * no `limit` determination applied, and status is not `unrated`.
1019
+ */
1020
+ readonly decision?: Decision;
1021
+ readonly qualification: QualificationResult;
1022
+ readonly rating?: RatingResult;
1023
+ readonly determinations: readonly Determination[];
1024
+ readonly success: boolean;
1025
+ readonly trace: readonly string[];
1026
+ readonly errors: readonly string[];
1027
+ }
1028
+
1029
+ /** Semantic definition validation. */
1030
+ export declare interface ProgramValidationResult {
1031
+ readonly valid: boolean;
1032
+ readonly errors: readonly string[];
1033
+ readonly warnings: readonly string[];
1034
+ }
1035
+
1036
+ /**
1037
+ * Select the rating lines a subject may be rated on from scoped eligibility.
1038
+ *
1039
+ * @remarks
1040
+ * A scope names a rating-line id. A line survives when its scope is absent
1041
+ * (eligible by default), `eligible`, or a `condition` (which is not an
1042
+ * eligibility value and never appears here). A scoped `ineligible` or `referral`
1043
+ * removes the line BEFORE the rater is invoked — the excluded line is never
1044
+ * evaluated merely to discard its amount.
1045
+ *
1046
+ * @param lines - The program's authored rating lines
1047
+ * @param scopes - The qualification's per-scope eligibility
1048
+ * @returns The surviving line definitions, in authored order
1049
+ *
1050
+ * @example
1051
+ * ```ts
1052
+ * import { selectProgramLines } from '@orkestrel/program'
1053
+ *
1054
+ * selectProgramLines(lines, { wind: 'ineligible' }) // every line except 'wind'
1055
+ * ```
1056
+ */
1057
+ export declare function selectProgramLines(lines: readonly LineDefinition[], scopes: Readonly<Record<string, Eligibility>>): readonly LineDefinition[];
1058
+
1059
+ /** The presentation and tally status derived from eligibility, conditions, and rating success. */
1060
+ export declare type Status = 'ineligible' | 'referral' | 'conditional' | 'unrated' | 'eligible';
1061
+
1062
+ /** Status tally precedence order — least to most resolved. */
1063
+ export declare const STATUS_PRECEDENCE: readonly Status[];
1064
+
1065
+ /**
1066
+ * Fold one subject's finite aggregate field values into a sums record.
1067
+ *
1068
+ * @remarks
1069
+ * Returns a FRESH record — `sums` is never mutated. Only finite numbers
1070
+ * contribute; a non-numeric or absent value contributes zero (never a
1071
+ * coercion). A {@link FieldPath} may be nested — `formatField` renders the
1072
+ * dot-joined key the returned record is keyed by.
1073
+ *
1074
+ * @param sums - The sums record to fold into
1075
+ * @param subject - The subject to fold in
1076
+ * @param fields - The fields to sum
1077
+ * @returns A fresh sums record with `subject`'s contribution added
1078
+ *
1079
+ * @example
1080
+ * ```ts
1081
+ * import { sumFields } from '@orkestrel/program'
1082
+ *
1083
+ * sumFields({ amount: 0 }, { amount: 5 }, ['amount']) // { amount: 5 }
1084
+ * ```
1085
+ */
1086
+ export declare function sumFields(sums: Readonly<Record<string, number>>, subject: Subject, fields: readonly FieldPath[]): Readonly<Record<string, number>>;
1087
+
1088
+ /** A status tally — a count plus summed aggregate fields. */
1089
+ export declare interface Tally {
1090
+ readonly count: number;
1091
+ readonly sums: Readonly<Record<string, number>>;
1092
+ }
1093
+
1094
+ /**
1095
+ * Add one subject's aggregate contribution to a status tally record.
1096
+ *
1097
+ * @param tallies - The tallies to update
1098
+ * @param result - The subject's program result (its `status` selects the tally)
1099
+ * @param subject - The subject to fold in
1100
+ * @param fields - The fields to sum
1101
+ * @returns A fresh, complete tally record with the subject folded in
1102
+ *
1103
+ * @example
1104
+ * ```ts
1105
+ * import { tallyProgram } from '@orkestrel/program'
1106
+ *
1107
+ * tallyProgram(tallies, result, { id: 'r1', amount: 5 }, ['amount'])
1108
+ * ```
1109
+ */
1110
+ export declare function tallyProgram(tallies: Readonly<Record<Status, Tally>>, result: ProgramResult, subject: Subject, fields: readonly FieldPath[]): Readonly<Record<Status, Tally>>;
1111
+
1112
+ /**
1113
+ * Validate a program definition's shape, references, and nested definitions.
1114
+ *
1115
+ * @remarks
1116
+ * The single semantic-validation implementation used by `Program.validate`. It
1117
+ * establishes exact shape through {@link isProgramDefinition}, validates the
1118
+ * rating structurally through the rater's {@link isRatingDefinition} guard (the
1119
+ * rater exposes no `validate`), delegates qualification validation to the
1120
+ * injected qualifier and authority / aggregate-gate validation to the shared
1121
+ * reason engine, and checks scope, notice, and aggregate-field references here.
1122
+ *
1123
+ * @param definition - The program definition to validate
1124
+ * @param qualifier - The qualifier that validates the nested qualification
1125
+ * @param engine - The reason engine that validates authority and aggregate gates
1126
+ * @returns A structured validation result
1127
+ *
1128
+ * @example
1129
+ * ```ts
1130
+ * import { validateProgramDefinition } from '@orkestrel/program'
1131
+ *
1132
+ * validateProgramDefinition(definition, qualifier, engine) // { valid: true, ... }
1133
+ * ```
1134
+ */
1135
+ export declare function validateProgramDefinition(definition: ProgramDefinition, qualifier: QualifierInterface, engine: ReasonInterface): ProgramValidationResult;
1136
+
1137
+ export { }