@orkestrel/program 0.0.7 → 0.0.9

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.
@@ -174,6 +174,202 @@ function isProgramDefinition(value) {
174
174
  "metadata"
175
175
  ])(value);
176
176
  }
177
+ /**
178
+ * Determine whether a value is an open program sums record.
179
+ *
180
+ * @remarks
181
+ * Every own string-named property is checked, including non-enumerable
182
+ * properties. Inherited and symbol-named members are outside the record this
183
+ * guard certifies. Values remain plain JavaScript numbers, including `NaN` and
184
+ * infinities, because the published contract does not refine them.
185
+ *
186
+ * @param value - The candidate value
187
+ * @returns `true` when every own string-named value is a number
188
+ *
189
+ * @example
190
+ * ```ts
191
+ * import { isProgramSums } from '@orkestrel/program'
192
+ *
193
+ * isProgramSums({ premium: 100 }) // true
194
+ * ```
195
+ */
196
+ function isProgramSums(value) {
197
+ return (0, _orkestrel_contract.whereOf)((0, _orkestrel_contract.objectOf)({}), (record) => Object.getOwnPropertyNames(record).every((key) => (0, _orkestrel_contract.isNumber)(Reflect.get(record, key))))(value);
198
+ }
199
+ /**
200
+ * Determine whether a value is an open result-side {@link Determination}.
201
+ *
202
+ * @remarks
203
+ * Unknown members and class instances are admitted. Arrays are refused.
204
+ * Optional `scope` and `message` members may be absent or `undefined`.
205
+ *
206
+ * @param value - The candidate value
207
+ * @returns `true` when every published determination member conforms
208
+ *
209
+ * @example
210
+ * ```ts
211
+ * import { isDetermination } from '@orkestrel/program'
212
+ *
213
+ * isDetermination({ id: 'audit', effect: 'notice', applied: true, premises: [] }) // true
214
+ * ```
215
+ */
216
+ var isDetermination = (0, _orkestrel_contract.objectOf)({
217
+ id: _orkestrel_contract.isString,
218
+ effect: isProgramEffect,
219
+ applied: _orkestrel_contract.isBoolean,
220
+ scope: _orkestrel_contract.isString,
221
+ message: _orkestrel_contract.isString,
222
+ premises: (0, _orkestrel_contract.arrayOf)(_orkestrel_qualifier.isPremise)
223
+ }, ["scope", "message"]);
224
+ /**
225
+ * Determine whether a value is an open result-side {@link AggregateGroup}.
226
+ *
227
+ * @remarks
228
+ * Unknown members and class instances are admitted. Arrays are refused.
229
+ *
230
+ * @param value - The candidate value
231
+ * @returns `true` when every published aggregate-group member conforms
232
+ *
233
+ * @example
234
+ * ```ts
235
+ * import { isAggregateGroup } from '@orkestrel/program'
236
+ *
237
+ * isAggregateGroup({ key: 'east', count: 1, sums: { premium: 100 } }) // true
238
+ * ```
239
+ */
240
+ var isAggregateGroup = (0, _orkestrel_contract.objectOf)({
241
+ key: _orkestrel_contract.isString,
242
+ count: _orkestrel_contract.isNumber,
243
+ sums: isProgramSums
244
+ });
245
+ /**
246
+ * Determine whether a value is an open result-side {@link Tally}.
247
+ *
248
+ * @remarks
249
+ * Unknown members and class instances are admitted. Arrays are refused.
250
+ *
251
+ * @param value - The candidate value
252
+ * @returns `true` when every published tally member conforms
253
+ *
254
+ * @example
255
+ * ```ts
256
+ * import { isTally } from '@orkestrel/program'
257
+ *
258
+ * isTally({ count: 1, sums: { premium: 100 } }) // true
259
+ * ```
260
+ */
261
+ var isTally = (0, _orkestrel_contract.objectOf)({
262
+ count: _orkestrel_contract.isNumber,
263
+ sums: isProgramSums
264
+ });
265
+ /**
266
+ * Determine whether a value is a total open status-tally record.
267
+ *
268
+ * @remarks
269
+ * Every {@link Status} in {@link STATUS_PRECEDENCE} is required and checked.
270
+ * Unknown members and class instances are admitted. Arrays are refused.
271
+ *
272
+ * @param value - The candidate value
273
+ * @returns `true` when every required status member is a {@link Tally}
274
+ *
275
+ * @example
276
+ * ```ts
277
+ * import { emptyTallies, isTallies } from '@orkestrel/program'
278
+ *
279
+ * isTallies(emptyTallies([])) // true
280
+ * ```
281
+ */
282
+ function isTallies(value) {
283
+ return (0, _orkestrel_contract.whereOf)((0, _orkestrel_contract.objectOf)({}), (record) => STATUS_PRECEDENCE.every((status) => isTally(Reflect.get(record, status))))(value);
284
+ }
285
+ /**
286
+ * Determine whether a value is an open {@link ProgramResult}.
287
+ *
288
+ * @remarks
289
+ * This guard is result-postured for values returned through a borrowed
290
+ * {@link ProgramInterface}. It admits unknown members and class instances while
291
+ * composing qualifier's `isQualificationResult` and rater's `isRatingResult`
292
+ * over their complete nested result closures. Arrays are refused.
293
+ *
294
+ * @param value - The candidate value
295
+ * @returns `true` when every published program-result member conforms
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * import { isProgramResult } from '@orkestrel/program'
300
+ *
301
+ * isProgramResult(program.execute(subject)) // true
302
+ * ```
303
+ */
304
+ var isProgramResult = (0, _orkestrel_contract.objectOf)({
305
+ id: _orkestrel_contract.isString,
306
+ name: _orkestrel_contract.isString,
307
+ eligibility: _orkestrel_qualifier.isEligibility,
308
+ status: isStatus,
309
+ decision: isDecision,
310
+ qualification: _orkestrel_qualifier.isQualificationResult,
311
+ rating: _orkestrel_rater.isRatingResult,
312
+ determinations: (0, _orkestrel_contract.arrayOf)(isDetermination),
313
+ success: _orkestrel_contract.isBoolean,
314
+ trace: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
315
+ errors: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)
316
+ }, ["decision", "rating"]);
317
+ /**
318
+ * Determine whether a value is an open {@link AggregateResult}.
319
+ *
320
+ * @remarks
321
+ * This guard is result-postured for values returned through a borrowed
322
+ * {@link ProgramInterface}. It admits unknown members and class instances while
323
+ * checking every nested program result, determination, group, total tally
324
+ * record, and sums record. Arrays are refused.
325
+ *
326
+ * @param value - The candidate value
327
+ * @returns `true` when every published aggregate-result member conforms
328
+ *
329
+ * @example
330
+ * ```ts
331
+ * import { isAggregateResult } from '@orkestrel/program'
332
+ *
333
+ * isAggregateResult(program.execute(subjects)) // true
334
+ * ```
335
+ */
336
+ var isAggregateResult = (0, _orkestrel_contract.objectOf)({
337
+ id: _orkestrel_contract.isString,
338
+ name: _orkestrel_contract.isString,
339
+ subjects: (0, _orkestrel_contract.arrayOf)(isProgramResult),
340
+ determinations: (0, _orkestrel_contract.arrayOf)(isDetermination),
341
+ groups: (0, _orkestrel_contract.arrayOf)(isAggregateGroup),
342
+ tallies: isTallies,
343
+ count: _orkestrel_contract.isNumber,
344
+ sums: isProgramSums,
345
+ success: _orkestrel_contract.isBoolean,
346
+ trace: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
347
+ errors: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)
348
+ });
349
+ /**
350
+ * Determine whether a value is an open {@link ProgramValidationResult}.
351
+ *
352
+ * @remarks
353
+ * `ProgramValidationResult` is this package's own declared interface, not an
354
+ * alias of reason's validation result. This guard therefore checks the three
355
+ * program-owned members directly so the contracts may evolve independently.
356
+ * Unknown members and class instances are admitted. Arrays are refused.
357
+ *
358
+ * @param value - The candidate value
359
+ * @returns `true` when every published program-validation member conforms
360
+ *
361
+ * @example
362
+ * ```ts
363
+ * import { isProgramValidationResult } from '@orkestrel/program'
364
+ *
365
+ * isProgramValidationResult({ valid: true, errors: [], warnings: [] }) // true
366
+ * ```
367
+ */
368
+ var isProgramValidationResult = (0, _orkestrel_contract.objectOf)({
369
+ valid: _orkestrel_contract.isBoolean,
370
+ errors: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString),
371
+ warnings: (0, _orkestrel_contract.arrayOf)(_orkestrel_contract.isString)
372
+ });
177
373
  //#endregion
