@orkestrel/reason 0.0.9 → 0.0.11

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.
@@ -1,4 +1,4 @@
1
- import { GUARD_DEPTH_LIMIT, arrayOf, holds, isArray, isBoolean, isFiniteNumber, isNumber, isObject, isRecord, isString, lazyOf, literalOf, notOf, orOf, parseJSONAs, parseNumber, parseNumberField, readArrayEntries, recordOf, resolveField, unionOf, whereOf } from "@orkestrel/contract";
1
+ import { GUARD_DEPTH_LIMIT, arrayOf, holds, isArray, isBoolean, isError, isFiniteNumber, isFunction, isInstance, isNumber, isObject, isRecord, isString, lazyOf, literalOf, notOf, orOf, parseJSONAs, parseNumber, parseNumberField, readArrayEntries, recordOf, resolveField, unionOf, whereOf } from "@orkestrel/contract";
2
2
  import { Emitter } from "@orkestrel/emitter";
3
3
  //#region src/core/constants.ts
4
4
  /**
@@ -47,7 +47,7 @@ var DEFAULT_PRIORITY = 0;
47
47
  * inferential chaining.
48
48
  *
49
49
  * @remarks
50
- * Fixed — unlike {@link DEFAULT_PRECISION} it is NOT overridable per
50
+ * Fixed — unlike {@link DEFAULT_PRECISION} it is not overridable per
51
51
  * definition, so confidence products stay comparable across derivations.
52
52
  */
53
53
  var CONFIDENCE_PRECISION = 4;
@@ -93,8 +93,8 @@ var DEFINITION_BUILDER_BRAND = Symbol("reasons.definitionBuilder");
93
93
  *
94
94
  * @remarks
95
95
  * Only `isSubjectBuilder` (`validators.ts`) reads this key, through `Reflect.get`.
96
- * Distinct from {@link DEFINITION_BUILDER_BRAND}, so the two entities can never
97
- * match each other's guard.
96
+ * Distinct from {@link DEFINITION_BUILDER_BRAND}, so neither entity can ever
97
+ * match the other's guard.
98
98
  */
99
99
  var SUBJECT_BUILDER_BRAND = Symbol("reasons.subjectBuilder");
100
100
  //#endregion