178
374
  //#region src/core/helpers.ts
179
375
  /**
@@ -978,6 +1174,83 @@ function buildAggregateResult(definition, subjects, determinations, groups, tall
978
1174
  errors: [...subjects.flatMap((entry) => entry.errors), ...gateErrors]
979
1175
  };
980
1176
  }
1177
+ /**
1178
+ * Build a {@link ProgramDefinition}.
1179
+ *
1180
+ * @remarks
1181
+ * Copies every collection and omits absent optional keys, so the returned
1182
+ * definition is a fresh, JSON-serializable value that never aliases its inputs.
1183
+ *
1184
+ * @param id - The program id
1185
+ * @param name - The display name
1186
+ * @param qualification - The nested qualification definition
1187
+ * @param rating - The nested rating definition; omit for an eligibility-only program
1188
+ * @param input - Optional description, notices, authority, aggregate, and metadata
1189
+ * @returns A fresh program definition
1190
+ *
1191
+ * @example
1192
+ * ```ts
1193
+ * import { programDefinition } from '@orkestrel/program'
1194
+ *
1195
+ * programDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
1196
+ * ```
1197
+ */
1198
+ function programDefinition(id, name, qualification, rating, input) {
1199
+ return {
1200
+ id,
1201
+ name,
1202
+ qualification,
1203
+ ...rating === void 0 ? {} : { rating },
1204
+ ...input?.description === void 0 ? {} : { description: input.description },
1205
+ ...input?.notices === void 0 ? {} : { notices: [...input.notices] },
1206
+ ...input?.authority === void 0 ? {} : { authority: input.authority },
1207
+ ...input?.aggregate === void 0 ? {} : { aggregate: input.aggregate },
1208
+ ...input?.metadata === void 0 ? {} : { metadata: copyJSONValue(input.metadata) }
1209
+ };
1210
+ }
1211
+ /**
1212
+ * Build a {@link Notice}.
1213
+ *
1214
+ * @param id - The notice id
1215
+ * @param message - The message template, carrying optional `{{token}}`s
1216
+ * @param input - Optional presentation scope
1217
+ * @returns A fresh notice
1218
+ *
1219
+ * @example
1220
+ * ```ts
1221
+ * import { noticeDefinition } from '@orkestrel/program'
1222
+ *
1223
+ * noticeDefinition('minimum', 'Minimum earned premium applies')
1224
+ * ```
1225
+ */
1226
+ function noticeDefinition(id, message, input) {
1227
+ return {
1228
+ id,
1229
+ message,
1230
+ ...input?.scope === void 0 ? {} : { scope: input.scope }
1231
+ };
1232
+ }
1233
+ /**
1234
+ * Build an {@link AggregateDefinition}.
1235
+ *
1236
+ * @param fields - The aggregate fields to sum across a batch
1237
+ * @param input - Optional partition field and aggregate gates
1238
+ * @returns A fresh aggregate definition
1239
+ *
1240
+ * @example
1241
+ * ```ts
1242
+ * import { aggregateDefinition } from '@orkestrel/program'
1243
+ *
1244
+ * aggregateDefinition(['amount'], { by: 'location' })
1245
+ * ```
1246
+ */
1247
+ function aggregateDefinition(fields, input) {
1248
+ return {
1249
+ fields: [...fields],
1250
+ ...input?.by === void 0 ? {} : { by: input.by },
1251
+ ...input?.gates === void 0 ? {} : { gates: input.gates }
1252
+ };
1253
+ }
981
1254
  //#endregion
982
1255
  //#region src/core/programs/Program.ts
983
1256
  /**
@@ -1067,11 +1340,15 @@ var Program = class {
1067
1340
  assertProgramSubject(subject);
1068
1341
  const qualified = buildQualificationSubject(subject, aggregate);
1069
1342
  const qualification = this.#qualifier.qualify(qualified, this.definition.qualification);
1343
+ if (!(0, _orkestrel_qualifier.isQualificationResult)(qualification)) throw new ProgramError("MISMATCH", "Qualifier returned invalid qualification result", this.definition.qualification.id);
1070
1344
  this.#emitter.emit("qualify", qualification);
1071
1345
  if (!qualification.success || qualification.eligibility !== "eligible") return this.#finish(subject, qualification, void 0);
1072
1346
  const lines = selectProgramLines(this.definition.rating?.lines ?? [], qualification.scopes);
1073
1347
  const rating = lines.length === 0 ? void 0 : this.#rater.rate(lines, subject);
1074
- if (rating !== void 0) this.#emitter.emit("rate", rating);
1348
+ if (rating !== void 0) {
1349
+ if (!(0, _orkestrel_rater.isRatingResult)(rating)) throw new ProgramError("MISMATCH", "Rater returned invalid rating result", this.definition.rating?.id);
1350
+ this.#emitter.emit("rate", rating);
1351
+ }
1075
1352
  return this.#finish(subject, qualification, rating);
1076
1353
  }
1077
1354
  #finish(subject, qualification, rating) {
@@ -1086,7 +1363,7 @@ var Program = class {
1086
1363
  }
1087
1364
  const outcome = { [OUTCOME_KEY]: buildOutcomeProjection(result) };
1088
1365
  const resolved = this.#engine.reason(outcome, authority);
1089
- if (resolved.reasoning !== "logical") throw new ProgramError("MISMATCH", "Authority returned non-logical reasoning", authority.id);
1366
+ if (!(0, _orkestrel_reason.isLogicalResult)(resolved)) throw new ProgramError("MISMATCH", "Authority returned invalid logical result", authority.id);
1090
1367
  const limits = buildLimits(authority, resolved, outcome, this.#evaluator, this.#labels);
1091
1368
  for (const limit of limits) this.#emitter.emit("determine", limit);
1092
1369
  result = buildProgramResult(this.definition, qualification, rating, [...notices, ...limits], status, { authority: resolved });
@@ -1117,7 +1394,7 @@ var Program = class {
1117
1394
  if (gates === void 0) return { determinations: [] };
1118
1395
  const record = buildAggregateRecord(count, sums, groups);
1119
1396
  const resolved = this.#engine.reason(record, gates);
1120
- if (resolved.reasoning !== "logical") throw new ProgramError("MISMATCH", "Aggregate gates returned non-logical reasoning", gates.id);
1397
+ if (!(0, _orkestrel_reason.isLogicalResult)(resolved)) throw new ProgramError("MISMATCH", "Aggregate gates returned invalid logical result", gates.id);
1121
1398
  const determinations = buildLimits(gates, resolved, record, this.#evaluator, this.#labels);
1122
1399
  for (const determination of determinations) this.#emitter.emit("determine", determination);
1123
1400
  return {
@@ -1308,83 +1585,6 @@ function createProgram(definition, options) {
1308
1585
  function createProgramManager(options) {
1309
1586
  return new ProgramManager(options);
1310
1587
  }
1311
- /**
1312
- * Build a {@link ProgramDefinition}.
1313
- *
1314
- * @remarks
1315
- * Copies every collection and omits absent optional keys, so the returned
1316
- * definition is a fresh, JSON-serializable value that never aliases its inputs.
1317
- *
1318
- * @param id - The program id
1319
- * @param name - The display name
1320
- * @param qualification - The nested qualification definition
1321
- * @param rating - The nested rating definition; omit for an eligibility-only program
1322
- * @param input - Optional description, notices, authority, aggregate, and metadata
1323
- * @returns A fresh program definition
1324
- *
1325
- * @example
1326
- * ```ts
1327
- * import { programDefinition } from '@orkestrel/program'
1328
- *
1329
- * programDefinition('standard', 'Standard', qualification, rating, { notices: [notice] })
1330
- * ```
1331
- */
1332
- function programDefinition(id, name, qualification, rating, input) {
1333
- return {
1334
- id,
1335
- name,
1336
- qualification,
1337
- ...rating === void 0 ? {} : { rating },
1338
- ...input?.description === void 0 ? {} : { description: input.description },
1339
- ...input?.notices === void 0 ? {} : { notices: [...input.notices] },
1340
- ...input?.authority === void 0 ? {} : { authority: input.authority },
1341
- ...input?.aggregate === void 0 ? {} : { aggregate: input.aggregate },
1342
- ...input?.metadata === void 0 ? {} : { metadata: copyJSONValue(input.metadata) }
1343
- };
1344
- }
1345
- /**
1346
- * Build a {@link Notice}.
1347
- *
1348
- * @param id - The notice id
1349
- * @param message - The message template, carrying optional `{{token}}`s
1350
- * @param input - Optional presentation scope
1351
- * @returns A fresh notice
1352
- *
1353
- * @example
1354
- * ```ts
1355
- * import { noticeDefinition } from '@orkestrel/program'
1356
- *
1357
- * noticeDefinition('minimum', 'Minimum earned premium applies')
1358
- * ```
1359
- */
1360
- function noticeDefinition(id, message, input) {
1361
- return {
1362
- id,
1363
- message,
1364
- ...input?.scope === void 0 ? {} : { scope: input.scope }
1365
- };
1366
- }
1367
- /**
1368
- * Build an {@link AggregateDefinition}.
1369
- *
1370
- * @param fields - The aggregate fields to sum across a batch
1371
- * @param input - Optional partition field and aggregate gates
1372
- * @returns A fresh aggregate definition
1373
- *
1374
- * @example
1375
- * ```ts
1376
- * import { aggregateDefinition } from '@orkestrel/program'
1377
- *
1378
- * aggregateDefinition(['amount'], { by: 'location' })
1379
- * ```
1380
- */
1381
- function aggregateDefinition(fields, input) {
1382
- return {
1383
- fields: [...fields],
1384
- ...input?.by === void 0 ? {} : { by: input.by },
1385
- ...input?.gates === void 0 ? {} : { gates: input.gates }
1386
- };
1387
- }
1388
1588
  //#endregion