@@ -143,7 +143,7 @@ var ReasonError = class extends Error {
143
143
  * ```
144
144
  */
145
145
  function isReasonError(value) {
146
- return value instanceof ReasonError;
146
+ return isInstance(value, ReasonError);
147
147
  }
148
148
  //#endregion
149
149
  //#region src/core/validators.ts
@@ -151,7 +151,7 @@ function isReasonError(value) {
151
151
  * Determines whether a value is a {@link Reasoning} literal.
152
152
  *
153
153
  * @param value - The value to test
154
- * @returns True if `value` is one of the four reasoning strategies; false otherwise
154
+ * @returns True if `value` names a reasoning strategy; false otherwise
155
155
  *
156
156
  * @example
157
157
  * ```ts
@@ -196,7 +196,7 @@ var isMathOperation = literalOf("add", "subtract", "multiply", "divide", "percen
196
196
  * Determines whether a value is an {@link Aggregation} literal.
197
197
  *
198
198
  * @param value - The value to test
199
- * @returns True if `value` is one of the five aggregations; false otherwise
199
+ * @returns True if `value` names an aggregation; false otherwise
200
200
  *
201
201
  * @example
202
202
  * ```ts
@@ -211,7 +211,7 @@ var isAggregation = literalOf("sum", "product", "average", "minimum", "maximum")
211
211
  * Determines whether a value is a {@link Comparison} literal.
212
212
  *
213
213
  * @param value - The value to test
214
- * @returns True if `value` is one of the ten comparison operators; false otherwise
214
+ * @returns True if `value` names a comparison operator; false otherwise
215
215
  *
216
216
  * @example
217
217
  * ```ts
@@ -226,7 +226,7 @@ var isComparison = literalOf("equals", "not", "above", "below", "from", "to", "a
226
226
  * Determines whether a value is a {@link LogicalOperator} literal.
227
227
  *
228
228
  * @param value - The value to test
229
- * @returns True if `value` is one of the five logical connectives; false otherwise
229
+ * @returns True if `value` names a logical connective; false otherwise
230
230
  *
231
231
  * @example
232
232
  * ```ts
@@ -248,7 +248,7 @@ var isLogicalOperator = literalOf("and", "or", "not", "implies", "xor");
248
248
  * ```ts
249
249
  * import { isFieldPath } from '@src/core'
250
250
  *
251
- * isFieldPath('age') // true — ONE key (never dot-split)
251
+ * isFieldPath('age') // true — a single key (never dot-split)
252
252
  * isFieldPath(['address', 'city']) // true — descends
253
253
  * isFieldPath(42) // false
254
254
  * ```
@@ -275,8 +275,8 @@ var isNumberRecord = whereOf(isRecord, (record) => Object.values(record).every(i
275
275
  * predicate.
276
276
  *
277
277
  * @remarks
278
- * `value` may be ANYTHING (including `null` / `undefined`) but the key must be
279
- * PRESENT — exact-record semantics reject a check that lost its `value` key.
278
+ * `value` may be anything (including `null` / `undefined`) but the key must be
279
+ * present — exact-record semantics reject a check that lost its `value` key.
280
280
  *
281
281
  * @param value - The value to test
282
282
  * @returns True if `value` is a well-formed check; false otherwise
@@ -360,8 +360,8 @@ function isFactorRange(value) {
360
360
  }, ["bounds"])(value);
361
361
  }
362
362
  /**
363
- * Determines whether a value is a {@link Source} — any of the four factor
364
- * sources, discriminated by `origin`.
363
+ * Determines whether a value is a {@link Source} — a static, field, lookup, or
364
+ * range factor source, discriminated by `origin`.
365
365
  *
366
366
  * @param value - The value to test
367
367
  * @returns True if `value` is a well-formed static / field / lookup / range source; false otherwise
@@ -472,9 +472,9 @@ function isFactorGroup(value) {
472
472
  * tree of atoms and compounds, discriminated by `form`.
473
473
  *
474
474
  * @remarks
475
- * Recursive through `lazyOf` — recursion is STACK-BOUNDED, not
475
+ * Recursive through `lazyOf` — recursion is bounded by the stack, not
476
476
  * unbounded: nesting beyond the engine's stack budget (roughly 1000 levels)
477
- * and cyclic input are CONTAINED as `false`, never a throw. Input past that
477
+ * and cyclic input are contained as `false`, never a throw. Input past that
478
478
  * bound is rejected, not validated.
479
479
  *
480
480
  * @param value - The value to test
@@ -534,9 +534,9 @@ function isRule(value) {
534
534
  * `form`.
535
535
  *
536
536
  * @remarks
537
- * Recursive through `lazyOf` — recursion is STACK-BOUNDED, not
537
+ * Recursive through `lazyOf` — recursion is bounded by the stack, not
538
538
  * unbounded: nesting beyond the engine's stack budget (roughly 1000 levels)
539
- * and cyclic input are CONTAINED as `false`, never a throw. Input past that
539
+ * and cyclic input are contained as `false`, never a throw. Input past that
540
540
  * bound is rejected, not validated.
541
541
  *
542
542
  * @param value - The value to test
@@ -755,8 +755,8 @@ function isInferentialDefinition(value) {
755
755
  }, ["description", "depth"])(value);
756
756
  }
757
757
  /**
758
- * Determines whether a value is a {@link Definition} — any of the four
759
- * definition shapes, discriminated by `reasoning`.
758
+ * Determines whether a value is a {@link Definition} — a quantitative, logical,
759
+ * symbolic, or inferential definition shape, discriminated by `reasoning`.
760
760
  *
761
761
  * @param value - The value to test
762
762
  * @returns True if `value` is a well-formed definition of any reasoning; false otherwise
@@ -864,6 +864,12 @@ function isResultFact(value) {
864
864
  /**
865
865
  * Determines whether a value is a {@link CheckResult}.
866
866
  *
867
+ * @remarks
868
+ * `actual` must be present and may hold anything, including `undefined` — the
869
+ * guard reads its presence, never its value. A result whose `actual` is
870
+ * `undefined` therefore does not survive a JSON round trip: `JSON.stringify`
871
+ * drops the key, and the parsed record is refused here.
872
+ *
867
873
  * @param value - The value to test
868
874
  * @returns True if every published check-result member is valid; false otherwise
869
875
  *
@@ -885,6 +891,10 @@ function isCheckResult(value) {
885
891
  /**
886
892
  * Determines whether a value is a {@link FactorResult}.
887
893
  *
894
+ * @remarks
895
+ * The optional `raw` and `checks` members are absent-or-valid: an absent key
896
+ * passes, a present key is checked in full.
897
+ *
888
898
  * @param value - The value to test
889
899
  * @returns True if every published factor-result member is valid; false otherwise
890
900
  *
@@ -1056,7 +1066,7 @@ function isLogicalResult(value) {
1056
1066
  * Determines whether a value is a {@link SymbolicResult}.
1057
1067
  *
1058
1068
  * @remarks
1059
- * `solutions` is checked across its OWN string-named members, enumerable or
1069
+ * `solutions` is checked across its own string-named members, enumerable or
1060
1070
  * not. Inherited members are deliberately unread: they belong to the
1061
1071
  * accepted-prototype property, not to the record this guard certifies.
1062
1072
  *
@@ -1134,13 +1144,13 @@ function isReasonValidationResult(value) {
1134
1144
  })(value);
1135
1145
  }
1136
1146
  /**
1137
- * Determines whether a value is a `DefinitionBuilder` ENTITY — the brand-guarded
1147
+ * Determines whether a value is a `DefinitionBuilder` entity — the brand-guarded
1138
1148
  * stateful workspace, not the plain {@link Definition} data union.
1139
1149
  *
1140
1150
  * @remarks
1141
1151
  * A `unique symbol` brand check (`Reflect.get`): a plain subject
1142
1152
  * is an open record whose values may legally be functions, so a
1143
- * method-presence check (`typeof value.build === 'function'`) is FORGEABLE —
1153
+ * method-presence check (`typeof value.build === 'function'`) is forgeable —
1144
1154
  * this guard is not. A module-owned `unique symbol` cannot be produced by
1145
1155
  * `JSON.parse` or written by any consumer that does not import
1146
1156
  * `DEFINITION_BUILDER_BRAND`, so plain data can never forge it. Total: a
@@ -1164,12 +1174,12 @@ function isDefinitionBuilder(value) {
1164
1174
  return isObject(value) && Reflect.get(value, DEFINITION_BUILDER_BRAND) === true;
1165
1175
  }
1166
1176
  /**
1167
- * Determines whether a value is a `SubjectBuilder` ENTITY — the brand-guarded
1177
+ * Determines whether a value is a `SubjectBuilder` entity — the brand-guarded
1168
1178
  * stateful workspace, not the plain {@link Subject} data record.
1169
1179
  *
1170
1180
  * @remarks
1171
1181
  * A `unique symbol` brand check (`Reflect.get`), distinct from
1172
- * {@link isDefinitionBuilder} — the two entities can never match each other's
1182
+ * {@link isDefinitionBuilder} — neither entity can ever match the other's
1173
1183
  * guard. Total: a non-object, a missing brand, or a hostile prototype all
1174
1184
  * return `false`, never throw.
1175
1185
  *
@@ -1197,7 +1207,7 @@ function isSubjectBuilder(value) {
1197
1207
  *
1198
1208
  * @remarks
1199
1209
  * Display-only: the joined form is how a field appears in traces and derived
1200
- * overlays; it is NOT re-parsed into a path (a string stays ONE key).
1210
+ * overlays; it is never re-parsed into a path (a string stays a single key).
1201
1211
  *
1202
1212
  * @param field - The field path to format
1203
1213
  * @returns The display string
@@ -1242,7 +1252,7 @@ function clamp(value, limit) {
1242
1252
  *
1243
1253
  * @remarks
1244
1254
  * The range test behind the `between` and `outside` `Comparison` operators: only
1245
- * the FIRST TWO elements of `range` are read, both ends are inclusive, and a
1255
+ * the first two elements of `range` are read, each end is inclusive, and a
1246
1256
  * non-numeric `value`, a non-array `range`, a `range` shorter than two
1247
1257
  * elements, or a non-numeric bound all report `false`. `outside` is the pure
1248
1258
  * negation of this predicate, so a malformed range reads as outside.
@@ -1306,9 +1316,9 @@ function emptyAggregate(aggregation) {
1306
1316
  * source passes its value through; `field` and `range` coerce through the
1307
1317
  * contracts `parseNumberField`, so a non-finite subject number or a
1308
1318
  * non-numeric string is unresolvable and takes `fallback`; `lookup` reads only
1309
- * OWN table keys, and a missing or `null` field takes `fallback` directly
1319
+ * the table's own keys, and a missing or `null` field takes `fallback` directly
1310
1320
  * rather than letting a `''` key intercept absent data. A `range` scans its
1311
- * bands in order and the FIRST match wins — a band without `bounds` is a
1321
+ * bands in order and the first match wins — a band without `bounds` is a
1312
1322
  * catch-all and an absent bound side is open. A malformed factor carrying no
1313
1323
  * source at all takes `fallback` rather than crashing.
1314
1324
  *
@@ -1340,7 +1350,7 @@ function resolveSource(source, subject, fallback) {
1340
1350
  const value = parseNumberField(subject, source.field);
1341
1351
  if (value === void 0) return fallback;
1342
1352
  for (const range of source.ranges) {
1343
- if (typeof range !== "object" || range === null) continue;
1353
+ if (!isObject(range)) continue;
1344
1354
  const limit = range.bounds;
1345
1355
  if (!limit) return range.value;
1346
1356
  const aboveMinimum = limit.minimum === void 0 || value >= limit.minimum;
@@ -1358,9 +1368,9 @@ function resolveSource(source, subject, fallback) {
1358
1368
  * @remarks
1359
1369
  * `Math.round` semantics — halves round toward `+∞` (`2.5` → `3`, `-2.5` → `-2`).
1360
1370
  * A negative precision rounds at whole-number scales (`-1` → tens, `-2` →
1361
- * hundreds). An EXTREME precision whose scale factor overflows the double range
1371
+ * hundreds). A precision whose scale factor overflows the double range
1362
1372
  * (`10^p` → `Infinity` at roughly `p > 308`, `0` at roughly `p < -323`) returns
1363
- * the value UNCHANGED — passthrough, never `NaN`.
1373
+ * the value unchanged — passthrough, never `NaN`.
1364
1374
  *
1365
1375
  * @param value - The number to round
1366
1376
  * @param precision - Decimal places to keep (defaults to `0`)
@@ -1378,7 +1388,7 @@ function resolveSource(source, subject, fallback) {
1378
1388
  */
1379
1389
  function roundTo(value, precision = 0) {
1380
1390
  const factor = Math.pow(10, precision);
1381
- if (!Number.isFinite(factor) || factor === 0) return value;
1391
+ if (!isFiniteNumber(factor) || factor === 0) return value;
1382
1392
  return Math.round(value * factor) / factor;
1383
1393
  }
1384
1394
  /**
@@ -1388,7 +1398,7 @@ function roundTo(value, precision = 0) {
1388
1398
  * @remarks
1389
1399
  * This is the derivation-bookkeeping equality of the chaining reasoners: the
1390
1400
  * logical overlay and the inferential fact-dedupe compare with it so a
1391
- * NaN-valued conclusion or fact term derives exactly ONCE and the fixpoint
1401
+ * NaN-valued conclusion or fact term derives exactly once and the fixpoint
1392
1402
  * converges (raw `===` would re-derive it every iteration, never converging).
1393
1403
  * It matches `Array.prototype.includes` semantics — the same membership test
1394
1404
  * the `any` / `none` comparisons use.
@@ -1415,7 +1425,7 @@ function equalValues(left, right) {
1415
1425
  * @remarks
1416
1426
  * The shared evaluation-order helper of the quantitative (factors) and logical
1417
1427
  * (rules) reasoners: lower priorities run first, an absent `priority` defaults
1418
- * to `0`, equal priorities keep DECLARATION order (stable), and the input array
1428
+ * to `0`, equal priorities keep declaration order (stable), and the input array
1419
1429
  * is never mutated. An array hole, `null`, or other non-record entry is dropped
1420
1430
  * rather than sorted — the output may be shorter than the input.
1421
1431
  *
@@ -1433,18 +1443,18 @@ function equalValues(left, right) {
1433
1443
  function sortByPriority(items) {
1434
1444
  const usable = [];
1435
1445
  for (const item of items) {
1436
- if (typeof item !== "object" || item === null) continue;
1446
+ if (!isObject(item)) continue;
1437
1447
  usable.push(item);
1438
1448
  }
1439
1449
  return usable.sort((left, right) => (left.priority ?? 0) - (right.priority ?? 0));
1440
1450
  }
1441
1451
  /**
1442
- * Collects the ids that appear MORE THAN ONCE in an id-carrying list — each
1452
+ * Collects the ids that appear more than once in an id-carrying list — each
1443
1453
  * duplicated id reported once, in first-occurrence order.
1444
1454
  *
1445
1455
  * @remarks
1446
1456
  * The shared uniqueness scan behind every reasoner's `validate()` duplicate-id
1447
- * WARNINGS (rules, groups, factors, equations, inferences). Runtime stays
1457
+ * warnings (rules, groups, factors, equations, inferences). Runtime stays
1448
1458
  * permissive about duplicates (first/last-wins artifacts) — this helper only
1449
1459
  * surfaces them.
1450
1460
  *
@@ -1470,7 +1480,7 @@ function findDuplicates(items) {
1470
1480
  * @remarks
1471
1481
  * Keys both stored facts and premise patterns (both are `Fact`-shaped) for
1472
1482
  * {@link indexByArity}: `matchFacts` already rejects an arity mismatch, so
1473
- * narrowing a same-predicate bucket to same-predicate-AND-same-arity only
1483
+ * narrowing a same-predicate bucket to one of the same predicate and arity only
1474
1484
  * excludes candidates that could never unify anyway. Only `predicate` — the
1475
1485
  * one free-form, adversary-controlled part — is length-prefixed
1476
1486
  * (`length + ':' + predicate`), mirroring {@link factToKey}'s framing so a
@@ -1499,7 +1509,7 @@ function factToArityKey(fact) {
1499
1509
  * @remarks
1500
1510
  * The index behind the inferential reasoner's same-predicate-and-arity join
1501
1511
  * scans (`#findAllBindings` / `#calculatePremiseConfidence`): `matchFacts`
1502
- * already rejects a predicate OR arity mismatch, so restricting a premise's
1512
+ * already rejects a predicate or an arity mismatch, so restricting a premise's
1503
1513
  * search to its own predicate+arity bucket changes nothing but the cost — the
1504
1514
  * surviving matches and their append order are identical to a predicate-only
1505
1515
  * index. Append order within a bucket is preserved, so a "first match wins"
@@ -1535,9 +1545,9 @@ function indexByArity(facts) {
1535
1545
  * forward-chaining dedupe. Primitives (and `null`) key by value, typeof-prefixed
1536
1546
  * so `1` (`number:1`) never collides with `'1'` (`string:1`); `-0` folds to `+0`
1537
1547
  * (both `number:0`) and `NaN` is self-consistent (`number:NaN`), matching
1538
- * SameValueZero. Objects and functions key by REFERENCE through `identities` — a
1548
+ * SameValueZero. Objects and functions key by reference through `identities` — a
1539
1549
  * first sighting is assigned the map's current size as its id, so distinct
1540
- * objects never collide and the SAME reference always reproduces its key.
1550
+ * objects never collide and the same reference always reproduces its key.
1541
1551
  *
1542
1552
  * @param term - The term to key
1543
1553
  * @param identities - The caller-created reference-identity ledger, threaded across a dedupe pass (a newly seen object/function is registered in it)
@@ -1553,7 +1563,7 @@ function indexByArity(facts) {
1553
1563
  * ```
1554
1564
  */
1555
1565
  function termToKey(term, identities) {
1556
- if (typeof term === "object" && term !== null || typeof term === "function") {
1566
+ if (isObject(term) || isFunction(term)) {
1557
1567
  const existing = identities.get(term);
1558
1568
  if (existing !== void 0) return `${typeof term}:#${existing}`;
1559
1569
  const id = identities.size;
@@ -1564,14 +1574,14 @@ function termToKey(term, identities) {
1564
1574
  }
1565
1575
  /**
1566
1576
  * Derives a fact's canonical dedup key — predicate + arity + per-term
1567
- * SameValueZero identity (confidence is NOT part of it).
1577
+ * SameValueZero identity, with confidence excluded from it.
1568
1578
  *
1569
1579
  * @remarks
1570
1580
  * The dedup key of the inferential reasoner's forward fixpoint: two facts with
1571
1581
  * the same predicate, arity, and SameValueZero-equal terms share a key (so a
1572
1582
  * NaN-term fact derives once and ±0 collapse keeping the first), while
1573
1583
  * confidence never enters the key. Each part — the predicate, the stringified
1574
- * arity, and every {@link termToKey} — is LENGTH-PREFIXED (`length + ':' + part`)
1584
+ * arity, and every {@link termToKey} — is length-prefixed (`length + ':' + part`)
1575
1585
  * before joining, so the delimiter can never be forged by an adversarial string
1576
1586
  * term embedding it: two distinct facts always produce distinct keys, even when
1577
1587
  * a term string contains the delimiter (an injective framing raw joining lacked).
@@ -1602,10 +1612,10 @@ function factToKey(fact, identities) {
1602
1612
  *
1603
1613
  * @remarks
1604
1614
  * The bidirectional unification of the inferential reasoner: a `'?'`-prefixed
1605
- * string term on EITHER side (pattern or candidate) is a variable that binds to
1615
+ * string term on either side (pattern or candidate) is a variable that binds to
1606
1616
  * the opposite term (the `'?'` prefix is kept in the binding key), while
1607
1617
  * consistency is enforced within the match — a variable seen twice must bind the
1608
- * SAME value (raw `!==`) or the whole match fails. A predicate mismatch or an
1618
+ * same value (raw `!==`) or the whole match fails. A predicate mismatch or an
1609
1619
  * arity (term-count) mismatch fails immediately; non-variable terms must be
1610
1620
  * strictly (`===`) equal.
1611
1621
  *
@@ -1628,11 +1638,11 @@ function matchFacts(pattern, candidate) {
1628
1638
  for (let index = 0; index < pattern.terms.length; index++) {
1629
1639
  const patternTerm = pattern.terms[index];
1630
1640
  const factTerm = candidate.terms[index];
1631
- if (typeof patternTerm === "string" && patternTerm.startsWith("?")) {
1641
+ if (isString(patternTerm) && patternTerm.startsWith("?")) {
1632
1642
  if (patternTerm in bindings) {
1633
1643
  if (bindings[patternTerm] !== factTerm) return void 0;
1634
1644
  } else bindings[patternTerm] = factTerm;
1635
- } else if (typeof factTerm === "string" && factTerm.startsWith("?")) {
1645
+ } else if (isString(factTerm) && factTerm.startsWith("?")) {
1636
1646
  if (factTerm in bindings) {
1637
1647
  if (bindings[factTerm] !== patternTerm) return void 0;
1638
1648
  } else bindings[factTerm] = patternTerm;
@@ -1647,7 +1657,7 @@ function matchFacts(pattern, candidate) {
1647
1657
  * @remarks
1648
1658
  * The pattern-instantiation step of the inferential reasoner: a `'?'`-prefixed
1649
1659
  * string term that is present in `bindings` is replaced by its bound value;
1650
- * every other term (constants and UNBOUND variables alike) is kept verbatim. The
1660
+ * every other term (constants and unbound variables alike) is kept verbatim. The
1651
1661
  * returned fact is a fresh copy (`{ ...fact, terms }`) — the input is never
1652
1662
  * mutated.
1653
1663
  *
@@ -1664,7 +1674,7 @@ function matchFacts(pattern, candidate) {
1664
1674
  */
1665
1675
  function instantiateFact(fact, bindings) {
1666
1676
  const terms = fact.terms.map((term) => {
1667
- if (typeof term === "string" && term.startsWith("?") && term in bindings) return bindings[term];
1677
+ if (isString(term) && term.startsWith("?") && term in bindings) return bindings[term];
1668
1678
  return term;
1669
1679
  });
1670
1680
  return {
@@ -1674,7 +1684,7 @@ function instantiateFact(fact, bindings) {
1674
1684
  }
1675
1685
  /**
1676
1686
  * Computes the confidence a set of matched premises contributes to a derived
1677
- * fact — the product of each premise's FIRST matching fact's confidence.
1687
+ * fact — the product of each premise's first matching fact's confidence.
1678
1688
  *
1679
1689
  * @remarks
1680
1690
  * The confidence half of the inferential reasoner's forward derivation: each
@@ -1715,7 +1725,7 @@ function computePremiseConfidence(premises, index, bindings) {
1715
1725
  * inferential reasoner's subject-injection step.
1716
1726
  *
1717
1727
  * @remarks
1718
- * Every own subject field EXCEPT `id` becomes a `has(key, value)` fact at full
1728
+ * Every own subject field except `id` becomes a `has(key, value)` fact at full
1719
1729
  * `DEFAULT_CONFIDENCE`; `null` / `undefined` and any `object` (including arrays)
1720
1730
  * value is skipped. The returned `trace` carries one line per injection plus a
1721
1731
  * final count when at least one fact was produced, so no caller-owned
@@ -1738,7 +1748,7 @@ function subjectToFacts(subject) {
1738
1748
  if (key === "id") continue;
1739
1749
  const value = subject[key];
1740
1750
  if (value === void 0 || value === null) continue;
1741
- if (typeof value === "object") continue;
1751
+ if (isObject(value)) continue;
1742
1752
  facts.push({
1743
1753
  id: `subject:${key}`,
1744
1754
  predicate: "has",
@@ -1793,21 +1803,21 @@ function findUnboundVariables(inference) {
1793
1803
  return unbound;
1794
1804
  }
1795
1805
  /**
1796
- * Determines whether a symbolic expression contains an UNBOUND occurrence of a
1806
+ * Determines whether a symbolic expression contains an unbound occurrence of a
1797
1807
  * target variable.
1798
1808
  *
1799
1809
  * @remarks
1800
1810
  * The variable-presence probe of the symbolic reasoner's isolation: a `variable`
1801
- * node matches only when its name is `target` AND `target` is not already in
1811
+ * node matches only when its name is `target` and `target` is not already in
1802
1812
  * `bindings` (a pre-bound target is a known value, not an unknown to isolate); a
1803
1813
  * `constant` never matches; an `operation` recurses into both operands (the
1804
- * `right` operand may be absent on a unary node). The walk is an ITERATIVE
1814
+ * `right` operand may be absent on a unary node). The walk is an iterative
1805
1815
  * worklist (never recursive) with short-circuit `true` on the first hit, so it
1806
1816
  * stays total on pathologically deep expression trees.
1807
1817
  *
1808
1818
  * @param expression - The expression to probe
1809
1819
  * @param target - The variable name being sought
1810
- * @param bindings - The known bindings (a bound target does NOT count as present)
1820
+ * @param bindings - The known bindings (a bound target does not count as present)
1811
1821
  * @returns True if an unbound `target` occurs in the expression; false otherwise
1812
1822
  *
1813
1823
  * @example
@@ -1833,7 +1843,7 @@ function containsVariable(expression, target, bindings) {
1833
1843
  return false;
1834
1844
  }
1835
1845
  /**
1836
- * Inverts a `x op right = value` step, solving for the LEFT operand `x`.
1846
+ * Inverts a `x op right = value` step, solving for the left operand `x`.
1837
1847
  *
1838
1848
  * @remarks
1839
1849
  * The left-operand inverse of the symbolic reasoner's isolation: `add` inverts
@@ -1868,7 +1878,7 @@ function invertLeft(operator, value, rightValue) {
1868
1878
  }
1869
1879
  }
1870
1880
  /**
1871
- * Inverts a `left op x = value` step, solving for the RIGHT operand `x`.
1881
+ * Inverts a `left op x = value` step, solving for the right operand `x`.
1872
1882
  *
1873
1883
  * @remarks
1874
1884
  * The right-operand inverse of the symbolic reasoner's isolation: `add` inverts
@@ -1910,7 +1920,7 @@ function invertRight(operator, value, leftValue) {
1910
1920
  * by zero is `NaN` (never a throw), the unary operations (`round` / `ceil` /
1911
1921
  * `floor` / `abs`) ignore `right`, and `percentage` is `left * (right / 100)`.
1912
1922
  * `operator` is typed `string` because untrusted definitions reach here
1913
- * unchecked; the ONE throwing path is the unknown-operator default, which
1923
+ * unchecked; the only throwing path is the unknown-operator default, which
1914
1924
  * throws `ReasonError('OPERATOR', …, { operator })` and is caught per equation
1915
1925
  * upstream.
1916
1926
  *
@@ -1981,7 +1991,7 @@ function resolveOperand(operation, operand) {
1981
1991
  * The shared atom-walk behind both {@link extractConclusions} and the raters'
1982
1992
  * conclusion merge: an `atom` yields itself; a compound flattens its operands in
1983
1993
  * authored order, so a later operand's atoms follow an earlier one's. The walk is
1984
- * an ITERATIVE explicit-stack traversal (never recursive), so it stays total on
1994
+ * an iterative explicit-stack traversal (never recursive), so it stays total on
1985
1995
  * pathologically deep expression trees; a hole in an `operands` array is skipped,
1986
1996
  * matching `flatMap`'s hole-skipping behavior.
1987
1997
  *
@@ -2016,13 +2026,13 @@ function extractAtoms(expression) {
2016
2026
  }
2017
2027
  /**
2018
2028
  * Flattens a logical conclusion expression into its asserted `field = value`
2019
- * pairs — connectives IGNORED.
2029
+ * pairs, ignoring the connectives.
2020
2030
  *
2021
2031
  * @remarks
2022
2032
  * The conclusion-extraction step of the logical reasoner's chaining: every
2023
2033
  * `atom` inside the expression asserts its `formatField(check.field) =
2024
2034
  * check.value` pair, and compounds are walked without regard to the connective
2025
- * (an atom under `not` / `or` is asserted the same way). Later operands WIN on
2035
+ * (an atom under `not` / `or` is asserted the same way). A later operand wins on
2026
2036
  * a key clash (`Object.assign` order). Recursion runs through this exported
2027
2037
  * function itself; the derived-overlay keys are `formatField` strings (an array
2028
2038
  * field path flattens to its dot-joined form).
@@ -2049,13 +2059,13 @@ function extractConclusions(expression) {
2049
2059
  *
2050
2060
  * @remarks
2051
2061
  * The reported keys are the `formatField`-flattened overlay keys an array-path
2052
- * conclusion atom writes ANYWHERE among `rules` that an array-path premise atom
2053
- * also reads ANYWHERE among `rules`. The cross-rule authoring-time footgun
2062
+ * conclusion atom writes anywhere among `rules` that an array-path premise atom
2063
+ * also reads anywhere among `rules`. The cross-rule authoring-time footgun
2054
2064
  * probe behind `LogicalReasoner.validate`'s overlay-key-mismatch warning: a
2055
- * logical conclusion's derived overlay is a FLAT record keyed by
2065
+ * logical conclusion's derived overlay is a flat record keyed by
2056
2066
  * `formatField(check.field)` — an array `FieldPath` dot-joins into one string
2057
- * key. A premise that reads the same field through a DOTTED-STRING path resolves
2058
- * that flat key correctly, but a premise that reads it through an ARRAY path
2067
+ * key. A premise that reads the same field through a dotted-string path resolves
2068
+ * that flat key correctly, but a premise that reads it through an array path
2059
2069
  * calls `resolveField`, which descends key-by-key into nesting the flat
2060
2070
  * overlay never created, so the chain silently fails to connect. Collects the
2061
2071
  * flattened keys of every array-path conclusion atom across `rules` (once
@@ -2081,7 +2091,7 @@ function findOverlayMismatches(rules) {
2081
2091
  const writeKeys = [];
2082
2092
  const seenWrites = /* @__PURE__ */ new Set();
2083
2093
  for (const candidate of rules) for (const atomLeaf of extractAtoms(candidate.conclusion)) {
2084
- if (!Array.isArray(atomLeaf.check.field)) continue;
2094
+ if (!isArray(atomLeaf.check.field)) continue;
2085
2095
  const key = formatField(atomLeaf.check.field);
2086
2096
  if (seenWrites.has(key)) continue;
2087
2097
  seenWrites.add(key);
@@ -2089,7 +2099,7 @@ function findOverlayMismatches(rules) {
2089
2099
  }
2090
2100
  const readKeys = /* @__PURE__ */ new Set();
2091
2101
  for (const candidate of rules) for (const premise of candidate.premises) for (const atomLeaf of extractAtoms(premise)) {
2092
- if (!Array.isArray(atomLeaf.check.field)) continue;
2102
+ if (!isArray(atomLeaf.check.field)) continue;
2093
2103
  readKeys.add(formatField(atomLeaf.check.field));
2094
2104
  }
2095
2105
  return writeKeys.filter((key) => readKeys.has(key));
@@ -2197,15 +2207,15 @@ function definitionToEnvelope(definition) {
2197
2207
  }
2198
2208
  /**
2199
2209
  * Inserts `item` into an id-keyed collection, deduping any existing element
2200
- * sharing its id, then placing it at the END (or immediately AFTER `target`).
2210
+ * sharing its id, then placing it at the end, or immediately after `target`.
2201
2211
  *
2202
2212
  * @remarks
2203
- * Insertion DEDUPES on the id first: `filtered` is `items` with every `item.id`
2213
+ * Insertion dedupes on the id first: `filtered` is `items` with every `item.id`
2204
2214
  * twin removed, because an input array may already carry same-id twins.
2205
- * Re-appending an existing id therefore REPOSITIONS it rather than updating it
2215
+ * Re-appending an existing id therefore repositions it rather than updating it
2206
2216
  * in place — {@link replaceById} is the position-preserving alternative. With
2207
2217
  * no `target`, `item` lands at the end; with a `target`, it lands immediately
2208
- * after the element whose `id === target` (searched in the DEDUPED array). A
2218
+ * after the element whose `id === target` (searched in the deduped array). A
2209
2219
  * `target` naming no element throws {@link ReasonError} (`'TARGET'`).
2210
2220
  *
2211
2221
  * @typeParam T - An id-carrying element type
@@ -2240,7 +2250,7 @@ function appendById(items, item, target) {
2240
2250
  }
2241
2251
  /**
2242
2252
  * Inserts `item` into an id-keyed collection, deduping any existing element
2243
- * sharing its id, then placing it at the START (or immediately BEFORE `target`).
2253
+ * sharing its id, then placing it at the start, or immediately before `target`.
2244
2254
  *
2245
2255
  * @remarks
2246
2256
  * Mirrors {@link appendById}'s dedup-then-insert semantics exactly, only the
@@ -2280,7 +2290,7 @@ function prependById(items, item, target) {
2280
2290
  ];
2281
2291
  }
2282
2292
  /**
2283
- * Swaps the element sharing `item.id` IN PLACE, preserving its position.
2293
+ * Swaps the element sharing `item.id` in place, preserving its position.
2284
2294
  *
2285
2295
  * @remarks
2286
2296
  * The position-preserving update primitive — unlike {@link appendById}, which
@@ -2337,11 +2347,11 @@ function removeById(items, id) {
2337
2347
  *
2338
2348
  * @remarks
2339
2349
  * A Strategic-Merge-Patch-style id-keyed upsert:
2340
- * the result is ordered by `incoming`'s id order FIRST (each element resolved
2350
+ * the result is ordered by `incoming`'s id order first (each element resolved
2341
2351
  * through `resolve` when its id also exists in `base`, defaulting to
2342
- * incoming-wins-wholesale), THEN the `base`-only survivors in `base`'s own
2352
+ * incoming-wins-wholesale), then the `base`-only survivors in `base`'s own
2343
2353
  * order (retained, never deleted — merge is additive). Same-id twins within
2344
- * EITHER input are deduped to their first occurrence.
2354
+ * either input are deduped to their first occurrence.
2345
2355
  *
2346
2356
  * @typeParam T - An id-carrying element type
2347
2357
  * @param base - The base collection
@@ -2380,7 +2390,7 @@ function mergeById(base, incoming, resolve) {
2380
2390
  * insert at the end, or immediately after `target`.
2381
2391
  *
2382
2392
  * @remarks
2383
- * Group order is COSMETIC (group aggregation is order-independent) but honored
2393
+ * Group order is cosmetic (group aggregation is order-independent) but honored
2384
2394
  * uniformly, same as every `append*` helper. Composes with {@link appendFactor}:
2385
2395
  * `appendGroup(def, appendFactor(group, factor))`.
2386
2396
  *
@@ -2427,8 +2437,8 @@ function prependGroup(definition, group, target) {
2427
2437
  };
2428
2438
  }
2429
2439
  /**
2430
- * Swaps the group sharing `group.id` in a {@link QuantitativeDefinition} IN
2431
- * PLACE, preserving its position (appends when absent).
2440
+ * Swaps the group sharing `group.id` in a {@link QuantitativeDefinition} in
2441
+ * place, preserving its position (appended when absent).
2432
2442
  *
2433
2443
  * @param definition - The definition to update
2434
2444
  * @param group - The replacement group
@@ -2475,9 +2485,9 @@ function removeGroup(definition, id) {
2475
2485
  * at the end, or immediately after `target`.
2476
2486
  *
2477
2487
  * @remarks
2478
- * Factor order is LOAD-BEARING: the same-priority tiebreak is declaration
2488
+ * Factor order is load-bearing: the same-priority tiebreak is declaration
2479
2489
  * order ({@link sortByPriority} is a stable ascending sort). Operates on the
2480
- * factor's DIRECT container — compose into a definition through
2490
+ * factor's direct container — compose into a definition through
2481
2491
  * `appendGroup(def, appendFactor(group, factor))`.
2482
2492
  *
2483
2493
  * @param group - The group to insert into
@@ -2523,8 +2533,8 @@ function prependFactor(group, factor, target) {
2523
2533
  };
2524
2534
  }
2525
2535
  /**
2526
- * Swaps the factor sharing `factor.id` in a {@link FactorGroup} IN PLACE,
2527
- * preserving its position (appends when absent).
2536
+ * Swaps the factor sharing `factor.id` in a {@link FactorGroup} in place,
2537
+ * preserving its position (appended when absent).
2528
2538
  *
2529
2539
  * @param group - The group to update
2530
2540
  * @param factor - The replacement factor
@@ -2570,7 +2580,7 @@ function removeFactor(group, id) {
2570
2580
  * at the end, or immediately after `target`.
2571
2581
  *
2572
2582
  * @remarks
2573
- * Order is LOAD-BEARING: the forward conclusion is the LAST declared
2583
+ * Order is load-bearing: the forward conclusion is the last declared
2574
2584
  * non-disabled rule, so `appendRule` without a `target` makes the new rule the
2575
2585
  * conclusion.
2576
2586
  *
@@ -2617,8 +2627,8 @@ function prependRule(definition, rule, target) {
2617
2627
  };
2618
2628
  }
2619
2629
  /**
2620
- * Swaps the rule sharing `rule.id` in a {@link LogicalDefinition} IN PLACE,
2621
- * preserving its position (appends when absent).
2630
+ * Swaps the rule sharing `rule.id` in a {@link LogicalDefinition} in place,
2631
+ * preserving its position (appended when absent).
2622
2632
  *
2623
2633
  * @param definition - The definition to update
2624
2634
  * @param rule - The replacement rule
@@ -2665,8 +2675,8 @@ function removeRule(definition, id) {
2665
2675
  * then-insert at the end, or immediately after `target`.
2666
2676
  *
2667
2677
  * @remarks
2668
- * Order is STRONGLY load-bearing: equations solve strictly in order and each
2669
- * rounded solution feeds forward.
2678
+ * Order is load-bearing: equations solve strictly in order and each rounded
2679
+ * solution feeds forward.
2670
2680
  *
2671
2681
  * @param definition - The definition to insert into
2672
2682
  * @param equation - The equation to insert
@@ -2711,8 +2721,8 @@ function prependEquation(definition, equation, target) {
2711
2721
  };
2712
2722
  }
2713
2723
  /**
2714
- * Swaps the equation sharing `equation.id` in a {@link SymbolicDefinition} IN
2715
- * PLACE, preserving its position (appends when absent).
2724
+ * Swaps the equation sharing `equation.id` in a {@link SymbolicDefinition} in
2725
+ * place, preserving its position (appended when absent).
2716
2726
  *
2717
2727
  * @param definition - The definition to update
2718
2728
  * @param equation - The replacement equation
@@ -2786,7 +2796,7 @@ function addVariable(definition, name, value) {
2786
2796
  * Removes one entry of a {@link SymbolicDefinition}'s `variables`.
2787
2797
  *
2788
2798
  * @remarks
2789
- * The destructure-rest form OMITS the key entirely (never sets it to
2799
+ * The destructure-rest form omits the key entirely (never sets it to
2790
2800
  * `undefined`), keeping the result exact-record valid. A no-op (fresh copy)
2791
2801
  * when `name` is absent.
2792
2802
  *
@@ -2813,7 +2823,7 @@ function removeVariable(definition, name) {
2813
2823
  * insert at the end, or immediately after `target`.
2814
2824
  *
2815
2825
  * @remarks
2816
- * `Fact.id` is an AUTHORING label — the runtime content-dedups facts by
2826
+ * `Fact.id` is an authoring label — the runtime content-dedups facts by
2817
2827
  * predicate+arity+terms ({@link factToKey}), independently of this helper's
2818
2828
  * id-keyed dedup.
2819
2829
  *
@@ -2860,8 +2870,8 @@ function prependFact(definition, fact, target) {
2860
2870
  };
2861
2871
  }
2862
2872
  /**
2863
- * Swaps the fact sharing `fact.id` in an {@link InferentialDefinition} IN
2864
- * PLACE, preserving its position (appends when absent).
2873
+ * Swaps the fact sharing `fact.id` in an {@link InferentialDefinition} in
2874
+ * place, preserving its position (appended when absent).
2865
2875
  *
2866
2876
  * @param definition - The definition to update
2867
2877
  * @param fact - The replacement fact
@@ -2908,7 +2918,7 @@ function removeFact(definition, id) {
2908
2918
  * dedup-then-insert at the end, or immediately after `target`.
2909
2919
  *
2910
2920
  * @remarks
2911
- * Order is LOAD-BEARING: backward proving iterates in declaration order and
2921
+ * Order is load-bearing: backward proving iterates in declaration order and
2912
2922
  * returns on first success.
2913
2923
  *
2914
2924
  * @param definition - The definition to insert into
@@ -2961,7 +2971,7 @@ function prependInference(definition, inference, target) {
2961
2971
  }
2962
2972
  /**
2963
2973
  * Swaps the inference sharing `inference.id` in an {@link InferentialDefinition}
2964
- * IN PLACE, preserving its position (appends when absent).
2974
+ * in place, preserving its position (appended when absent).
2965
2975
  *
2966
2976
  * @param definition - The definition to update
2967
2977
  * @param inference - The replacement inference
@@ -3008,10 +3018,10 @@ function removeInference(definition, id) {
3008
3018
  * Reconciles two {@link QuantitativeDefinition}s onto `base`'s id.
3009
3019
  *
3010
3020
  * @remarks
3011
- * The merge is ADDITIVE: a base-only group survives into the result and is
3021
+ * The merge is additive: a base-only group survives into the result and is
3012
3022
  * never deleted, and a scalar absent from `incoming` keeps its base value —
3013
3023
  * merge never clears a field, which is what {@link clearQuantitativeDefinition}
3014
- * is for. `groups` merges through {@link mergeById}; a matched (same-id) PAIR of
3024
+ * is for. `groups` merges through {@link mergeById}; a matched (same-id) pair of
3015
3025
  * groups recurses one level deeper — their `factors` also merge through
3016
3026
  * `mergeById` — the one exception to incoming-wins-wholesale. Every other
3017
3027
  * scalar / value-object field is incoming-wins-when-present, else base kept.
@@ -3049,7 +3059,7 @@ function mergeQuantitativeDefinition(base, incoming) {
3049
3059
  * Reconciles two {@link LogicalDefinition}s onto `base`'s id.
3050
3060
  *
3051
3061
  * @remarks
3052
- * The merge is ADDITIVE: a base-only rule survives into the result and is never
3062
+ * The merge is additive: a base-only rule survives into the result and is never
3053
3063
  * deleted, and a scalar absent from `incoming` keeps its base value — merge
3054
3064
  * never clears a field, which is what {@link clearLogicalDefinition} is for.
3055
3065
  * `rules` merges through {@link mergeById} (incoming-wins-wholesale on a matched
@@ -3082,7 +3092,7 @@ function mergeLogicalDefinition(base, incoming) {
3082
3092
  * Reconciles two {@link SymbolicDefinition}s onto `base`'s id.
3083
3093
  *
3084
3094
  * @remarks
3085
- * The merge is ADDITIVE: a base-only equation or variable survives into the
3095
+ * The merge is additive: a base-only equation or variable survives into the
3086
3096
  * result and is never deleted, and a scalar absent from `incoming` keeps its
3087
3097
  * base value — merge never clears a field, which is what
3088
3098
  * {@link clearSymbolicDefinition} is for. `equations` merges through
@@ -3122,7 +3132,7 @@ function mergeSymbolicDefinition(base, incoming) {
3122
3132
  * Reconciles two {@link InferentialDefinition}s onto `base`'s id.
3123
3133
  *
3124
3134
  * @remarks
3125
- * The merge is ADDITIVE: a base-only inference or fact survives into the result
3135
+ * The merge is additive: a base-only inference or fact survives into the result
3126
3136
  * and is never deleted, and a scalar absent from `incoming` keeps its base
3127
3137
  * value — merge never clears a field, which is what
3128
3138
  * {@link clearInferentialDefinition} is for. `inferences` and `facts` each
@@ -3233,7 +3243,7 @@ function clearInferentialDefinition(definition, key) {
3233
3243
  * Upserts one field of a {@link Subject} — copy-on-write spread.
3234
3244
  *
3235
3245
  * @remarks
3236
- * Named `assignField` rather than `setField` because it RETURNS a fresh subject
3246
+ * Named `assignField` rather than `setField` because it returns a fresh subject
3237
3247
  * instead of writing into the one it is given — `set*` reads as an in-place
3238
3248
  * write. Id-agnostic: overwrites an `id` key like any other field — id
3239
3249
  * protection is an entity's job, not this helper's.
@@ -3260,7 +3270,7 @@ function assignField(subject, key, value) {
3260
3270
  * Deletes one field of a {@link Subject} — destructure-rest omit.
3261
3271
  *
3262
3272
  * @remarks
3263
- * The key is DELETED entirely (never set to `undefined`), keeping the result
3273
+ * The key is deleted entirely (never set to `undefined`), keeping the result
3264
3274
  * exact-record valid. A no-op (fresh copy) when `key` is absent.
3265
3275
  *
3266
3276
  * @param subject - The subject to update
@@ -3315,8 +3325,8 @@ function mergeSubjects(base, incoming) {
3315
3325
  * When `subject.id` is a string, each clone's id is minted
3316
3326
  * `` `${baseId}-${index}` `` (index from `0`); with no string `id`, the clones
3317
3327
  * pass through unchanged (still fresh copies). Pure and deterministic — the
3318
- * same input always produces the same output (run-twice equality) — and does
3319
- * NOT emit. `count <= 0` yields an empty array.
3328
+ * same input always produces the same output (run-twice equality) — and emits
3329
+ * nothing. `count <= 0` yields an empty array.
3320
3330
  *
3321
3331
  * @param subject - The subject to clone
3322
3332
  * @param count - How many clones to produce
@@ -3333,7 +3343,7 @@ function mergeSubjects(base, incoming) {
3333
3343
  function repeatSubject(subject, count) {
3334
3344
  const baseId = subject.id;
3335
3345
  const clones = [];
3336
- for (let index = 0; index < count; index += 1) clones.push(typeof baseId === "string" ? {
3346
+ for (let index = 0; index < count; index += 1) clones.push(isString(baseId) ? {
3337
3347
  ...subject,
3338
3348
  id: `${baseId}-${index}`
3339
3349
  } : { ...subject });
@@ -3346,8 +3356,8 @@ function repeatSubject(subject, count) {
3346
3356
  *
3347
3357
  * @remarks
3348
3358
  * The safe inverse of the builders: `parseJSONAs` composed with the data guard
3349
- * {@link isDefinition}. A built definition body IS the durable JSON payload —
3350
- * `JSON.stringify(definition)` round-trips through `parseDefinition`. Two
3359
+ * {@link isDefinition}. A built definition body is the durable JSON payload —
3360
+ * `JSON.stringify(definition)` round-trips through `parseDefinition`. The
3351
3361
  * authoring hazards: a required `Check.value: undefined` drops its key on
3352
3362
  * `JSON.stringify` (author `null` instead), and a `Fact.terms` element of
3353
3363
  * `undefined` serializes to `null` (terms must be JSON-safe scalars/strings).
@@ -3374,16 +3384,16 @@ function parseDefinition(json) {
3374
3384
  * {@link ReasonerInterface}s.
3375
3385
  *
3376
3386
  * @remarks
3377
- * Holds NO strategy-specific logic: dispatch is purely a registry lookup by
3387
+ * Holds no strategy-specific logic: dispatch is purely a registry lookup by
3378
3388
  * `definition.reasoning` (one reasoner per reasoning; re-registration
3379
3389
  * replaces). A missing reasoner throws `MISSING` and a pre-run validation
3380
- * failure (when the `validate` option is on) throws `INVALID` — both BYPASS
3381
- * `bail` and emit nothing. A reasoner throw emits `error` with the raw thrown
3390
+ * failure (when the `validate` option is on) throws `INVALID` — each bypasses
3391
+ * `bail` and emits nothing. A reasoner throw emits `error` with the raw thrown
3382
3392
  * value, then rethrows under `bail: true` (the default) or converts to a
3383
- * type-shaped failure result under `bail: false`; only SUCCESSFUL results emit
3393
+ * type-shaped failure result under `bail: false`; only a successful result emits
3384
3394
  * `reason` (synchronously, before returning). The batch overload maps subjects
3385
3395
  * in order (validation, when on, repeats per subject). `destroy()` clears the
3386
- * registry, emits `destroy`, then destroys the emitter LAST and is
3396
+ * registry, emits `destroy`, then destroys the emitter last and is
3387
3397
  * idempotent; every other method afterwards throws `DESTROYED` — only the
3388
3398
  * {@link emitter} getter keeps working.
3389
3399
  *
@@ -3488,7 +3498,7 @@ var Reason = class {
3488
3498
  } catch (error) {
3489
3499
  this.#emitter.emit("error", error);
3490
3500
  if (this.#bail) throw error;
3491
- return buildErrorResult(definition, error instanceof Error ? error.message : String(error));
3501
+ return buildErrorResult(definition, isError(error) ? error.message : String(error));
3492
3502
  }
3493
3503
  }
3494
3504
  #ensureAlive() {
@@ -3502,14 +3512,14 @@ var Reason = class {
3502
3512
  * quantitative and logical reasoners.
3503
3513
  *
3504
3514
  * @remarks
3505
- * TOTAL and strict: `evaluate` never throws (an unknown operator is caught and
3515
+ * Total and strict: `evaluate` never throws (an unknown operator is caught and
3506
3516
  * surfaced as `CheckResult.error` with `met: false`) and never coerces —
3507
3517
  * `equals` / `not` are raw `===` / `!==`, the ordering operators demand numbers
3508
- * on BOTH sides, `any` / `none` demand an array expected value (a non-array is
3509
- * not met for either — `none` is NOT the raw complement of `any` on malformed
3510
- * input), while `outside` IS the pure negation of `between` (so a malformed
3518
+ * on each side, `any` / `none` demand an array expected value (a non-array is
3519
+ * not met for either — `none` is not the raw complement of `any` on malformed
3520
+ * input), while `outside` is the pure negation of `between` (so a malformed
3511
3521
  * range is `outside`). Fields resolve through the core `resolveField` — a string
3512
- * is ONE key, an array descends. Stateless and deterministic.
3522
+ * is a single key, an array descends. Stateless and deterministic.
3513
3523
  *
3514
3524
  * @example
3515
3525
  * ```ts
@@ -3538,7 +3548,7 @@ var Evaluator = class {
3538
3548
  actual
3539
3549
  };
3540
3550
  } catch (error) {
3541
- const message = error instanceof Error ? error.message : String(error);
3551
+ const message = isError(error) ? error.message : String(error);
3542
3552
  return {
3543
3553
  field: check.field,
3544
3554
  met: false,
@@ -3573,7 +3583,7 @@ var Evaluator = class {
3573
3583
  * per-factor pipeline stage.
3574
3584
  *
3575
3585
  * @remarks
3576
- * TOTAL: never throws. Absent-`operand` defaults are operation-specific —
3586
+ * Total: never throws. Absent-`operand` defaults are operation-specific —
3577
3587
  * identity-preserving `1` for `multiply` / `divide` / `power`, `0` for every
3578
3588
  * other binary operation; `round` / `ceil` / `floor` / `abs` are unary and
3579
3589
  * ignore the operand. `divide` by zero yields `NaN` (deliberately not JS's
@@ -3613,11 +3623,11 @@ var Transformer = class {
3613
3623
  * reasoner's group and definition combiner.
3614
3624
  *
3615
3625
  * @remarks
3616
- * TOTAL: never throws. Empty-input identities: `sum` / `average` → `0`,
3626
+ * Total: never throws. Empty-input identities: `sum` / `average` → `0`,
3617
3627
  * `product` → `1`, `minimum` / `maximum` → `NaN` (the deliberate "no data"
3618
- * signal). Weights are honored ONLY when their length matches `values` exactly
3628
+ * signal). Weights are honored only when their length matches `values` exactly
3619
3629
  * (otherwise silently unweighted): a weight multiplies into a `sum`, acts as an
3620
- * EXPONENT for a `product`, is the weight of a weighted mean for `average`
3630
+ * exponent for a `product`, is the weight of a weighted mean for `average`
3621
3631
  * (a zero total weight yields `0`, never a division blow-up), and is ignored by
3622
3632
  * `minimum` / `maximum`. An unknown aggregation yields `0`. Stateless and
3623
3633
  * deterministic.
@@ -3663,21 +3673,21 @@ var Aggregator = class {
3663
3673
  * Performs factor-based numeric scoring.
3664
3674
  *
3665
3675
  * @remarks
3666
- * Each factor runs a fixed pipeline: checks gate (ALL met) → source resolve
3667
- * (`fallback` when unresolvable) → finite check → transforms chain → bounds
3668
- * clamp → finite recheck. Factors evaluate in stable ascending `priority`
3676
+ * Each factor runs a fixed pipeline: checks gate (every check met) → source
3677
+ * resolve (`fallback` when unresolvable) → finite check → transforms chain →
3678
+ * bounds clamp → finite recheck. Factors evaluate in stable ascending `priority`
3669
3679
  * order; a `strict` group is all-or-nothing; a group's value is its `base` plus
3670
- * the weighted aggregation of its APPLIED factors, clamped (never rounded); the
3680
+ * the weighted aggregation of its applied factors, clamped (never rounded); the
3671
3681
  * definition's value is its `base` plus the unweighted aggregation of the
3672
- * APPLIED groups' values, clamped, then rounded to `precision`. A
3682
+ * applied groups' values, clamped, then rounded to `precision`. A
3673
3683
  * required-factor failure or non-finite value appends an error (`success:
3674
3684
  * false`) without aborting — the numeric `value` is always computed. The
3675
- * definition-level value is finite-checked AFTER rounding: a non-finite
3685
+ * definition-level value is finite-checked after rounding: a non-finite
3676
3686
  * aggregate (a `minimum` / `maximum` over zero applied groups) appends an error
3677
3687
  * while the `NaN` stays visible in `value`. Field sources coerce through the
3678
3688
  * contracts `parseNumberField`: a non-finite subject number or a non-numeric
3679
3689
  * string is unresolvable and takes the fallback path. Lookup sources read only
3680
- * OWN table keys, and a missing / `null` field falls back directly. Nothing
3690
+ * the table's own keys, and a missing / `null` field falls back directly. Nothing
3681
3691
  * mutates its inputs; fully deterministic.
3682
3692
  *
3683
3693
  * @example
@@ -3754,7 +3764,7 @@ var QuantitativeReasoner = class {
3754
3764
  definition: definition.id,
3755
3765
  reasoning: this.reasoning
3756
3766
  });
3757
- if (!definition.groups || !Array.isArray(definition.groups)) return {
3767
+ if (!isArray(definition.groups)) return {
3758
3768
  reasoning: "quantitative",
3759
3769
  value: 0,
3760
3770
  groups: [],
@@ -3768,7 +3778,7 @@ var QuantitativeReasoner = class {
3768
3778
  let count = 0;
3769
3779
  const groupResults = [];
3770
3780
  for (const group of definition.groups) {
3771
- if (typeof group !== "object" || group === null) continue;
3781
+ if (!isObject(group)) continue;
3772
3782
  if (group.enabled === false) {
3773
3783
  trace.push(`Skipped group "${group.id}" (disabled)`);
3774
3784
  continue;
@@ -3783,7 +3793,7 @@ var QuantitativeReasoner = class {
3783
3793
  trace.push(`Aggregated ${groupValues.length} groups with "${definition.aggregation}": base=${base}, raw=${value}`);
3784
3794
  value = clamp(value, definition.bounds);
3785
3795
  value = roundTo(value, definition.precision ?? 4);
3786
- if (!Number.isFinite(value)) {
3796
+ if (!isFiniteNumber(value)) {
3787
3797
  const description = Number.isNaN(value) ? "NaN" : String(value);
3788
3798
  trace.push(`Definition "${definition.id}": produced non-finite value (${description})`);
3789
3799
  errors.push(`Definition "${definition.id}" produced non-finite value: ${description}`);
@@ -3829,7 +3839,7 @@ var QuantitativeReasoner = class {
3829
3839
  const factorValues = appliedFactors.map((factor) => factor.value);
3830
3840
  const weightById = /* @__PURE__ */ new Map();
3831
3841
  for (const original of group.factors) {
3832
- if (typeof original !== "object" || original === null) continue;
3842
+ if (!isObject(original)) continue;
3833
3843
  if (!weightById.has(original.id)) weightById.set(original.id, original.weight ?? 1);
3834
3844
  }
3835
3845
  const weights = appliedFactors.map((factor) => weightById.get(factor.id) ?? 1);
@@ -3867,7 +3877,7 @@ var QuantitativeReasoner = class {
3867
3877
  value: 0
3868
3878
  };
3869
3879
  }
3870
- if (!Number.isFinite(raw)) {
3880
+ if (!isFiniteNumber(raw)) {
3871
3881
  const description = Number.isNaN(raw) ? "NaN" : String(raw);
3872
3882
  trace.push(`Factor "${factor.id}": source produced non-finite value (${description})`);
3873
3883
  errors.push(`Factor "${factor.id}" produced non-finite value: ${description}`);
@@ -3881,7 +3891,7 @@ var QuantitativeReasoner = class {
3881
3891
  let value = raw;
3882
3892
  if (factor.transforms && factor.transforms.length > 0) value = this.#transformer.chain(value, factor.transforms);
3883
3893
  value = clamp(value, factor.bounds);
3884
- if (!Number.isFinite(value)) {
3894
+ if (!isFiniteNumber(value)) {
3885
3895
  const description = Number.isNaN(value) ? "NaN" : String(value);
3886
3896
  trace.push(`Factor "${factor.id}": produced non-finite value (${description})`);
3887
3897
  errors.push(`Factor "${factor.id}" produced non-finite value: ${description}`);
@@ -3909,18 +3919,18 @@ var QuantitativeReasoner = class {
3909
3919
  * @remarks
3910
3920
  * Forward chaining is a naive fixpoint capped at `depth` iterations: rules run
3911
3921
  * in ascending `priority` order each pass, and a firing rule's conclusion atoms
3912
- * become a derived overlay that SHADOWS same-named subject fields on later
3922
+ * become a derived overlay that shadows same-named subject fields on later
3913
3923
  * passes (derived keys are `formatField(check.field)` strings). Convergence —
3914
3924
  * one full pass with no new derivation — appends a trace containing
3915
3925
  * `converged`. Final rule results re-evaluate against the settled overlay in
3916
- * ORIGINAL rule order (forward) / priority-sorted order (backward), and the
3917
- * overall `conclusion` is whether the LAST result applied. Backward chaining
3918
- * proves EVERY rule goal-first: an unmet premise triggers sub-goal search
3926
+ * original rule order (forward) / priority-sorted order (backward), and the
3927
+ * overall `conclusion` is whether the last result applied. Backward chaining
3928
+ * proves every rule goal-first: an unmet premise triggers sub-goal search
3919
3929
  * through rules whose conclusion atoms assert the needed `field = value` pair,
3920
3930
  * guarded by a visited-rule set (cycle-safe) plus the depth cap; `not` succeeds
3921
3931
  * when its operand cannot be established (negation-as-failure) and `implies` is
3922
- * vacuously true on an unprovable antecedent. Expression evaluation is EAGER
3923
- * (no short-circuit); conclusion extraction IGNORES connectives — every atom
3932
+ * vacuously true on an unprovable antecedent. Expression evaluation is eager
3933
+ * (no short-circuit); conclusion extraction ignores connectives — every atom
3924
3934
  * inside a conclusion is asserted, even under `not` / `or`. Overlay bookkeeping
3925
3935
  * compares with SameValueZero (`equalValues`), so a NaN-valued conclusion
3926
3936
  * derives once and the fixpoint converges. Nothing mutates its inputs; fully
@@ -3993,7 +4003,7 @@ var LogicalReasoner = class {
3993
4003
  definition: definition.id,
3994
4004
  reasoning: this.reasoning
3995
4005
  });
3996
- if (!definition.rules || !Array.isArray(definition.rules)) return {
4006
+ if (!isArray(definition.rules)) return {
3997
4007
  reasoning: "logical",
3998
4008
  conclusion: false,
3999
4009
  rules: [],
@@ -4022,7 +4032,7 @@ var LogicalReasoner = class {
4022
4032
  const sortedRules = sortByPriority(definition.rules);
4023
4033
  const reportedErrors = /* @__PURE__ */ new Set();
4024
4034
  for (const rule of sortedRules) {
4025
- if (typeof rule !== "object" || rule === null) continue;
4035
+ if (!isObject(rule)) continue;
4026
4036
  if (rule.enabled === false) continue;
4027
4037
  if (!rule.premises || rule.premises.length === 0) {
4028
4038
  errors.push(`Rule "${rule.id}" has no premises — skipped`);
@@ -4064,7 +4074,7 @@ var LogicalReasoner = class {
4064
4074
  };
4065
4075
  const finalResults = [];
4066
4076
  for (const rule of definition.rules) {
4067
- if (typeof rule !== "object" || rule === null) continue;
4077
+ if (!isObject(rule)) continue;
4068
4078
  if (rule.enabled === false) continue;
4069
4079
  if (reportedErrors.has(rule.id)) continue;
4070
4080
  finalResults.push(this.#evaluateRule(rule, finalSubject));
@@ -4080,9 +4090,9 @@ var LogicalReasoner = class {
4080
4090
  const ruleResults = /* @__PURE__ */ new Map();
4081
4091
  if (definition.rules.length === 0) trace.push("No rules defined");
4082
4092
  const sortedRules = sortByPriority(definition.rules.filter((rule) => {
4083
- if (typeof rule !== "object" || rule === null) return false;
4093
+ if (!isObject(rule)) return false;
4084
4094
  if (rule.enabled === false) return false;
4085
- if (!rule.premises || !Array.isArray(rule.premises)) {
4095
+ if (!isArray(rule.premises)) {
4086
4096
  errors.push(`Rule "${rule.id}" has no premises — skipped`);
4087
4097
  return false;
4088
4098
  }
@@ -4093,7 +4103,7 @@ var LogicalReasoner = class {
4093
4103
  return true;
4094
4104
  }));
4095
4105
  for (const rule of definition.rules) {
4096
- if (typeof rule !== "object" || rule === null) continue;
4106
+ if (!isObject(rule)) continue;
4097
4107
  if (rule.enabled === false) trace.push(`Skipped rule "${rule.id}" (disabled)`);
4098
4108
  }
4099
4109
  for (const rule of sortedRules) this.#proveRule(rule, 0, /* @__PURE__ */ new Set(), sortedRules, derived, subject, trace, ruleResults, maxDepth);
@@ -4144,7 +4154,7 @@ var LogicalReasoner = class {
4144
4154
  #proveExpression(expression, rules, depth, visited, derived, subject, trace, ruleResults, maxDepth) {
4145
4155
  if (depth > maxDepth) return false;
4146
4156
  if (expression.form === "atom") for (const rule of rules) {
4147
- if (typeof rule !== "object" || rule === null) continue;
4157
+ if (!isObject(rule)) continue;
4148
4158
  if (visited.has(rule.id)) continue;
4149
4159
  const conclusionFacts = extractConclusions(rule.conclusion);
4150
4160
  const field = formatField(expression.check.field);
@@ -4253,16 +4263,16 @@ var LogicalReasoner = class {
4253
4263
  * @remarks
4254
4264
  * Bindings seed from `definition.variables`, then numeric subject fields (a
4255
4265
  * finite number or a numeric string, coerced through the contracts
4256
- * `parseNumber`) OVERRIDE same-named variables — the `id` field is skipped.
4266
+ * `parseNumber`) override same-named variables — the `id` field is skipped.
4257
4267
  * Equations solve strictly in order: when the `target` is unbound and appears
4258
- * on exactly ONE side, it is isolated by peeling invertible operations (`add` /
4268
+ * on exactly one side, it is isolated by peeling invertible operations (`add` /
4259
4269
  * `subtract` / `multiply` / `divide`); a non-invertible operation, a target on
4260
4270
  * both sides of an operation, or an unbound variable throws internally — the
4261
- * throw is caught PER EQUATION and surfaced as a result error (`Equation
4271
+ * throw is caught per equation and surfaced as a result error (`Equation
4262
4272
  * "<id>": <message>` plus a `FAILED` trace) while later equations still run.
4263
4273
  * Inversion or division by zero yields `NaN`, caught by the non-finite check.
4264
- * A solved value is rounded to `precision` BEFORE binding, so later equations
4265
- * see the rounded value; `solutions` reads FINAL bindings keyed by each
4274
+ * A solved value is rounded to `precision` before binding, so later equations
4275
+ * see the rounded value; `solutions` reads the settled bindings keyed by each
4266
4276
  * equation's target (a failed equation's target still appears when bound
4267
4277
  * elsewhere). Nothing mutates its inputs; fully deterministic.
4268
4278
  *
@@ -4330,7 +4340,7 @@ var SymbolicReasoner = class {
4330
4340
  definition: definition.id,
4331
4341
  reasoning: this.reasoning
4332
4342
  });
4333
- if (!definition.equations || !Array.isArray(definition.equations)) return {
4343
+ if (!isArray(definition.equations)) return {
4334
4344
  reasoning: "symbolic",
4335
4345
  solutions: {},
4336
4346
  success: false,
@@ -4363,12 +4373,12 @@ var SymbolicReasoner = class {
4363
4373
  };
4364
4374
  }
4365
4375
  for (const equation of definition.equations) {
4366
- if (typeof equation !== "object" || equation === null) continue;
4376
+ if (!isObject(equation)) continue;
4367
4377
  try {
4368
4378
  const value = this.#solve(equation, bindings);
4369
4379
  const rounded = roundTo(value, precision);
4370
- if (!Number.isFinite(value) || !Number.isFinite(rounded)) {
4371
- const offender = Number.isFinite(value) ? rounded : value;
4380
+ if (!isFiniteNumber(value) || !isFiniteNumber(rounded)) {
4381
+ const offender = isFiniteNumber(value) ? rounded : value;
4372
4382
  const description = Number.isNaN(offender) ? "NaN" : `${offender}`;
4373
4383
  errors.push(`Equation "${equation.id}": produced non-finite value (${description})`);
4374
4384
  trace.push(`Equation "${equation.id}": FAILED — produced non-finite value (${description})`);
@@ -4377,14 +4387,14 @@ var SymbolicReasoner = class {
4377
4387
  bindings[equation.target] = rounded;
4378
4388
  trace.push(`Equation "${equation.id}": ${equation.target} = ${rounded}`);
4379
4389
  } catch (error) {
4380
- const message = error instanceof Error ? error.message : String(error);
4390
+ const message = isError(error) ? error.message : String(error);
4381
4391
  errors.push(`Equation "${equation.id}": ${message}`);
4382
4392
  trace.push(`Equation "${equation.id}": FAILED — ${message}`);
4383
4393
  }
4384
4394
  }
4385
4395
  const solutions = {};
4386
4396
  for (const equation of definition.equations) {
4387
- if (typeof equation !== "object" || equation === null) continue;
4397
+ if (!isObject(equation)) continue;
4388
4398
  const value = bindings[equation.target];
4389
4399
  if (value !== void 0) solutions[equation.target] = value;
4390
4400
  }
@@ -4455,7 +4465,7 @@ var SymbolicReasoner = class {
4455
4465
  *
4456
4466
  * @remarks
4457
4467
  * A string term starting with `?` is a unification variable — matching is
4458
- * positional and BIDIRECTIONAL (variables may sit in facts as well as
4468
+ * positional and bidirectional (variables may sit in facts as well as
4459
4469
  * patterns), with binding consistency enforced within one match and across
4460
4470
  * premises through pre-instantiation. Scalar subject fields (except `id`;
4461
4471
  * `null` / `undefined` / objects / arrays skipped) inject as
@@ -4466,8 +4476,8 @@ var SymbolicReasoner = class {
4466
4476
  * duplicate facts (same predicate, arity, and SameValueZero-equal terms — a
4467
4477
  * NaN term derives once and converges) are never re-derived. Backward chaining
4468
4478
  * proves each inference's conclusion in
4469
- * declaration order and RETURNS ON THE FIRST SUCCESS with one derived fact
4470
- * (confidence = the inference's own — premise confidences are NOT propagated)
4479
+ * declaration order and returns on the first success with one derived fact
4480
+ * (confidence = the inference's own — premise confidences are not propagated)
4471
4481
  * plus its {@link ProofNode} tree; recursion is guarded only by the depth cap.
4472
4482
  * Deriving nothing is still success. Nothing mutates its inputs; fully
4473
4483
  * deterministic.
@@ -4530,11 +4540,11 @@ var InferentialReasoner = class {
4530
4540
  if (!definition.inferences || definition.inferences.length === 0) warnings.push("Definition has no inference rules");
4531
4541
  for (const id of findDuplicates(definition.inferences ?? [])) warnings.push(`Duplicate inference id "${id}"`);
4532
4542
  for (const fact of definition.facts ?? []) {
4533
- if (typeof fact !== "object" || fact === null) continue;
4543
+ if (!isObject(fact)) continue;
4534
4544
  if (fact.confidence !== void 0 && !(fact.confidence >= 0 && fact.confidence <= 1)) warnings.push(`Fact "${fact.id}" confidence outside [0, 1]`);
4535
4545
  }
4536
4546
  for (const inference of definition.inferences ?? []) {
4537
- if (typeof inference !== "object" || inference === null) continue;
4547
+ if (!isObject(inference)) continue;
4538
4548
  if (!inference.id) errors.push("Inference must have an id");
4539
4549
  if (!inference.premises || inference.premises.length === 0) warnings.push(`Inference "${inference.id}" has no premises`);
4540
4550
  if (!inference.conclusion) errors.push(`Inference "${inference.id}" must have a conclusion`);
@@ -4552,7 +4562,7 @@ var InferentialReasoner = class {
4552
4562
  definition: definition.id,
4553
4563
  reasoning: this.reasoning
4554
4564
  });
4555
- if (!definition.facts || !Array.isArray(definition.facts) || !definition.inferences || !Array.isArray(definition.inferences)) return {
4565
+ if (!definition.facts || !isArray(definition.facts) || !definition.inferences || !isArray(definition.inferences)) return {
4556
4566
  reasoning: "inferential",
4557
4567
  derived: [],
4558
4568
  success: false,
@@ -4577,7 +4587,7 @@ var InferentialReasoner = class {
4577
4587
  const maxDepth = definition.depth ?? 10;
4578
4588
  const knownFacts = [];
4579
4589
  for (const known of [...definition.facts, ...subjectFacts]) {
4580
- if (typeof known !== "object" || known === null) continue;
4590
+ if (!isObject(known)) continue;
4581
4591
  knownFacts.push(known);
4582
4592
  }
4583
4593
  const derived = [];
@@ -4592,7 +4602,7 @@ var InferentialReasoner = class {
4592
4602
  }
4593
4603
  const validInferences = [];
4594
4604
  for (const inference of definition.inferences) {
4595
- if (typeof inference !== "object" || inference === null) continue;
4605
+ if (!isObject(inference)) continue;
4596
4606
  if (inference.enabled === false) continue;
4597
4607
  if (!inference.premises || inference.premises.length === 0) {
4598
4608
  errors.push(`Inference "${inference.id}" has no premises — skipped`);
@@ -4641,7 +4651,7 @@ var InferentialReasoner = class {
4641
4651
  const derived = [];
4642
4652
  const allBaseFacts = [];
4643
4653
  for (const known of [...definition.facts, ...subjectFacts]) {
4644
- if (typeof known !== "object" || known === null) continue;
4654
+ if (!isObject(known)) continue;
4645
4655
  allBaseFacts.push(known);
4646
4656
  }
4647
4657
  if (definition.inferences.length === 0) {
@@ -4649,7 +4659,7 @@ var InferentialReasoner = class {
4649
4659
  return { derived };
4650
4660
  }
4651
4661
  for (const inference of definition.inferences) {
4652
- if (typeof inference !== "object" || inference === null) continue;
4662
+ if (!isObject(inference)) continue;
4653
4663
  if (inference.enabled === false) continue;
4654
4664
  if (!inference.conclusion) {
4655
4665
  errors.push(`Inference "${inference.id}" has no conclusion — skipped`);
@@ -4678,9 +4688,9 @@ var InferentialReasoner = class {
4678
4688
  depth
4679
4689
  };
4680
4690
  for (const inference of inferences) {
4681
- if (typeof inference !== "object" || inference === null) continue;
4691
+ if (!isObject(inference)) continue;
4682
4692
  if (inference.enabled === false) continue;
4683
- if (!inference.premises || !Array.isArray(inference.premises)) continue;
4693
+ if (!isArray(inference.premises)) continue;
4684
4694
  if (!inference.conclusion) continue;
4685
4695
  const conclusionBindings = matchFacts(goal, inference.conclusion);
4686
4696
  if (!conclusionBindings) continue;
@@ -4799,17 +4809,17 @@ var Collection = class {
4799
4809
  * kind-free manager over a symbolic definition's `equations`.
4800
4810
  *
4801
4811
  * @remarks
4802
- * OWNS its `equations` as a private {@link Collection} — copy-on-write state
4812
+ * Owns its `equations` as a private {@link Collection} — copy-on-write state
4803
4813
  * shared by composition with the other list managers — plus its own
4804
4814
  * {@link Emitter} over {@link EquationManagerEventMap}. Equation order is
4805
- * STRONGLY load-bearing — equations solve strictly in order and each rounded
4806
- * solution feeds forward. `remove` is the batch family (array form declared
4807
- * FIRST): no argument removes every equation, one id removes that equation, an
4815
+ * load-bearing — equations solve strictly in order and each rounded solution
4816
+ * feeds forward. `remove` is the batch family (array form declared
4817
+ * first): no argument removes every equation, one id removes that equation, an
4808
4818
  * id list removes those equations and returns true only when every named id
4809
4819
  * existed. It emits one `remove` per equation actually removed.
4810
4820
  * `seat` is the owning builder's silent bulk re-seat
4811
4821
  * channel (used by `merge`). `destroy()` is idempotent and tears the emitter
4812
- * down LAST; any other call after it throws `ReasonError('DESTROYED', …)`.
4822
+ * down last; any other call after it throws `ReasonError('DESTROYED', …)`.
4813
4823
  *
4814
4824
  * @example
4815
4825
  * ```ts
@@ -4892,17 +4902,17 @@ var EquationManager = class {
4892
4902
  * manager over an inferential definition's `facts`.
4893
4903
  *
4894
4904
  * @remarks
4895
- * OWNS its `facts` as a private {@link Collection} — copy-on-write state shared
4905
+ * Owns its `facts` as a private {@link Collection} — copy-on-write state shared
4896
4906
  * by composition with the other list managers — plus its own {@link Emitter}
4897
- * over {@link FactManagerEventMap}. `Fact.id` is an AUTHORING label — the
4907
+ * over {@link FactManagerEventMap}. `Fact.id` is an authoring label — the
4898
4908
  * runtime content-dedups facts by predicate+arity+terms, independently of this
4899
4909
  * manager's id-keyed dedup. `remove` is the batch family (array form declared
4900
- * FIRST): no argument removes every fact, one id removes that fact, an id list
4910
+ * first): no argument removes every fact, one id removes that fact, an id list
4901
4911
  * removes those facts and returns true only when every named id existed. It
4902
4912
  * emits one `remove` per fact actually removed.
4903
4913
  * `seat` is the owning builder's silent bulk re-seat
4904
4914
  * channel (used by `merge`). `destroy()` is idempotent and tears the emitter
4905
- * down LAST; any other call after it throws `ReasonError('DESTROYED', …)`.
4915
+ * down last; any other call after it throws `ReasonError('DESTROYED', …)`.
4906
4916
  *
4907
4917
  * @example
4908
4918
  * ```ts
@@ -4976,24 +4986,25 @@ var FactManager = class {
4976
4986
  //#endregion
4977
4987
  //#region src/core/builders/managers/FactorManager.ts
4978
4988
  /**
4979
- * Implements the {@link FactorManagerInterface} — the sole DIVERGENT
4980
- * manager: factors nest inside groups, so it holds NO collection state of its
4989
+ * Implements the {@link FactorManagerInterface} — the one divergent
4990
+ * manager: factors nest inside groups, so it holds no collection state of its
4981
4991
  * own and threads a required `groupId` locator.
4982
4992
  *
4983
4993
  * @remarks
4984
4994
  * Constructor-injected with the sibling {@link GroupManagerInterface}: each
4985
4995
  * write verb reads the located group (`groups.group(groupId)`), applies the
4986
- * factor-level pure helper ({@link appendFactor} etc.), and writes the updated
4996
+ * factor-level pure helper ({@link appendFactor}, {@link prependFactor},
4997
+ * {@link replaceFactor}, or {@link removeFactor}), and writes the updated
4987
4998
  * group back through `groups.replace(…)`. A `groupId` naming no existing group
4988
- * throws `ReasonError('TARGET', …, { groupId })`. It still owns its OWN
4999
+ * throws `ReasonError('TARGET', …, { groupId })`. It still owns its own
4989
5000
  * {@link Emitter} over {@link FactorManagerEventMap} (factor-id payloads).
4990
5001
  * `remove` is the batch family behind the leading `groupId` locator (array form
4991
- * declared FIRST): the locator alone removes every factor of that group, a
5002
+ * declared first): the locator alone removes every factor of that group, a
4992
5003
  * further id removes that factor, a further id list removes those factors and
4993
5004
  * returns true only when every named id existed. It emits one `remove` per
4994
5005
  * factor actually removed, each paired with a `replace` on the sibling
4995
5006
  * `GroupManagerInterface` for the containing group.
4996
- * `destroy()` is idempotent and tears the emitter down LAST; any other call
5007
+ * `destroy()` is idempotent and tears the emitter down last; any other call
4997
5008
  * after it throws `ReasonError('DESTROYED', …)`.
4998
5009
  *
4999
5010
  * @example
@@ -5092,16 +5103,16 @@ var FactorManager = class {
5092
5103
  * manager over a quantitative definition's `groups`.
5093
5104
  *
5094
5105
  * @remarks
5095
- * OWNS its `groups` as a private {@link Collection} — copy-on-write state
5106
+ * Owns its `groups` as a private {@link Collection} — copy-on-write state
5096
5107
  * shared by composition with the other list managers — plus its own
5097
5108
  * {@link Emitter} over {@link GroupManagerEventMap}. Every write verb delegates
5098
- * to the collection, then emits (the affected group id) AFTER the mutation.
5099
- * `remove` is the batch family (array form declared FIRST): no argument removes
5109
+ * to the collection, then emits (the affected group id) after the mutation.
5110
+ * `remove` is the batch family (array form declared first): no argument removes
5100
5111
  * every group, one id removes that group, an id list removes those groups and
5101
5112
  * returns true only when every named id existed. It emits one `remove` per
5102
5113
  * group actually removed.
5103
5114
  * `seat` is the owning builder's silent bulk re-seat channel (used by `merge`).
5104
- * `destroy()` is idempotent and tears the emitter down LAST; any other call
5115
+ * `destroy()` is idempotent and tears the emitter down last; any other call
5105
5116
  * after it throws `ReasonError('DESTROYED', …)`.
5106
5117
  *
5107
5118
  * @example
@@ -5180,16 +5191,16 @@ var GroupManager = class {
5180
5191
  * kind-free manager over an inferential definition's `inferences`.
5181
5192
  *
5182
5193
  * @remarks
5183
- * OWNS its `inferences` as a private {@link Collection} — copy-on-write state
5194
+ * Owns its `inferences` as a private {@link Collection} — copy-on-write state
5184
5195
  * shared by composition with the other list managers — plus its own
5185
5196
  * {@link Emitter} over {@link InferenceManagerEventMap}. Inference order is
5186
- * LOAD-BEARING — backward proving iterates in declaration order and returns on
5187
- * first success. `remove` is the batch family (array form declared FIRST): no
5197
+ * load-bearing — backward proving iterates in declaration order and returns on
5198
+ * first success. `remove` is the batch family (array form declared first): no
5188
5199
  * argument removes every inference, one id removes that inference, an id list
5189
5200
  * removes those inferences and returns true only when every named id existed.
5190
5201
  * It emits one `remove` per inference actually removed.
5191
5202
  * `seat` is the owning builder's silent bulk re-seat channel
5192
- * (used by `merge`). `destroy()` is idempotent and tears the emitter down LAST;
5203
+ * (used by `merge`). `destroy()` is idempotent and tears the emitter down last;
5193
5204
  * any other call after it throws `ReasonError('DESTROYED', …)`.
5194
5205
  *
5195
5206
  * @example
@@ -5278,17 +5289,17 @@ var InferenceManager = class {
5278
5289
  * manager over a logical definition's `rules`.
5279
5290
  *
5280
5291
  * @remarks
5281
- * OWNS its `rules` as a private {@link Collection} — copy-on-write state shared
5292
+ * Owns its `rules` as a private {@link Collection} — copy-on-write state shared
5282
5293
  * by composition with the other list managers — plus its own {@link Emitter}
5283
- * over {@link RuleManagerEventMap}. Rule order is LOAD-BEARING — the forward
5284
- * conclusion is the LAST declared non-disabled rule, so `append` without a
5294
+ * over {@link RuleManagerEventMap}. Rule order is load-bearing — the forward
5295
+ * conclusion is the last declared non-disabled rule, so `append` without a
5285
5296
  * `target` makes the new rule the conclusion. `remove` is the batch family
5286
- * (array form declared FIRST): no argument removes every rule, one id removes
5297
+ * (array form declared first): no argument removes every rule, one id removes
5287
5298
  * that rule, an id list removes those rules and returns true only when every
5288
5299
  * named id existed. It emits one `remove` per rule actually removed. `seat` is
5289
5300
  * the owning builder's
5290
5301
  * silent bulk re-seat channel (used by `merge`). `destroy()` is idempotent and
5291
- * tears the emitter down LAST; any other call after it throws
5302
+ * tears the emitter down last; any other call after it throws
5292
5303
  * `ReasonError('DESTROYED', …)`.
5293
5304
  *
5294
5305
  * @example
@@ -5370,17 +5381,17 @@ var RuleManager = class {
5370
5381
  * unordered record.
5371
5382
  *
5372
5383
  * @remarks
5373
- * OWNS its `#variables` record as private copy-on-write state and its own
5384
+ * Owns its `#variables` record as private copy-on-write state and its own
5374
5385
  * {@link Emitter} over {@link VariableManagerEventMap}. The record has no
5375
5386
  * placement, so only `add` / `remove` exist (no `append` / `prepend`): `add`
5376
5387
  * upserts and emits `add(name)`, `remove` omits the key entirely (never sets
5377
5388
  * `undefined`) and emits `remove(name)`. `remove` is the batch family over
5378
- * NAMES (array form declared FIRST): no argument removes every variable, one
5389
+ * names (array form declared first): no argument removes every variable, one
5379
5390
  * name removes that variable, a name list removes those variables and returns
5380
5391
  * true only when every named variable existed. It emits one `remove` per
5381
5392
  * variable actually removed. `seat` is the owning builder's silent
5382
5393
  * bulk re-seat channel (used by `merge`). `destroy()` is idempotent and tears
5383
- * the emitter down LAST; any other call after it throws
5394
+ * the emitter down last; any other call after it throws
5384
5395
  * `ReasonError('DESTROYED', …)`.
5385
5396
  *
5386
5397
  * @example
@@ -5461,22 +5472,22 @@ var VariableManager = class {
5461
5472
  //#region src/core/builders/DefinitionBuilder.ts
5462
5473
  /**
5463
5474
  * Implements a stateful workspace builder accumulating a {@link Definition}
5464
- * through always-present self-owning manager properties: a private SCALAR
5465
- * ENVELOPE (reasoning / id / name plus the kind's scalars) composed with each
5475
+ * through always-present self-owning manager properties: a private scalar
5476
+ * envelope (reasoning / id / name plus the kind's scalars) composed with each
5466
5477
  * collection read from its manager.
5467
5478
  *
5468
5479
  * @remarks
5469
- * Each manager is BRING-YOUR-OWN (a supplied one is reused) or a fresh one
5470
- * seeded from the seed's matching collection (empty for off-kind collections).
5471
- * Managers are KIND-FREE — an off-kind manager is ignored by `build()`,
5472
- * never a `MISMATCH`. `build()` is TOTAL, deterministic, and returns a FRESH
5473
- * plain {@link Definition} each call. `merge(incoming)` requires the SAME
5480
+ * Each manager is the one the caller supplies, or a fresh one seeded from the
5481
+ * seed's matching collection (empty for off-kind collections). Managers are
5482
+ * kind-free — `build()` ignores an off-kind manager, never a `MISMATCH`.
5483
+ * `build()` is total, deterministic, and returns a fresh plain
5484
+ * {@link Definition} each call. `merge(incoming)` requires the same
5474
5485
  * `reasoning` (else `MISMATCH`) and distributes incoming scalars into the
5475
5486
  * envelope and collections into the managers through the matching `merge*`
5476
5487
  * helper. `clear(key)` deletes one optional field of the envelope for the
5477
5488
  * instance's `reasoning` (a non-clearable key throws `MISMATCH`). `destroy()`
5478
5489
  * cascades to every manager, emits `destroy`, then tears the builder emitter
5479
- * down LAST; it is idempotent, and post-destroy mutation / build throws
5490
+ * down last; it is idempotent, and post-destroy mutation / build throws
5480
5491
  * `ReasonError('DESTROYED', …)`.
5481
5492
  *
5482
5493
  * @example
@@ -5658,27 +5669,26 @@ var DefinitionBuilder = class {
5658
5669
  * flat key-value collection, no managers.
5659
5670
  *
5660
5671
  * @remarks
5661
- * `id` is OPTIONAL (`options?.id ?? seed.id`). When present, the builder is
5672
+ * `id` is optional (`options?.id ?? seed.id`). When present, the builder is
5662
5673
  * id-ful: `build()` carries that `id` and `clear()` restores it. When absent,
5663
- * the builder is ANONYMOUS —
5664
- * `.id` is `undefined` and the accumulated subject carries no `id` key.
5665
- * `field(key)` / `fields()` are the singular / plural accessor pair over
5666
- * TOP-LEVEL keys only. `set(key, value)` delegates to `assignField`;
5667
- * `set('id', …)` throws `ReasonError('MISMATCH', …)` — id is immutable through the entity,
5668
- * id-ful or anonymous alike. `remove` is the batch family (array form
5669
- * declared first): no argument removes every non-id field and emits one
5670
- * `remove` per key in key order, one key removes that field, and a key list
5671
- * removes those fields. Removing `'id'` through a keyed form throws the same
5672
- * `MISMATCH` for the same reason. `merge(incoming)` delegates to `mergeSubjects`
5673
- * (incoming-wins, base `id` preserved — plain {@link Subject} data only).
5674
- * `clear()` removes every non-id field, restoring `{ id }` when id-ful or
5675
- * an empty record when anonymous. `repeat(count)` returns `count`
5676
- * deterministic minted-id clones as PLAIN payloads — a pure read that does
5677
- * NOT emit. `build(): Subject` is total, deterministic, and returns a fresh
5678
- * durable payload each call. Post-destroy mutation throws
5674
+ * the builder is anonymous — `.id` is `undefined` and the accumulated subject
5675
+ * carries no `id` key. `field(key)` / `fields()` are the singular / plural
5676
+ * accessor pair over top-level keys only. `set(key, value)` delegates to
5677
+ * `assignField`; setting `id` throws `ReasonError('MISMATCH', …)`, because the
5678
+ * id is immutable through the entity, id-ful or anonymous alike. `remove` is
5679
+ * the batch family (array form declared first): no argument removes every
5680
+ * non-id field and emits one `remove` per key in key order, one key removes
5681
+ * that field, and a key list removes those fields. Removing `'id'` through a
5682
+ * keyed form throws the same `MISMATCH` for the same reason. `merge(incoming)`
5683
+ * delegates to `mergeSubjects` (incoming-wins, base `id` preserved — plain
5684
+ * {@link Subject} data only). `clear()` removes every non-id field, restoring
5685
+ * `{ id }` when id-ful or an empty record when anonymous. `repeat(count)`
5686
+ * returns `count` deterministic minted-id clones as plain payloads — a pure
5687
+ * read that emits nothing. `build(): Subject` is total, deterministic, and
5688
+ * returns a fresh durable payload each call. Post-destroy mutation throws
5679
5689
  * `ReasonError('DESTROYED', …)` — only the `emitter` getter and `destroy`
5680
5690
  * itself keep working, mirroring `Reason`. `destroy()` is idempotent and
5681
- * tears the emitter down LAST.
5691
+ * tears the emitter down last.
5682
5692
  *
5683
5693
  * @example
5684
5694
  * ```ts
@@ -5687,7 +5697,7 @@ var DefinitionBuilder = class {
5687
5697
  * const applicant = createSubjectBuilder({ id: 'alice', age: 25 })
5688
5698
  * applicant.set('region', 'CA')
5689
5699
  * applicant.merge({ licensed: true, accidents: 0 }) // incoming wins, id kept
5690
- * applicant.remove(['accidents']) // batch form first — true when ALL existed
5700
+ * applicant.remove(['accidents']) // batch form first — true when every key existed
5691
5701
  * applicant.field('region') // 'CA'
5692
5702
  * applicant.fields() // the live record
5693
5703
  * applicant.repeat(2) // ids 'alice-0', 'alice-1'
@@ -5704,8 +5714,8 @@ var SubjectBuilder = class {
5704
5714
  #destroyed = false;
5705
5715
  constructor(seed, options) {
5706
5716
  const id = options?.id ?? seed.id;
5707
- this.#id = typeof id === "string" ? id : void 0;
5708
- if (typeof this.#id === "string") this.#subject = {
5717
+ this.#id = isString(id) ? id : void 0;
5718
+ if (isString(this.#id)) this.#subject = {
5709
5719
  ...seed,
5710
5720
  id: this.#id
5711
5721
  };
@@ -5751,7 +5761,7 @@ var SubjectBuilder = class {
5751
5761
  merge(incoming) {
5752
5762
  this.#ensureAlive();
5753
5763
  const merged = mergeSubjects(this.#subject, incoming);
5754
- if (typeof this.#id !== "string" && Object.hasOwn(merged, "id")) {
5764
+ if (!isString(this.#id) && Object.hasOwn(merged, "id")) {
5755
5765
  const { id: _id, ...rest } = merged;
5756
5766
  this.#subject = rest;
5757
5767
  } else this.#subject = merged;
@@ -5759,7 +5769,7 @@ var SubjectBuilder = class {
5759
5769
  }
5760
5770
  clear() {
5761
5771
  this.#ensureAlive();
5762
- this.#subject = typeof this.#id === "string" ? { id: this.#id } : {};
5772
+ this.#subject = isString(this.#id) ? { id: this.#id } : {};
5763
5773
  this.#emitter.emit("clear");
5764
5774
  }
5765
5775
  repeat(count) {
@@ -5956,7 +5966,7 @@ function createInferentialReasoner(options) {
5956
5966
  *
5957
5967
  * @remarks
5958
5968
  * `reasoners` — the initial registry (a later entry of the same reasoning
5959
- * replaces an earlier one; the orchestrator ships with NO defaults). `bail` —
5969
+ * replaces an earlier one; the orchestrator ships with no defaults). `bail` —
5960
5970
  * `true` (the default) rethrows a reasoner throw after the `error` emit;
5961
5971
  * `false` converts it to a failure result. `validate` — validate every
5962
5972
  * definition before running it, throwing `INVALID` on failure (default
@@ -5966,16 +5976,32 @@ function createInferentialReasoner(options) {
5966
5976
  * @param options - Optional registry, policies, and emitter hooks
5967
5977
  * @returns A {@link ReasonInterface}
5968
5978
  *
5969
- * @example
5979
+ * @example Create an orchestrator and score a subject
5970
5980
  * ```ts
5971
- * import { createLogicalReasoner, createQuantitativeReasoner, createReason } from '@src/core'
5981
+ * import {
5982
+ * createFactorGroup,
5983
+ * createFieldFactor,
5984
+ * createQuantitativeDefinition,
5985
+ * createQuantitativeReasoner,
5986
+ * createReason,
5987
+ * createStaticFactor,
5988
+ * } from '@orkestrel/reason'
5972
5989
  *
5973
- * const reason = createReason({
5974
- * reasoners: [createQuantitativeReasoner(), createLogicalReasoner()],
5975
- * on: { reason: (result) => console.log(result.success) },
5976
- * })
5977
- * const result = reason.reason({ age: 25 }, definition)
5978
- * reason.destroy()
5990
+ * const reason = createReason({ reasoners: [createQuantitativeReasoner()] })
5991
+ *
5992
+ * const definition = createQuantitativeDefinition('risk', 'Risk score', [
5993
+ * createFactorGroup('drivers', 'sum', [
5994
+ * createFieldFactor('age', 'age'), // reads subject.age, parseNumber-coerced
5995
+ * createStaticFactor('floor', 10), // a fixed contribution
5996
+ * ]),
5997
+ * ])
5998
+ *
5999
+ * const result = reason.reason({ age: 25 }, definition) // one subject → one result
6000
+ * if (result.reasoning === 'quantitative') result.value // 35 — narrow by the discriminant
6001
+ * result.trace // the step-by-step account of how the value came to be
6002
+ *
6003
+ * reason.supports('quantitative') // true — a reasoner is registered for this reasoning
6004
+ * reason.reasoner('quantitative')?.supports(definition) // the reasoner's own guard, same check
5979
6005
  * ```
5980
6006
  */
5981
6007
  function createReason(options) {
@@ -5987,15 +6013,14 @@ function createReason(options) {
5987
6013
  * `variables`, `facts`, and `inferences` self-owning manager properties.
5988
6014
  *
5989
6015
  * @remarks
5990
- * `id`. Default: `seed.id`. Each manager slot is BRING-YOUR-OWN (a supplied
5991
- * one is reused, else a fresh one is seeded from the seed's matching
5992
- * collection). `on` — initial event listeners. `error` — the emitter's
5993
- * listener-error handler. Mutate through the manager
5994
- * properties (`groups` / `factors` / `rules` / `equations` / `variables` /
5995
- * `facts` / `inferences`) and `merge` / `clear`, then call `build()` to produce
5996
- * a fresh, plain {@link Definition} snapshot.
5997
- *
5998
- * @param seed - The starting definition (any of the four reasoning kinds)
6016
+ * `id`. Default: `seed.id`. Each manager slot takes the manager the caller
6017
+ * supplies, or a fresh one seeded from the seed's matching collection. `on` —
6018
+ * initial event listeners. `error` — the emitter's listener-error handler.
6019
+ * Mutate through the manager properties (`groups` / `factors` / `rules` /
6020
+ * `equations` / `variables` / `facts` / `inferences`) and `merge` / `clear`,
6021
+ * then call `build()` to produce a fresh, plain {@link Definition} snapshot.
6022
+ *
6023
+ * @param seed - The starting definition, of any reasoning
5999
6024
  * @param options - Optional `id` override, manager injections, and emitter hooks
6000
6025
  * @returns A {@link DefinitionBuilderInterface}
6001
6026
  *
@@ -6017,8 +6042,8 @@ function createDefinitionBuilder(seed, options) {
6017
6042
  * {@link Subject}.
6018
6043
  *
6019
6044
  * @remarks
6020
- * `id`. Default: `seed.id`; OPTIONAL — when neither `options.id` nor
6021
- * a string `seed.id` is present the builder is ANONYMOUS (`.id` is
6045
+ * `id`. Default: `seed.id`; it is optional — when neither `options.id` nor
6046
+ * a string `seed.id` is present the builder is anonymous (`.id` is
6022
6047
  * `undefined`, `build()` emits no `id` key). `on` — initial event listeners.
6023
6048
  * `error` — the emitter's listener-error handler.
6024
6049
  * Mutate through `set` / `remove` / `merge` / `clear`, then call `build()` to
@@ -6202,7 +6227,7 @@ function createInferenceManager(options) {
6202
6227
  /**
6203
6228
  * Creates a {@link Check} — one field predicate.
6204
6229
  *
6205
- * @param field - The subject field to resolve (a string is ONE key; an array descends)
6230
+ * @param field - The subject field to resolve (a string is a single key; an array descends)
6206
6231
  * @param operator - The comparison to apply
6207
6232
  * @param value - The expected value (any type — the operator decides what is meaningful)
6208
6233
  * @returns A fresh check
@@ -6272,7 +6297,7 @@ function createCompound(operator, operands) {
6272
6297
  * `enabled` through `overrides`.
6273
6298
  *
6274
6299
  * @param id - The rule id
6275
- * @param premises - The expressions that must ALL hold
6300
+ * @param premises - The expressions that must all hold
6276
6301
  * @param conclusion - The expression whose atoms are asserted when they do
6277
6302
  * @param overrides - Optional {@link Rule} fields merged over the defaults
6278
6303
  * @returns A fresh rule
@@ -6297,7 +6322,7 @@ function createRule(id, premises, conclusion, overrides) {
6297
6322
  * Creates a {@link Transform} — one math step.
6298
6323
  *
6299
6324
  * @remarks
6300
- * The `operand` key is OMITTED when absent (never set to `undefined`), so the
6325
+ * The `operand` key is omitted when absent (never set to `undefined`), so the
6301
6326
  * transform stays exact-record valid; the transformer then applies its
6302
6327
  * per-operation default (`1` for `multiply` / `divide` / `power`, `0` otherwise).
6303
6328
  *
@@ -6323,7 +6348,7 @@ function createTransform(operator, operand) {
6323
6348
  * Creates a {@link Bounds} — an inclusive numeric clamp.
6324
6349
  *
6325
6350
  * @remarks
6326
- * Absent sides are OMITTED (never set to `undefined`) — an absent bound is
6351
+ * Absent sides are omitted (never set to `undefined`) — an absent bound is
6327
6352
  * unbounded on that side.
6328
6353
  *
6329
6354
  * @param minimum - The inclusive lower bound
@@ -6386,7 +6411,7 @@ function createConstant(value) {
6386
6411
  * Creates an operation {@link SymbolicExpression} node.
6387
6412
  *
6388
6413
  * @remarks
6389
- * The `right` key is OMITTED when absent — correct for the unary operations
6414
+ * The `right` key is omitted when absent — correct for the unary operations
6390
6415
  * (`round` / `ceil` / `floor` / `abs`); a binary operation with no `right`
6391
6416
  * treats it as the constant `0`.
6392
6417
  *
@@ -6483,7 +6508,7 @@ function createFact(id, predicate, terms, confidence) {
6483
6508
  * `enabled` through `overrides`.
6484
6509
  *
6485
6510
  * @param id - The inference id
6486
- * @param premises - The fact patterns that must ALL unify
6511
+ * @param premises - The fact patterns that must all unify
6487
6512
  * @param conclusion - The fact pattern derived when they do
6488
6513
  * @param overrides - Optional {@link Inference} fields merged over the defaults
6489
6514
  * @returns A fresh inference