1389
1589
  exports.AGGREGATE_KEY = AGGREGATE_KEY;
1390
1590
  exports.DEFAULT_PROGRAM_VALIDATE = DEFAULT_PROGRAM_VALIDATE;
@@ -1419,12 +1619,20 @@ exports.findMissingScopes = findMissingScopes;
1419
1619
  exports.formatGroupKey = formatGroupKey;
1420
1620
  exports.hasReservedKey = hasReservedKey;
1421
1621
  exports.isAggregateDefinition = isAggregateDefinition;
1622
+ exports.isAggregateGroup = isAggregateGroup;
1623
+ exports.isAggregateResult = isAggregateResult;
1422
1624
  exports.isDecision = isDecision;
1625
+ exports.isDetermination = isDetermination;
1423
1626
  exports.isNotice = isNotice;
1424
1627
  exports.isProgramDefinition = isProgramDefinition;
1425
1628
  exports.isProgramEffect = isProgramEffect;
1426
1629
  exports.isProgramError = isProgramError;
1630
+ exports.isProgramResult = isProgramResult;
1631
+ exports.isProgramSums = isProgramSums;
1632
+ exports.isProgramValidationResult = isProgramValidationResult;
1427
1633
  exports.isStatus = isStatus;
1634
+ exports.isTallies = isTallies;
1635
+ exports.isTally = isTally;
1428
1636
  exports.noticeDefinition = noticeDefinition;
1429
1637
  exports.programDefinition = programDefinition;
1430
1638
  exports.selectProgramLines = selectProgramLines;
@@ -628,6 +628,45 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
628
628
  */
629
629
  export declare function isAggregateDefinition(value: unknown): value is AggregateDefinition;
630
630
 
631
+ /**
632
+ * Determine whether a value is an open result-side {@link AggregateGroup}.
633
+ *
634
+ * @remarks
635
+ * Unknown members and class instances are admitted. Arrays are refused.
636
+ *
637
+ * @param value - The candidate value
638
+ * @returns `true` when every published aggregate-group member conforms
639
+ *
640
+ * @example
641
+ * ```ts
642
+ * import { isAggregateGroup } from '@orkestrel/program'
643
+ *
644
+ * isAggregateGroup({ key: 'east', count: 1, sums: { premium: 100 } }) // true
645
+ * ```
646
+ */
647
+ export declare const isAggregateGroup: Guard<AggregateGroup>;
648
+
649
+ /**
650
+ * Determine whether a value is an open {@link AggregateResult}.
651
+ *
652
+ * @remarks
653
+ * This guard is result-postured for values returned through a borrowed
654
+ * {@link ProgramInterface}. It admits unknown members and class instances while
655
+ * checking every nested program result, determination, group, total tally
656
+ * record, and sums record. Arrays are refused.
657
+ *
658
+ * @param value - The candidate value
659
+ * @returns `true` when every published aggregate-result member conforms
660
+ *
661
+ * @example
662
+ * ```ts
663
+ * import { isAggregateResult } from '@orkestrel/program'
664
+ *
665
+ * isAggregateResult(program.execute(subjects)) // true
666
+ * ```
667
+ */
668
+ export declare const isAggregateResult: Guard<AggregateResult>;
669
+
631
670
  /**
632
671
  * Determine whether a value is a {@link Decision} literal.
633
672
  *
@@ -643,6 +682,25 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
643
682
  */
644
683
  export declare const isDecision: Guard<Decision>;
645
684
 
685
+ /**
686
+ * Determine whether a value is an open result-side {@link Determination}.
687
+ *
688
+ * @remarks
689
+ * Unknown members and class instances are admitted. Arrays are refused.
690
+ * Optional `scope` and `message` members may be absent or `undefined`.
691
+ *
692
+ * @param value - The candidate value
693
+ * @returns `true` when every published determination member conforms
694
+ *
695
+ * @example
696
+ * ```ts
697
+ * import { isDetermination } from '@orkestrel/program'
698
+ *
699
+ * isDetermination({ id: 'audit', effect: 'notice', applied: true, premises: [] }) // true
700
+ * ```
701
+ */
702
+ export declare const isDetermination: Guard<Determination>;
703
+
646
704
  /**
647
705
  * Determine whether a value is an exact {@link Notice} record.
648
706
  *
@@ -695,6 +753,69 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
695
753
  /** Narrow a caught value to a {@link ProgramError}. */
696
754
  export declare function isProgramError(value: unknown): value is ProgramError;
697
755
 
756
+ /**
757
+ * Determine whether a value is an open {@link ProgramResult}.
758
+ *
759
+ * @remarks
760
+ * This guard is result-postured for values returned through a borrowed
761
+ * {@link ProgramInterface}. It admits unknown members and class instances while
762
+ * composing qualifier's `isQualificationResult` and rater's `isRatingResult`
763
+ * over their complete nested result closures. Arrays are refused.
764
+ *
765
+ * @param value - The candidate value
766
+ * @returns `true` when every published program-result member conforms
767
+ *
768
+ * @example
769
+ * ```ts
770
+ * import { isProgramResult } from '@orkestrel/program'
771
+ *
772
+ * isProgramResult(program.execute(subject)) // true
773
+ * ```
774
+ */
775
+ export declare const isProgramResult: Guard<ProgramResult>;
776
+
777
+ /**
778
+ * Determine whether a value is an open program sums record.
779
+ *
780
+ * @remarks
781
+ * Every own string-named property is checked, including non-enumerable
782
+ * properties. Inherited and symbol-named members are outside the record this
783
+ * guard certifies. Values remain plain JavaScript numbers, including `NaN` and
784
+ * infinities, because the published contract does not refine them.
785
+ *
786
+ * @param value - The candidate value
787
+ * @returns `true` when every own string-named value is a number
788
+ *
789
+ * @example
790
+ * ```ts
791
+ * import { isProgramSums } from '@orkestrel/program'
792
+ *
793
+ * isProgramSums({ premium: 100 }) // true
794
+ * ```
795
+ */
796
+ export declare function isProgramSums(value: unknown): value is Readonly<Record<string, number>>;
797
+
798
+ /**
799
+ * Determine whether a value is an open {@link ProgramValidationResult}.
800
+ *
801
+ * @remarks
802
+ * `ProgramValidationResult` is this package's own declared interface, not an
803
+ * alias of reason's validation result. This guard therefore checks the three
804
+ * program-owned members directly so the contracts may evolve independently.
805
+ * Unknown members and class instances are admitted. Arrays are refused.
806
+ *
807
+ * @param value - The candidate value
808
+ * @returns `true` when every published program-validation member conforms
809
+ *
810
+ * @example
811
+ * ```ts
812
+ * import { isProgramValidationResult } from '@orkestrel/program'
813
+ *
814
+ * isProgramValidationResult({ valid: true, errors: [], warnings: [] }) // true
815
+ * ```
816
+ */
817
+ export declare const isProgramValidationResult: Guard<ProgramValidationResult>;
818
+
698
819
  /**
699
820
  * Determine whether a value is a {@link Status} literal.
700
821
  *
@@ -710,6 +831,43 @@ export declare function aggregateSums(subjects: readonly Subject[], fields: read
710
831
  */
711
832
  export declare const isStatus: Guard<Status>;
712
833
 
834
+ /**
835
+ * Determine whether a value is a total open status-tally record.
836
+ *
837
+ * @remarks
838
+ * Every {@link Status} in {@link STATUS_PRECEDENCE} is required and checked.
839
+ * Unknown members and class instances are admitted. Arrays are refused.
840
+ *
841
+ * @param value - The candidate value
842
+ * @returns `true` when every required status member is a {@link Tally}
843
+ *
844
+ * @example
845
+ * ```ts
846
+ * import { emptyTallies, isTallies } from '@orkestrel/program'
847
+ *
848
+ * isTallies(emptyTallies([])) // true
849
+ * ```
850
+ */
851
+ export declare function isTallies(value: unknown): value is Readonly<Record<Status, Tally>>;
852
+
853
+ /**
854
+ * Determine whether a value is an open result-side {@link Tally}.
855
+ *
856
+ * @remarks
857
+ * Unknown members and class instances are admitted. Arrays are refused.
858
+ *
859
+ * @param value - The candidate value
860
+ * @returns `true` when every published tally member conforms
861
+ *
862
+ * @example
863
+ * ```ts
864
+ * import { isTally } from '@orkestrel/program'
865
+ *
866
+ * isTally({ count: 1, sums: { premium: 100 } }) // true
867
+ * ```
868
+ */
869
+ export declare const isTally: Guard<Tally>;
870
+
713
871
  /** An authored, unconditional program notice. */
714
872
  export declare interface Notice {
715
873
  readonly id: string;